> For the complete documentation index, see [llms.txt](https://gushi10546.gitbook.io/aptos-kai-fa-zhe-wen-dang/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gushi10546.gitbook.io/aptos-kai-fa-zhe-wen-dang/kai-fa-zhe-jiao-cheng/di-yi-ge-nft.md).

# 第一个 NFT

本教程通过以下的分步步骤描述如何在 Aptos 区块链上创建和转移 NFT。核心 NFT 或代币的 Aptos 实例可以在 [token.move](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-token/sources/token.move) 中找到。

## 第 1 步：选择一个 SDK

* [Aptos 官方 Typescript SDK](https://aptos.dev/sdks/typescript-sdk)
* [Aptos 官方 Python SDK](https://aptos.dev/sdks/python-sdk)
* Aptos 官方 Rust SDK —— 待发布

## 第 2 步：运行示例

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

克隆 `aptos-core`：

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

* Typescript

  导航至 Typescript SDK 示例目录：

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

  安装必要的依赖项：

  ```bash
  yarn install
  ```

  运行 `simple-nft` 示例：

  ```bash
  yarn run simple_nft
  ```
* Python

  导航至 Python SDK 示例目录：

  ```bash
  cd ~/aptos-core/ecosystem/python/sdk
  ```

  安装必要的依赖项：

  ```bash
  curl -sSL https://install.python-poetry.org | python3
  poetry update
  ```

  运行 `simple-nft` 示例：

  ```bash
  poetry run python -m examples.simple-nft
  ```
* Rust

  待发布

## 第 3 步：理解输出

执行 simple-nft 示例后，应出现以下输出，但某些值会有所不同：

```rust
=== 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

* Typescript

  💡 **见完整示例**

  查看 [`[simple_nft]`](https://github.com/aptos-labs/aptos-core/blob/main/ecosystem/typescript/sdk/examples/typescript/simple_nft.ts) 完整代码，按照以下步骤操作
* Python

  💡 **见完整示例**

  查看 [`[simple_nft]`](https://github.com/aptos-labs/aptos-core/blob/main/ecosystem/python/sdk/examples/simple-nft.py) 完整代码，按照以下步骤操作
* Rust

  待发布

### 第 4.1 步：初始化客户端

在第 1 步中，示例初始化 API 和 Faucet 客户端。

* API 客户端与 REST API 交互，并且
* Faucet 客户端与 devnet Faucet 服务交互以创建和注资帐户
* Typescript

  ```tsx
  const client = new AptosClient(NODE_URL);
  const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL);
  ```

  使用 API 客户端，我们可以创建一个 T`okenClient`，用于常见的代币操作，例如创建、转移和领取集合和代币等。

  ```tsx
  const tokenClient = new TokenClient(client);
  ```

  common.ts 将 URL 值初始化为这样：

  ```tsx
  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

  ```python
  rest_client = RestClient(NODE_URL)
  faucet_client = FaucetClient(FAUCET_URL, rest_client)
  ```

  `common.py` 将这些值初始化为这样：

  ```python
  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

  ```tsx
  const alice = new AptosAccount();
  const bob = new AptosAccount();
  ```
* Python

  ```python
  alice = Account.generate()
  bob = Account.generate()
  ```
* Rust

  待发布

### 第 4.3 步：创建链上账户

在 Aptos 中，每个帐户都必须有一个链上表示，以支持接收代币和货币，以及在其他 dApp 中进行互动。一个帐户代表了一个存储资产的媒介，因此必须明确创建。这个示例利用 Faucet 来创建 Alice 和 Bob 的帐户。只有 Alice 帐户里有资金：

* Typescript

  ```tsx
  await faucetClient.fundAccount(alice.address(), 20_000);
  await faucetClient.fundAccount(bob.address(), 20_000);
  ```
* Python

  ```python
  faucet_client.fund_account(alice.address(), 20_000)
  faucet_client.fund_account(bob.address(), 20_000)
  ```
* Rust

  待发布

### 第 4.4 步：创建一个集合

现在开始创建代币的过程。首先，创建者必须创建一个集合来存储代币。一个集合可以包含零个、一个或多个不同的代币。集合只是一个容器，并不限制代币的属性：

* Typescript

  应用程序将调用 `creatColletion`：

  ```tsx
  const txnHash1 = await tokenClient.createCollection(
    alice,
    collectionName,
    "Alice's simple collection",
    "https://alice.com",
  );
  ```

  `createCollection`的函数签名。它返回一个交易哈希值：

  ```tsx
  async createCollection(
    account: AptosAccount,
    name: string,
    description: string,
    uri: string,
    maxAmount: BCS.AnyNumber = MAX_U64_BIG_INT,
  ): Promise<string> {
  ```
* Python

  应用程序将调用 `create_colletion`：

  ```python
  txn_hash = rest_client.create_collection(
      alice, collection_name, "Alice's simple collection", "https://aptos.dev"
  )
  ```

  `create_colletion`的函数签名。它返回一个交易哈希值：

  ```python
  def create_collection(
      self, account: Account, name: str, description: str, uri: str
  ) -> str:
  ```
* Rust

  待发布

### 第 4.5 步：创建一个代币

要创建一个代币，创建者必须指定一个相关的集合。一个代币必须与一个集合相关联，并且该集合必须有可以被铸造的剩余代币。有许多与代币相关的属性，但辅助 API 只公开了创建静态内容所需的最小数量。

* Typescript

  应用程序将调用 `creatToken`：

  ```tsx
  const txnHash2 = await tokenClient.createToken(
    alice,
    collectionName,
    tokenName,
    "Alice's simple token",
    1,
    "https://aptos.dev/img/nyan.jpeg",
  );
  ```

  `createToken`的函数签名。它返回一个交易哈希值：

  ```tsx
  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`：

  ```python
  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`的函数签名。它返回一个交易哈希值：

  ```python
  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

  读取集合的元数据：

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

  读取代币的元数据：

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

  以下是 `getTokenData` 如何查询代币元数据的：

  ```tsx
  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

  读取集合的元数据：

  ```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)}"
  )
  ```

  读取代币的元数据：

  ```python
  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`如何查询代币元数据的：

  ```python
  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

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

  ```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

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

  ```python
  txn_hash = rest_client.offer_token(
      alice,
      bob.address(),
      alice.address(),
      collection_name,
      token_name,
      property_version,
      1,
  )
  ```
* Rust

  待发布

领取代币：

* Typescript

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

  ```python
  txn_hash = rest_client.claim_token(
      bob,
      alice.address(),
      alice.address(),
      collection_name,
      token_name,
      property_version,
  )
  ```
* Rust

  待发布

### 第 4.9 步：代币的安全单边转账

为支持代币安全单边转账，发送方可以首先要求接收方确认链下的待定转移。这里以多代理交易请求的形式出现。多代理交易包含多个签名，每个链上帐户都有一个签名。然后，Move 可以利用这一点给所有签名的人以签名者级别的权限。对于代币转移，这样确保了接收方确实希望收到这个代币，而不需要使用上述的代币转移框架。

* Typescript

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

  ```python
  txn_hash = rest_client.direct_transfer_token(
      bob, alice, alice.address(), collection_name, token_name, 0, 1
  )
  ```
* Rust

  待发布

### 第 4.10 步：实现代币单边转账

即将上线！


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gushi10546.gitbook.io/aptos-kai-fa-zhe-wen-dang/kai-fa-zhe-jiao-cheng/di-yi-ge-nft.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
