Skip to main content

你的第一个 NFT

本教程介绍如何在 Aptos 区块链上创建和转移 NFT。 核心 NFT 的 Aptos 实现可以参见 token.move Move 合约。

第 1 步:选择一个 SDK

从以下列表中安装您喜欢的 SDK:


第 2 步:运行示例

每个 SDK 都提供一个 examples 目录。 本教程涵盖了simple-nft示例。

克隆 aptos-core 存储库:

git clone git@github.com:aptos-labs/aptos-core.git ~/aptos-core

切换到 Typescript SDK 示例目录:

cd ~/aptos-core/ecosystem/typescript/sdk/examples/typescript

安装必要的依赖项:

yarn install

运行脚本示例 simple_nft:

yarn run simple_nft

第 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 和水龙头客户端。
  • 创建两个帐户:Alice 和 Bob。
  • Alice 和 Bob 账户的资金和创建。
  • 使用 Alice 的账户创建一个集合和一个 Token。
  • 爱丽丝提供一个 Token,鲍勃认领它。
  • Bob 通过多代理交易单方面将 Token发送给 Alice。

第 4 步:深入 SDK

查看完整代码

请参阅 simple_nft 以获取完整代码,如下所示 脚步。


步骤 4.1:初始化客户端

在第一步中,示例初始化 API 和水龙头客户端。

  • API 客户端与 REST API 交互,以及
  • 水龙头客户端与 devnet Faucet 服务交互以创建和注资帐户。
const client = new AptosClient(NODE_URL);
const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL);

使用 API 客户端,我们可以创建一个 TokenClient,我们将其用于常见的 Token 操作,例如创建集合和 Token 、传输它们、声明它们等等。

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";
tip

默认情况下,这两个服务的 URL 都指向 Aptos devnet 服务。 但是,可以使用以下环境变量配置它们:

  • APTOS_NODE_URL
  • APTOS_FAUCET_URL

步骤 4.2:创建本地帐户

下一步是在本地创建两个帐户。 Accounts 代表链上和链下状态。 链下状态由地址和用于验证所有权的公钥、私钥对组成。 此步骤演示如何生成该链下状态。

const alice = new AptosAccount();
const bob = new AptosAccount();

步骤 4.3:创建区块链账户

在 Aptos 中,每个帐户都必须具有链上表示,以支持接收 Token和代币以及与其他 dApp 进行交互。 账户代表了存储资产的媒介,因此必须明确创建。 此示例利用水龙头创建 Alice 和 Bob 的帐户:

await faucetClient.fundAccount(alice.address(), 100_000_000);
await faucetClient.fundAccount(bob.address(), 100_000_000);

步骤 4.4:创建集合

现在开始创建 Token 的过程。 首先,创建者必须创建一个集合来存储 Token 。 一个集合中可以包含零个、一个或多个不同的 Token。 该集合不限制 Token 的属性,因为它只是一个容器。

您的应用程序将调用 createCollection:

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: AnyNumber = MAX_U64_BIG_INT,
extraArgs?: OptionalTransactionArgs,
): Promise<string> {


步骤 4.5:创建 Token

要创建 Token ,创建者必须指定关联的集合。 一个 Token必须与一个集合相关联,并且该集合必须有剩余的可以铸造的 Token。 有许多与 Token 相关联的属性,但帮助程序 API 仅公开创建静态内容所需的最少数量。

您的应用程序将调用 createToken:

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: 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> = [],
extraArgs?: OptionalTransactionArgs,
): Promise<string> {


步骤 4.6:读取 Token 和集合元数据

集合和 Token 元数据都存储在创建者帐户的集合中的表格中。 SDK 为查询这些特定表提供了便利的方法:

读取集合的元数据:

const collectionData = await tokenClient.getCollectionData(alice.address(), collectionName);
console.log(`Alice's collection: ${JSON.stringify(collectionData, null, 4)}`);

读取 Token的元数据:

const tokenData = await tokenClient.getTokenData(alice.address(), collectionName, tokenName);
console.log(`Alice's token data: ${JSON.stringify(tokenData, null, 4)}`);

以下是 getTokenData 查询 Token元数据的方式:

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);
}

步骤 4.7:读取 Token余额

Aptos 中的每个 Token都是不同的资产,用户拥有的资产存储在他们的TokenStore中。 要获得余额:

const aliceBalance1 = await tokenClient.getToken(
alice.address(),
collectionName,
tokenName,
`${tokenPropertyVersion}`,
);
console.log(`Alice's token balance: ${aliceBalance1["amount"]}`);

步骤 4.8:提供和领取 Token

许多用户收到了不需要的 Token,这些 Token可能会引起严重后果的最低限度的尴尬。 Aptos 赋予每个账户所有者决定是否接收单边转账的权利。 默认情况下,不支持单边转账。 因此 Aptos 为 提供声明 Token提供了一个框架。

提供 Token:

const txnHash3 = await tokenClient.offerToken(
alice,
bob.address(),
alice.address(),
collectionName,
tokenName,
1,
tokenPropertyVersion,
);

领取 Token:

const txnHash4 = await tokenClient.claimToken(
bob,
alice.address(),
alice.address(),
collectionName,
tokenName,
tokenPropertyVersion,
);

步骤 4.9:安全的单边 Token转移

为了支持 Token的安全单边转移,发送者可能首先要求接收者确认链下关于待处理的转移。 这以多代理事务请求的形式出现。 多代理交易包含多个签名,每个链上账户一个。 然后,Move 可以利用它为所有已签名的人提供签名者级别的权限。 对于 Token转移,这可确保接收方确实希望接收此 Token,而无需使用上述 Token转移框架。

let txnHash5 = await tokenClient.directTransferToken(
bob,
alice,
alice.address(),
collectionName,
tokenName,
1,
tokenPropertyVersion,
);

步骤 4.10:启用单边代币转移

Coming soon.

Coming soon.