第一个 NFT
本教程通过以下的分步步骤描述如何在 Aptos 区块链上创建和转移 NFT。核心 NFT 或代币的 Aptos 实例可以在 token.move 中找到。
第 1 步:选择一个 SDK
Aptos 官方 Rust SDK —— 待发布
第 2 步:运行示例
每个 SDK 都提供一个示例目录。本教程涵盖了该 simple-nft
示例。
克隆 aptos-core
:
git clone git@github.com:aptos-labs/aptos-core.git ~/aptos-core
Typescript
导航至 Typescript SDK 示例目录:
cd ~/aptos-core/ecosystem/typescript/sdk/examples/typescript
安装必要的依赖项:
yarn install
运行
simple-nft
示例:yarn run simple_nft
Python
导航至 Python SDK 示例目录:
cd ~/aptos-core/ecosystem/python/sdk
安装必要的依赖项:
curl -sSL https://install.python-poetry.org | python3 poetry update
运行
simple-nft
示例:poetry run python -m examples.simple-nft
Rust
待发布
第 3 步:理解输出
执行 simple-nft 示例后,应出现以下输出,但某些值会有所不同:
=== Addresses ===
Alice: 0x9df0f527f3a0b445e4d5c320cfa269cdefafc7cd1ed17ffce4b3fd485b17aafb
Bob: 0xfcc74af84dde26b0050dce35d6b3d11c60f5c8c58728ca3a0b11035942a0b1de
=== Initial Coin Balances ===
Alice: 20000
Bob: 20000
=== Creating Collection and Token ===
Alice's collection: {
"description": "Alice's simple collection",
"maximum": "18446744073709551615",
"mutability_config": {
"description": false,
"maximum": false,
"uri": false
},
"name": "Alice's",
"supply": "1",
"uri": "https://aptos.dev"
}
Alice's token balance: 1
Alice's token data: {
"default_properties": {
"map": {
"data": []
}
},
"description": "Alice's simple token",
"largest_property_version": "0",
"maximum": "1",
"mutability_config": {
"description": false,
"maximum": false,
"properties": false,
"royalty": false,
"uri": false
},
"name": "Alice's first token",
"royalty": {
"payee_address": "0x9df0f527f3a0b445e4d5c320cfa269cdefafc7cd1ed17ffce4b3fd485b17aafb",
"royalty_points_denominator": "1000000",
"royalty_points_numerator": "0"
},
"supply": "1",
"uri": "https://aptos.dev/img/nyan.jpeg"
}
=== Transferring the token to Bob ===
Alice's token balance: 0
Bob's token balance: 1
=== Transferring the token back to Alice using MultiAgent ===
Alice's token balance: 1
Bob's token balance: 0
该示例演示了:
初始化 REST 和 Faucet 客户端
创建 Alice 和 Bob 两个帐户
为 Alice 和 Bob 的帐户提供资金
使用 Alice 的帐户创建一个集合和一个代币
Alice 提供代币,Bob 领取代币
Bob 通过一个多代理交易单方面地将代币发送给 Alice
第 4 步:深入了解 SDK
Rust
待发布
第 4.1 步:初始化客户端
在第 1 步中,示例初始化 API 和 Faucet 客户端。
API 客户端与 REST API 交互,并且
Faucet 客户端与 devnet Faucet 服务交互以创建和注资帐户
Typescript
const client = new AptosClient(NODE_URL); const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL);
使用 API 客户端,我们可以创建一个 T
okenClient
,用于常见的代币操作,例如创建、转移和领取集合和代币等。const tokenClient = new TokenClient(client);
common.ts 将 URL 值初始化为这样:
export const NODE_URL = process.env.APTOS_NODE_URL || "https://fullnode.devnet.aptoslabs.com"; export const FAUCET_URL = process.env.APTOS_FAUCET_URL || "https://faucet.devnet.aptoslabs.com";
Python
rest_client = RestClient(NODE_URL) faucet_client = FaucetClient(FAUCET_URL, rest_client)
common.py
将这些值初始化为这样:NODE_URL = os.getenv("APTOS_NODE_URL", "https://fullnode.devnet.aptoslabs.com/v1") FAUCET_URL = os.getenv( "APTOS_FAUCET_URL", "https://faucet.devnet.aptoslabs.com" )
💡 提示
默认情况下,两个服务等 URL 都指向 Aptos devnet 服务。然而,它们可以通过以下环境变量进行配置。
Rust
待发布
第 4.2 步:创建本地账户
下一步是在本地创建两个帐户。帐户代表链上和链下的状态。链下状态包括一个地址和用户验证所有权的公钥、私钥对。这一步演示了如何生成链下状态。
Typescript
const alice = new AptosAccount(); const bob = new AptosAccount();
Python
alice = Account.generate() bob = Account.generate()
Rust
待发布
第 4.3 步:创建链上账户
在 Aptos 中,每个帐户都必须有一个链上表示,以支持接收代币和货币,以及在其他 dApp 中进行互动。一个帐户代表了一个存储资产的媒介,因此必须明确创建。这个示例利用 Faucet 来创建 Alice 和 Bob 的帐户。只有 Alice 帐户里有资金:
Typescript
await faucetClient.fundAccount(alice.address(), 20_000); await faucetClient.fundAccount(bob.address(), 20_000);
Python
faucet_client.fund_account(alice.address(), 20_000) faucet_client.fund_account(bob.address(), 20_000)
Rust
待发布
第 4.4 步:创建一个集合
现在开始创建代币的过程。首先,创建者必须创建一个集合来存储代币。一个集合可以包含零个、一个或多个不同的代币。集合只是一个容器,并不限制代币的属性:
Typescript
应用程序将调用
creatColletion
:const txnHash1 = await tokenClient.createCollection( alice, collectionName, "Alice's simple collection", "https://alice.com", );
createCollection
的函数签名。它返回一个交易哈希值:async createCollection( account: AptosAccount, name: string, description: string, uri: string, maxAmount: BCS.AnyNumber = MAX_U64_BIG_INT, ): Promise<string> {
Python
应用程序将调用
create_colletion
:txn_hash = rest_client.create_collection( alice, collection_name, "Alice's simple collection", "https://aptos.dev" )
create_colletion
的函数签名。它返回一个交易哈希值:def create_collection( self, account: Account, name: str, description: str, uri: str ) -> str:
Rust
待发布
第 4.5 步:创建一个代币
要创建一个代币,创建者必须指定一个相关的集合。一个代币必须与一个集合相关联,并且该集合必须有可以被铸造的剩余代币。有许多与代币相关的属性,但辅助 API 只公开了创建静态内容所需的最小数量。
Typescript
应用程序将调用
creatToken
:const txnHash2 = await tokenClient.createToken( alice, collectionName, tokenName, "Alice's simple token", 1, "https://aptos.dev/img/nyan.jpeg", );
createToken
的函数签名。它返回一个交易哈希值:async createToken( account: AptosAccount, collectionName: string, name: string, description: string, supply: number, uri: string, max: BCS.AnyNumber = MAX_U64_BIG_INT, royalty_payee_address: MaybeHexString = account.address(), royalty_points_denominator: number = 0, royalty_points_numerator: number = 0, property_keys: Array<string> = [], property_values: Array<string> = [], property_types: Array<string> = [], ): Promise<string> {
Python
应用程序将调用
create_token
:txn_hash = rest_client.create_token( alice, collection_name, token_name, "Alice's simple token", 1, "https://aptos.dev/img/nyan.jpeg", 0, )
create_token
的函数签名。它返回一个交易哈希值:def create_token( self, account: Account, collection_name: str, name: str, description: str, supply: int, uri: str, royalty_points_per_million: int, ) -> str:
Rust
待发布
第 4.6 步:读取代币和集合的元数据
集合和代币元数据都存储在创建者帐户的 Collection
表中。SDK 为查询这些特定的表格提供了便利的封装器:
Typescript
读取集合的元数据:
const collectionData = await tokenClient.getCollectionData(alice.address(), collectionName); console.log(`Alice's collection: ${JSON.stringify(collectionData, null, 4)}`);
读取代币的元数据:
const tokenData = await tokenClient.getTokenData(alice.address(), collectionName, tokenName); console.log(`Alice's token data: ${JSON.stringify(tokenData, null, 4)}`);
以下是
getTokenData
如何查询代币元数据的:async getTokenData( creator: MaybeHexString, collectionName: string, tokenName: string, ): Promise<TokenTypes.TokenData> { const creatorHex = creator instanceof HexString ? creator.hex() : creator; const collection: { type: Gen.MoveStructTag; data: any } = await this.aptosClient.getAccountResource( creatorHex, "0x3::token::Collections", ); const { handle } = collection.data.token_data; const tokenDataId = { creator: creatorHex, collection: collectionName, name: tokenName, }; const getTokenTableItemRequest: Gen.TableItemRequest = { key_type: "0x3::token::TokenDataId", value_type: "0x3::token::TokenData", key: tokenDataId, }; // We know the response will be a struct containing TokenData, hence the // implicit cast. return this.aptosClient.getTableItem(handle, getTokenTableItemRequest); }
Python
读取集合的元数据:
collection_data = rest_client.get_collection(alice.address(), collection_name) print( f"Alice's collection: {json.dumps(collection_data, indent=4, sort_keys=True)}" )
读取代币的元数据:
token_data = rest_client.get_token_data( alice.address(), collection_name, token_name, property_version ) print( f"Alice's token data: {json.dumps(token_data, indent=4, sort_keys=True)}" )
以下是
get_token_data
如何查询代币元数据的:def get_token_data( self, creator: AccountAddress, collection_name: str, token_name: str, property_version: int, ) -> Any: token_data_handle = self.account_resource(creator, "0x3::token::Collections")[ "data" ]["token_data"]["handle"] token_data_id = { "creator": creator.hex(), "collection": collection_name, "name": token_name, } return self.get_table_item( token_data_handle, "0x3::token::TokenDataId", "0x3::token::TokenData", token_data_id, )
Rust
待发布
第 4.7 步:读取代币余额
Aptos 内的每一个代币都是一个独立的资产,用户拥有的资产都存储在他们的 TokenStore
内。为读取余额:
Typescript
const aliceBalance1 = await tokenClient.getToken( alice.address(), collectionName, tokenName, `${tokenPropertyVersion}`, ); console.log(`Alice's token balance: ${aliceBalance1["amount"]}`);
Python
balance = rest_client.get_token_balance( alice.address(), alice.address(), collection_name, token_name, property_version ) print(f"Alice's token balance: {balance}")
Rust
待发布
第 4.8 步:提供和领取代币
许多用户因收到不需要的代币,而可能造成尴尬甚至是严重后果。Aptos 赋予每个帐户所有者权力,决定是否接受单边转账。默认情况下,是不支持单边转账的。所以 Aptos 提供了一个提供和领取代币的框架。
提供代币:
Typescript
const txnHash3 = await tokenClient.offerToken( alice, bob.address(), alice.address(), collectionName, tokenName, 1, tokenPropertyVersion, );
Python
txn_hash = rest_client.offer_token( alice, bob.address(), alice.address(), collection_name, token_name, property_version, 1, )
Rust
待发布
领取代币:
Typescript
const txnHash4 = await tokenClient.claimToken( bob, alice.address(), alice.address(), collectionName, tokenName, tokenPropertyVersion, );
Python
txn_hash = rest_client.claim_token( bob, alice.address(), alice.address(), collection_name, token_name, property_version, )
Rust
待发布
第 4.9 步:代币的安全单边转账
为支持代币安全单边转账,发送方可以首先要求接收方确认链下的待定转移。这里以多代理交易请求的形式出现。多代理交易包含多个签名,每个链上帐户都有一个签名。然后,Move 可以利用这一点给所有签名的人以签名者级别的权限。对于代币转移,这样确保了接收方确实希望收到这个代币,而不需要使用上述的代币转移框架。
Typescript
let txnHash5 = await tokenClient.directTransferToken( bob, alice, alice.address(), collectionName, tokenName, 1, tokenPropertyVersion, );
Python
txn_hash = rest_client.direct_transfer_token( bob, alice, alice.address(), collection_name, token_name, 0, 1 )
Rust
待发布
第 4.10 步:实现代币单边转账
即将上线!
Last updated