---
title: 非同质化代币 (NFT)
description: 在Tact中处理非同质化代币 (NFT) 的常见示例
---

此页列出了与 [NFTs]交互的常见例子(https://docs.ton.org/develop/dapps/asset-processing/nfts)。

## 接受 NFT 所有权分配 {#accepting-nft-ownership-assignment}

指派的 NFT 所有权的通知信息具有以下结构：

```tact
message(0x05138d91) NFTOwnershipAssigned {
    previousOwner: Address;
    forwardPayload: Slice as remaining;
}
```

使用 [receiver](/zh-cn/book/receive) 函数来接受通知消息。

:::caution

  必须验证通知发件人！

:::

验证可以通过两种方式进行：

1. 直接保存 NFT 项目地址并进行验证。

```tact
message(0x05138d91) NFTOwnershipAssigned {
    previousOwner: Address;
    forwardPayload: Slice as remaining;
}

contract SingleNft {
    nftItemAddress: Address;

    init(nftItemAddress: Address) {
        self.nftItemAddress = nftItemAddress;
    }

    receive() { cashback(sender()) } // for the deployment

    receive(msg: NFTOwnershipAssigned) {
        require(self.nftItemAddress == sender(), "NFT contract is not the sender");

        // your logic of processing nft ownership assign notification
    }
}
```

2. 使用 [`StateInit{:tact}`](/zh-cn/book/expressions#initof) 和派生的 NFT 项目地址。

```tact
message(0x05138d91) NFTOwnershipAssigned {
    previousOwner: Address;
    forwardPayload: Slice as remaining;
}

struct NFTItemInitData {
    index: Int as uint64;
    collectionAddress: Address;
}

inline fun calculateNFTAddress(index: Int, collectionAddress: Address, nftCode: Cell): Address {
    let initData = NFTItemInitData{
        index,
        collectionAddress,
    };

    return contractAddress(StateInit{code: nftCode, data: initData.toCell()});
}

contract NftInCollection {
    nftCollectionAddress: Address;
    nftItemIndex: Int as uint64;
    nftCode: Cell;

    init(nftCollectionAddress: Address, nftItemIndex: Int, nftCode: Cell) {
        self.nftCollectionAddress = nftCollectionAddress;
        self.nftItemIndex = nftItemIndex;
        self.nftCode = nftCode;
    }

    receive() { cashback(sender()) } // for the deployment

    receive(msg: NFTOwnershipAssigned) {
        let expectedNftAddress = calculateNFTAddress(self.nftItemIndex, self.nftCollectionAddress, self.nftCode); // or you can even store expectedNftAddress
        require(expectedNftAddress == sender(), "NFT contract is not the sender");

        // your logic of processing nft ownership assign notification
    }
}
```

由于NFT项目的初始数据布局可能各不相同，第一种方法往往更合适。

## 转移 NFT 项目

要发送 NFT 项目转移，请使用 [`send(){:tact}`](/zh-cn/book/send)函数。

```tact
import "@stdlib/deploy";

message(0x5fcc3d14) NFTTransfer {
    queryId: Int as uint64;
    newOwner: Address; // address of the new owner of the NFT item.
    responseDestination: Address; // address where to send a response with confirmation of a successful transfer and the rest of the incoming message coins.
    customPayload: Cell? = null; //  optional custom data. In most cases should be null
    forwardAmount: Int as coins; // the amount of nanotons to be sent to the new owner.
    forwardPayload: Slice as remaining; // optional custom data that should be sent to the new owner.
}

contract Example {
    nftItemAddress: Address;

    init(nftItemAddress: Address) {
        self.nftItemAddress = nftItemAddress;
    }

    // ... add more code from previous examples ...

    receive("transfer") {
        send(SendParameters {
            to: self.nftItemAddress,
            value: ton("0.1"),
            body: NFTTransfer{
                queryId: 42,
                // FIXME: Change this according to your needs
                newOwner: sender(),
                responseDestination: myAddress(),
                customPayload: null,
                forwardAmount: 1,
                forwardPayload: rawSlice("F"), // precomputed beginCell().storeUint(0xF, 4).endCell().beginParse()
            }.toCell(),
        });
    }
}
```

## 获取 NFT 静态信息

请注意，TON Blockchain 不允许合约相互调用 [getters](https://docs.tact-lang.org/book/contracts#getter-functions)。
要从另一个合约接收数据，您必须交换消息。

```tact
message(0x2fcb26a2) NFTGetStaticData {
    queryId: Int as uint64;
}

message(0x8b771735) NFTReportStaticData {
    queryId: Int as uint64;
    index: Int as uint256;
    collection: Address;
}

struct NFTItemInitData {
    index: Int as uint64;
    collectionAddress: Address;
}

inline fun calculateNFTAddress(index: Int, collectionAddress: Address, nftCode: Cell): Address {
    let initData = NFTItemInitData{
        index,
        collectionAddress,
    };

    return contractAddress(StateInit{code: nftCode, data: initData.toCell()});
}

contract Example {
    nftCollectionAddress: Address;
    nftItemIndex: Int as uint64;
    nftCode: Cell;

    init(nftCollectionAddress: Address, nftItemIndex: Int, nftCode: Cell) {
        self.nftCollectionAddress = nftCollectionAddress;
        self.nftItemIndex = nftItemIndex;
        self.nftCode = nftCode;
    }

    // ... add more code from previous examples ...

    receive("get static data") {
        // FIXME: Put proper address("[NFT_ADDRESS]") here
        let nftAddress = sender();
        send(SendParameters {
            to: nftAddress,
            value: ton("0.1"),
            body: NFTGetStaticData{
                queryId: 42,
            }.toCell(),
        });
    }

    receive(msg: NFTReportStaticData) {
        let expectedNftAddress = calculateNFTAddress(msg.index, msg.collection, self.nftCode);
        require(expectedNftAddress == sender(), "NFT contract is not the sender");

        // Save nft static data or do something
    }
}
```

## 获取 NFT 版税参数

此处](https://github.com/ton-blockchain/TEPs/blob/master/text/0066-nft-royalty-standard.md)介绍了 NFT 版税参数。

```tact
message(0x693d3950) NFTGetRoyaltyParams {
    queryId: Int as uint64;
}

message(0xa8cb00ad) NFTReportRoyaltyParams {
    queryId: Int as uint64;
    numerator: Int as uint16;
    denominator: Int as uint16;
    destination: Address;
}

contract Example {
    nftCollectionAddress: Address;

    init(nftCollectionAddress: Address) {
        self.nftCollectionAddress = nftCollectionAddress;
    }

    // ... add more code from previous examples ...

    receive("get royalty params") {
        send(SendParameters {
            to: self.nftCollectionAddress,
            value: ton("0.1"),
            body: NFTGetRoyaltyParams{
                queryId: 42,
            }.toCell(),
        });
    }

    receive(msg: NFTReportRoyaltyParams) {
        require(self.nftCollectionAddress == sender(), "NFT collection contract is not the sender");

        // Do something with msg
    }
}
```

## NFT 集合方法

:::caution

  这些方法不是任何标准的一部分，它们只能与 [此特定实现](https://github.com/ton-blockchain/token-contract/blob/main/nft/nft-collection.fc) 一起工作。 请在使用之前记住这一点。

:::

请注意，只有NFT所有者才能使用这些方法。

### 部署 NFT

```tact
message(0x1) NFTDeploy {
    queryId: Int as uint64;
    itemIndex: Int as uint64;
    amount: Int as coins; // amount to sent when deploying nft
    nftContent: Cell;
}

contract Example {
    nftCollectionAddress: Address;

    init(nftCollectionAddress: Address) {
        self.nftCollectionAddress = nftCollectionAddress;
    }

    // ... add more code from previous examples ...

    receive("deploy") {
        send(SendParameters {
            to: self.nftCollectionAddress,
            value: ton("0.14"),
            body: NFTDeploy{
                queryId: 42,
                itemIndex: 42,
                amount: ton("0.1"),
                nftContent: beginCell().endCell() // FIXME: Should be your content, mostly generated offchain
            }.toCell(),
        });
    }
}
```

### 更改所有者

```tact
message(0x3) NFTChangeOwner {
    queryId: Int as uint64;
    newOwner: Address;
}

contract Example {
    nftCollectionAddress: Address;

    init(nftCollectionAddress: Address) {
        self.nftCollectionAddress = nftCollectionAddress;
    }

    // ... add more code from previous examples ...

    receive("change owner") {
        send(SendParameters {
            to: self.nftCollectionAddress,
            value: ton("0.05"),
            body: NFTChangeOwner{
                queryId: 42,
                // FIXME: Put proper address("NEW_OWNER_ADDRESS") here
                newOwner: sender(),
            }.toCell(),
        });
    }
}
```

:::tip[Hey there!]

  没有找到您最喜欢的 NFT 操作的例子？ 您有很酷的实施方案吗？ 本页为残页。 [欢迎投稿！](https://github.com/tact-lang/tact/issues)

:::
