你的第一个 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
- Python
- Rust
切换到 Typescript SDK 示例目录:
cd ~/aptos-core/ecosystem/typescript/sdk/examples/typescript
安装必要的依赖项:
yarn install
运行脚本示例 simple_nft
:
yarn run simple_nft
切换到 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
Coming soon.
第 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
- Typescript
- Python
- Rust
步骤 4.1:初始化客户端
在第一步中,示例初始化 API 和水龙头客户端。
- API 客户端与 REST API 交互,以及
- 水龙头客户端与 devnet Faucet 服务交互以创建和注资帐户。
- Typescript
- Python
- Rust
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";
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://tap.devnet.prod.gcp.aptosdev.com", # "https://faucet.testnet.aptoslabs.com"
)
Coming soon.
默认情况下,这两个服务的 URL 都指向 Aptos devnet 服务。 但是,可以使用以下环境变量配置它们:
APTOS_NODE_URL
APTOS_FAUCET_URL
步骤 4.2:创建本地帐户
下一步是在本地创建两个帐户。 Accounts 代表链上和链下状态。 链下状态由地址和用于验证所有权的公钥、私钥对组成。 此步骤演示如何生成该链下状态。
- Typescript
- Python
- Rust
const alice = new AptosAccount();
const bob = new AptosAccount();
alice = Account.generate()
bob = Account.generate()
Coming soon.
步骤 4.3:创建区块链账户
在 Aptos 中,每个帐户都必须具有链上表示,以支持接收 Token和代币以及与其他 dApp 进行交互。 账户代表了存储资产的媒介,因此必须明确创建。 此示例利用水龙头创建 Alice 和 Bob 的帐户:
- Typescript
- Python
- Rust
await faucetClient.fundAccount(alice.address(), 100_000_000);
await faucetClient.fundAccount(bob.address(), 100_000_000);
faucet_client.fund_account(alice.address(), 100_000_000)
faucet_client.fund_account(bob.address(), 100_000_000)
Coming soon.
步骤 4.4:创建集合
现在开始创建 Token 的过程。 首先,创建者必须创建一个集合来存储 Token 。 一个集合中可以包含零个、一个或多个不同的 Token。 该集合不限制 Token 的属性,因为它只是一个容器。
- Typescript
- Python
- Rust
您的应用程序将调用 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> {
您的应用程序将调用 create_collection
:
txn_hash = rest_client.create_collection(
alice, collection_name, "Alice's simple collection", "https://aptos.dev"
)
create_collection
的函数签名。 它返回一个事务哈希:
def create_collection(
self, account: Account, name: str, description: str, uri: str
) -> str:
Coming soon.
步骤 4.5:创建 Token
要创建 Token ,创建者必须指定关联的集合。 一个 Token必须与一个集合相关联,并且该集合必须有剩余的可以铸造的 Token。 有许多与 Token 相关联的属性,但帮助程序 API 仅公开创建静态内容所需的最少数量。
- Typescript
- Python
- Rust
您的应用程序将调用 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> {
您的应用程序将调用 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:
Coming soon.
步骤 4.6:读取 Token 和集合元数据
集合和 Token 元数据都存储在创建者帐户的集合
中的表格中。 SDK 为查询这些特定表提供了便利的方法:
- Typescript
- Python
- Rust
读取集合的元数据:
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);
}
读取集合的元数据:
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的元数据:
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
查询 Token元数据的方式:
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,
)
Coming soon.
步骤 4.7:读取 Token余额
Aptos 中的每个 Token都是不同的资产,用户拥有的资产存储在他们的TokenStore
中。 要获得余额:
- Typescript
- Python
- Rust
const aliceBalance1 = await tokenClient.getToken(
alice.address(),
collectionName,
tokenName,
`${tokenPropertyVersion}`,
);
console.log(`Alice's token balance: ${aliceBalance1["amount"]}`);
balance = rest_client.get_token_balance(
alice.address(), alice.address(), collection_name, token_name, property_version
)
print(f"Alice's token balance: {balance}")
Coming soon.
步骤 4.8:提供和领取 Token
许多用户收到了不需要的 Token,这些 Token可能会引起严重后果的最低限度的尴尬。 Aptos 赋予每个账户所有者决定是否接收单边转账的权利。 默认情况下,不支持单边转账。 因此 Aptos 为 提供 和 声明 Token提供了一个框架。
提供 Token:
- Typescript
- Python
- Rust
const txnHash3 = await tokenClient.offerToken(
alice,
bob.address(),
alice.address(),
collectionName,
tokenName,
1,
tokenPropertyVersion,
);
txn_hash = rest_client.offer_token(
alice,
bob.address(),
alice.address(),
collection_name,
token_name,
property_version,
1,
)
Coming soon!
领取 Token:
- Typescript
- Python
- Rust
const txnHash4 = await tokenClient.claimToken(
bob,
alice.address(),
alice.address(),
collectionName,
tokenName,
tokenPropertyVersion,
);
txn_hash = rest_client.claim_token(
bob,
alice.address(),
alice.address(),
collection_name,
token_name,
property_version,
)
Coming soon.
步骤 4.9:安全的单边 Token转移
为了支持 Token的安全单边转移,发送者可能首先要求接收者确认链下关于待处理的转移。 这以多代理事务请求的形式出现。 多代理交易包含多个签名,每个链上账户一个。 然后,Move 可以利用它为所有已签名的人提供签名者
级别的权限。 对于 Token转移,这可确保接收方确实希望接收此 Token,而无需使用上述 Token转移框架。
- Typescript
- Python
- Rust
let txnHash5 = await tokenClient.directTransferToken(
bob,
alice,
alice.address(),
collectionName,
tokenName,
1,
tokenPropertyVersion,
);
txn_hash = rest_client.direct_transfer_token(
bob, alice, alice.address(), collection_name, token_name, 0, 1
)
Coming soon.
步骤 4.10:启用单边代币转移
Coming soon.
- Python
- Rust
- Typescript
Coming soon.
Coming soon.
Coming soon.