这篇文章上次修改于 855 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

NRC-721 是 Nervos 上创建和处理 NFT 的标准。

NFT 相关信息被记录在 factory cell 和 token cell 中。

  • factory cell 存储一些通用的数据,可避免因重复存储相同的数据造成的浪费。比如包含 base_uri 等信息。
  • token cell 包含从 factory 构建出的唯一 token 的一些信息,被 nft type script 管理。比如包含 token_id 等信息。

先部署 factory cell,然后创建 token cell。

本文通过参考 nrc-721-template 列出了所有细节,也可以通过 Javascript SDKmint factory cellmint token cell) 简化创建 token cell 的过程。

本文未涵盖如何编写 nft type script,即 nft 合约。

除了 NRC-721 协议,还可以选择 m-NFT 协议,类似 ERC-1155 协议,m-NFT 协议允许批量操作 NFT。

1 部署 lock script

  1. 编写 lock script,编译生成二进制 lock_bin
  2. 加载 lock_bin,其哈希值为 lock_bin_hash,deploy_cell(lock_bin),得到 lock_out_point,后续用来定位该 lock_cell。

    lock_out_point {
        tx_hash: "0x123...",  // 标识 transaction
        index: "0x0"          // 说明指向 transaction.outputs 中的下标为 0 的 cell
    }

    cell 被部署后,会生成 out_point {tx_hash, index},表示 tx_hash 指向的 transaction 中 outputs 中下标为 index 的 cell。

  3. 构造 lock_script

    lock_script {
        code_hash: ${lock_bin_hash}, 
        hash_type: "data", 
        args
    }

    lock_script 用来匹配 transaction.cell_deps 所指向的 cell。hash_type 为 "data" 表示应该取 code_hash 等于 transaction.cell_deps 中 blake2b_hash(cell.data) 的 cell。

  4. 构造 lock_script_dep

    lock_script_dep {
        out_point: ${lock_out_point},
        dep_type: "code"
    }

​ 后面会被放到 transaction.cell_deps 中。

2 构造 input

  1. 构造 previous_input_cell

    previous_input_cell {
        capacity: 500,
        lock: ${lock_script},
        type: null
    } 
  2. 部署 previous_ input_cell 得到 previous_input's_out_point

    previous_input's_out_point {
        tx_hash: "0x234..."
        index: "0x0"
    }
  3. 构造 lock_input

    lock_input {
        previous_output: ${previous_input's_out_point}
    }

3 部署 nft 合约

  1. 编写 nft 合约,编译生成二进制 nft_bin
  2. 加载 nft_bin,其哈希值为 nft_data_hash,deploy_cell(nft_bin),得到 nft_out_point

    nft_out_point {
        tx_hash: "0x345...",
        index: "0x0"
    }
  3. 构造 nft_type_script_dep

    nft_type_script_dep {
        out_point: ${nft_out_point},
        dep_type: "code"
    }

4 构造 output

4.1 部署 factory cell

factory cell 存储一些通用的数据,可避免因重复存储相同的数据造成的浪费。

  1. 构造 factory_type_script

    factory_type_script {
        code_hash: TYPE_ID or Custom Script
        hash_type: "type"
        args: TYPE_ID
    }
  2. 构造 factory_cell

    factory_cell {
        capacity: 2000,
        lock: ${lock_script},
        type: ${factory_type_script}
        data: {
            name: length<uint8> + text<utf-8> (max 255 char)
            symbol: length<uint8> + text<utf-8> (max 255 char)
            base_token_uri: length<uint8> + text<utf-8> (max 255 char)
        }
    }
  3. 部署 factory_cell 得到 factory_cell‘s_out_point

    factory_cell's_out_point {
        tx_hash: "0x456..."
        index: "0x0"
    }
  4. 构造 factory_cell_dep

    factory_cell_dep {
        out_point: ${factory_cell's_out_point},
        dep_type: "code"
    }

4.2 构造 token cell

token cell 包含从 factory 构建出的唯一 token 的一些信息,受 nft_script 的管理。

  1. 构造 nft_type_args

    ${factory_type_script.code_hash} + ${factory_type_script.hash_type} + ${factory_type_script.args} + TOKEN_ID

    本例中,TOKEN_ID = blake2b(${input})

  2. 构造 nft_type_script。通过 nft_out_point 得到 nft_bin,计算其 hash 得到 nft_bin_hash

    nft_type_script {
        code_hash: ${nft_bin_hash}
        hash_type: "data"
        args: ${nft_type_args}
    }
  3. 构造 token_cell

    token_cell {
        capacity: 500,
        lock: ${lock_script}
        type: ${nft_type_script}
    }
    
    token_data {
        "0x"
    }

5 组装成 transaction

transaction {
    inputs: [${lock_input}],
    outputs: [${token_cell}],
    outputs_data: [${token_data}],
    cell_deps: [${lock_script_dep}, ${factory_cell_dep}, ${nft_type_script_dep}],
    witnesses: signature
}

6 DAPP 中获取 NFT 信息

factory cell 和 token cell 被部署到链上后,可在 DAPP 中通过它们获取 NFT 相关信息。

1.png

示例见 example

参考

[RFC] NRC721: NFTs on Nervos Blockchain