Transfers
In this tutorial you'll learn how to implement NFT transfers as defined in the core standards into your smart contract.
We will define two methods for transferring NFTs:
nft_transfer
: that transfers ownership of an NFT from one account to anothernft_transfer_call
: that transfers an NFT to a "receiver" and calls a method on the receiver's account
nft_transfer
is a simple transfer between two user, while nft_transfer_call
allows you to attach an NFT to a function call
Giới thiệu
Up until this point, you've created a simple NFT smart contract that allows users to mint tokens and view information using the enumeration standards. Hôm nay, bạn sẽ mở rộng smart contract của mình để cho phép các user không chỉ mint token mà còn transfer chúng.
As we did in the minting tutorial, let's break down the problem into multiple subtasks to make our lives easier. Khi một token được mint, thông tin được lưu trữ ở 3 nơi:
- tokens_per_owner: tập các token cho mỗi account.
- tokens_by_id: map một token ID với một
Token
object. - token_metadata_by_id: map một token ID với metadata của nó.
Bây giờ hãy xem xét tình huống sau đây. Nếu Benji là người sở hữu token A và muốn chuyển nó cho Mike như một món quà sinh nhật, điều gì sẽ xảy ra? Đầu tiên, token A sẽ bị xóa khỏi tập các token của Benji và được thêm vào tập các token của Mike.
Nếu đó chỉ là logic bạn thực hiện, bạn sẽ gặp một số vấn đề. Nếu bạn thực hiện một view
call để truy vấn thông tin về token đó sau khi nó đã được chuyển cho Mike, nó sẽ vẫn trả về kết quả rằng Benji là chủ sở hữu.
Điều này là do contract vẫn đang map token ID với Token
object cũ, object đó có field owner_id
là account ID của Benji. Bạn vẫn phải thay đổi cấu trúc data của tokens_by_id
, vì thế token ID map với một Token
object mới mà Mike sở hữu.
Với những gì đã nói, quy trình cuối cùng khi chủ sở hữu chuyển token tới người nhận sẽ như sau:
- Xóa token khỏi tập token của chủ sở hữu.
- Thêm token tới tập token của người nhận.
- Map token ID tới một
Token
object mới chứa chính xác thông tin chủ sở hữu.
token_metadata_by_id
. Điều này là do bất kể ai sở hữu token, token ID sẽ luôn map tới cùng một metadata. Metadata sẽ không bao giờ thay đổi và vì vậy chúng ta có thể để nó một mình (không map với một token Id nào cả). :::Tại thời điểm này, bạn đã sẵn sàng để tiếp tục và tạo những sửa đổi quan trọng với smart contract của mình.
Các sửa đổi với contract
Let's start our journey in the nft-contract-skeleton/src/nft_core.rs
file.
Transfer function
You'll start by implementing the nft_transfer
logic. Function này sẽ chuyển token_id
được chỉ định tới receiver_id
với một tuỳ chọn memo
ví dụ như "Happy Birthday Mike!"
.
Loading...
There are a couple things to notice here. Firstly, we've introduced a new function called assert_one_yocto()
, which ensures the user has attached exactly one yoctoNEAR to the call. This is a security measure to ensure that the user is signing the transaction with a full access key.
Since the transfer function is potentially transferring very valuable assets, you'll want to make sure that whoever is calling the function has a full access key.
Secondly, we've introduced an internal_transfer
method. This will perform all the logic necessary to transfer an NFT.
Các internal helper function
Let's quickly move over to the nft-contract/src/internal.rs
file so that you can implement the assert_one_yocto()
and internal_transfer
methods.
Let's start with the easier one, assert_one_yocto()
.
assert_one_yocto
Loading...
internal_transfer
It's now time to explore the internal_transfer
function which is the core of this tutorial. This function takes the following parameters:
- sender_id: account đang cố gắng chuyển token.
- receiver_id: account nhận token.
- token_id: token ID đang được chuyển.
- memo: một tùy chọn memo kèm theo.
The first thing we have to do is to make sure that the sender is authorized to transfer the token. In this case, we just make sure that the sender is the owner of the token. We do that by getting the Token
object using the token_id
and making sure that the sender is equal to the token's owner_id
.
Second, we remove the token ID from the sender's list and add the token ID to the receiver's list of tokens. Finally, we create a new Token
object with the receiver as the owner and remap the token ID to that newly created object.
We want to create this function within the contract implementation (below the internal_add_token_to_owner
you created in the minting tutorial).
Loading...
Now let's look at the function called internal_remove_token_from_owner
. That function implements the functionality for removing a token ID from an owner's set.
In the remove function, we get the set of tokens for a given account ID and then remove the passed in token ID. If the account's set is empty after the removal, we simply remove the account from the tokens_per_owner
data structure.
Loading...
Bây giờ, file internal.rs
của bạn sẽ có outline như dưới đây:
internal.rs
├── hash_account_id
├── assert_one_yocto
├── refund_deposit
└── impl Contract
├── internal_add_token_to_owner
├── internal_remove_token_from_owner
└── internal_transfer
Transfer call function
The idea behind the nft_transfer_call
function is to transfer an NFT to a receiver while calling a method on the receiver's contract all in the same transaction.
This way, we can effectively attach an NFT to a function call.
Loading...
Trước tiên, function sẽ xác nhận rằng người gọi đã đính kèm chính xác 1 yocto cho mục đích bảo mật. Nó sau đó sẽ transfer NFT sử dụng internal_transfer
và bắt đầu cross contract call. It will call the method nft_on_transfer
on the receiver_id
's contract, and create a promise to call back nft_resolve_transfer
with the result. This is a very common workflow when dealing with cross contract calls.
As dictated by the core standard, the function we are calling (nft_on_transfer
) needs to return a boolean stating whether or not you should return the NFT to it's original owner.
Loading...
If nft_on_transfer
returned true or the called failed, you should send the token back to it's original owner. Ngược lại, nếu trả về false thì không cần thêm logic nào cả.
As for the return value of our function nft_resolve_transfer
, the standard dictates that the function should return a boolean indicating whether or not the receiver successfully received the token or not.
Điều này có nghĩa là nếu nft_on_transfer
trả về true, bạn nên trả về false. Bởi vì nếu token đang được trả lại cho chủ sở hữu ban đầu của nó, thì cuối cùng receiver_id
đã không nhận được thành công token. Ngược lại, nếu nft_on_transfer
trả về false, bạn nên trả về true vì chúng ta không cần trả về token và do đó receiver_id
sở hữu thành công token.
Với việc hoàn thành điều đó, bạn đã thêm thành công logic cần thiết để cho phép người dùng transfer các NFT. Bây giờ là lúc deploy và thực hiện một vài bài test.
Redeploy contract
Using cargo-near, deploy the contract as you did in the previous tutorials:
cargo near deploy $NFT_CONTRACT_ID without-init-call network-config testnet sign-with-keychain send
near login
. Bạn có thể export một biến môi trường export NFT_CONTRACT_ID=YOUR_ACCOUNT_ID_HERE
. :::Test các thay đổi mới
Bây giờ thì bạn đã deploy một bản vá lỗi cho contract, đã đến lúc để chuyển sang giai đoạn test. Sử dụng NFT contract trước đó mà bạn đã đã mint token cho chính mình, bạn có thể test method nft_transfer
. Nếu bạn transfer NFT, nó sẽ bị xoá khỏi các bộ sưu tập trong account của bạn được hiển thị ở wallet. Ngoài ra, nếu bạn truy vấn bất kỳ enumeration function nào, nó sẽ cho thấy rằng bạn không còn là chủ sở hữu nữa.
Hãy test điều này bằng cách transfer một NFT tới account benjiman.testnet
và xem NFT có còn thuộc quyền sở hữu của bạn hay không.
Test transfer function
benjiman.testnet
transfer nó lại cho bạn. Nếu bạn không muốn NFT của mình bị mất, tạo một account mới và transfer token tới account đó. :::Nếu bạn chạy command sau, nó sẽ transfer token "token-1"
tới account benjiman.testnet
với memo "Go Team :)"
. Hãy lưu ý rằng bạn cũng đang đính kèm chính xác 1 yoctoNEAR bằng cách sử dụng --depositYocto
flag.
token-1
với token ID của bạn. :::near contract call-function as-transaction $NFT_CONTRACT_ID nft_transfer json-args '{"receiver_id": "benjiman.testnet", "token_id": "token-1", "memo": "Go Team :)"}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as $NFT_CONTRACT_ID network-config testnet sign-with-legacy-keychain send
Nếu bây giờ bạn truy vấn tất cả các token mà account bạn sở hữu, token đó sẽ bị thiếu. Tương tự, nếu bạn truy vấn danh sách các token được sở hữu bởi benjiman.testnet
, account đó bây giờ sẽ sở hữu NFT của bạn.
Test transfer call function
Bây giờ thì bạn đã test nft_transfer
function, đến lúc để test nft_transfer_call
function. Nếu bạn cố gắng transfer một NFT tới một người nhận không tiến hành nft_on_transfer
function, contract sẽ panic và NFT sẽ không được transfer. Hãy test chức năng này bên dưới.
Đầu tiên mint một NFT mới sẽ được sử dụng để test chức năng transfer call.
near contract call-function as-transaction $NFT_CONTRACT_ID nft_mint json-args '{"token_id": "token-2", "metadata": {"title": "NFT Tutorial Token", "description": "Testing the transfer call function", "media": "https://bafybeiftczwrtyr3k7a2k4vutd3amkwsmaqyhrdzlhvpt33dyjivufqusq.ipfs.dweb.link/goteam-gif.gif"}, "receiver_id": "'$NFT_CONTRACT_ID'"}' prepaid-gas '100.0 Tgas' attached-deposit '0.1 NEAR' sign-as $NFT_CONTRACT_ID network-config testnet sign-with-legacy-keychain send
Bây giờ thì bạn đã mint token, bạn có thể thử transfer NFT tới account no-contract.testnet
, account này không có contract nào giống như tên của nó đã cho thấy. Điều này có nghĩa rằng người nhận không tiến hành nft_on_transfer
function và NFT sẽ vẫn là của bạn sau khi transaction hoàn thành.
near contract call-function as-transaction $NFT_CONTRACT_ID nft_transfer_call json-args '{"receiver_id": "no-contract.testnet", "token_id": "token-2", "msg": "foo"}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as $NFT_CONTRACT_ID network-config testnet sign-with-legacy-keychain send
Nếu bạn truy vấn các token của mình, bạn sẽ vẫn có token-2
và tại thời điểm này, bạn đã hoàn thành!
Tổng kết
Trong bài hướng dẫn này, bạn đã học cách làm thế nào để mở rộng một NFT contract thông qua mint function và bạn đã biết thêm cách để người dùng transfer các NFT. Bạn đã chia nhỏ vấn đề thành các vấn đề bé hơn, nhiều subtask dễ xử lý hơn và lấy thông tin đó để triển khai cả hai function là NFT transfer và NFT transfer call. Ngoài ra, bạn đã deploy bản vá lỗi khác tới smart contract của mình và đã test chức năng transfer.
In the next tutorial, you'll learn about the approval management system and how you can approve others to transfer tokens on your behalf.