Approval
Trong bài hướng dẫn này bạn sẽ học được những điều cơ bản của một hệ thống approval management, nó sẽ cho phép bạn cấp quyền truy cập cho người khác để transfer các NFT thay mình. Đây là xương sống của tất cả các NFT marketplace và cho phép một số tình huống phức tạp nhưng cần thiết xảy ra. Nếu bạn tham gia với chúng tôi lần đầu, đừng ngại clone repository này và checkout branch 4.core
để theo dõi.
git checkout 4.core
5.approval
branch. :::Giới thiệu
Up until this point you've created a smart contract that allows users to mint and transfer NFTs as well as query for information using the enumeration standard. Như chúng ta đã làm trong các hướng dẫn trước, hãy chia vấn đề thành các nhiệm vụ nhỏ hơn, dễ xử lý. Let's first define some of the end goals that we want to accomplish as per the approval management extension of the standard. Chúng ta muốn user có khả năng:
- Grant other accounts access to transfer their NFTs on a per token basis.
- Check if an account has access to a specific token.
- Revoke a specific account the ability to transfer an NFT.
- Revoke all other accounts the ability to transfer an NFT.
Nếu bạn nhìn vào tất cả các mục tiêu này, chúng đều dựa trên cơ sở mỗi token. Đây là một dấu hiệu rõ ràng rằng bạn nên thay đổi cấu trúc Token
để theo dõi thông tin cho từng token.
Cho phép một account transfer NFT của bạn
Hãy bắt đầu bằng cách cố gắng hoàn thành mục tiêu đầu tiên. Làm cách nào bạn có thể cấp cho một account khác quyền truy cập để thay bạn chuyển một NFT?
Cách đơn giản nhất mà bạn có thể làm là thêm danh sách các account được chấp thuận vào cấu trúc Token
. Khi transfer NFT, nếu người gọi không phải là chủ sở hữu, bạn có thể kiểm tra xem họ có trong danh sách này hay không.
Trước khi transfer, bạn cần xoá danh sách các account được chấp thuận vì chủ sở hữu mới sẽ không muốn các account được chủ sở hữu ban đầu chấp thuận vẫn có quyền truy cập để transfer NFT mới của họ.
Vấn đề
Nhìn bên ngoài, điều này sẽ hoạt động, nhưng nếu bạn bắt đầu nghĩ về các trường hợp phức tạp, một số vấn đề sẽ nảy sinh. Thông thường khi thực hiện development, một cách tiếp cận phổ biến là nghĩ về giải pháp dễ dàng và đơn giản nhất. Khi bạn đã tìm giải pháp, bạn có thể bắt đầu phân nhánh để suy nghĩ về cách tối ưu hóa và các trường phức tạp.
Hãy xem xét kịch bản sau đây. Benji có một NFT và cấp cho hai marketplace riêng biệt quyền truy cập để transfer token của mình. Bằng cách làm như vậy, anh ấy đang đặt bán NFT (tìm hiểu thêm về điều đó trong phần tích hợp marketplace). Giả sử anh ấy đặt bán NFT với giá 1 NEAR trên cả hai market. The token's list of approved account IDs would look like the following:
Token: {
owner_id: Benji
approved_accounts_ids: [marketplace A, marketplace B]
}
Josh sau đó đến và mua NFT trên marketplace A với giá 1 NEAR. Điều này sẽ gỡ NFT đang bán khỏi marketplace A và xoá danh sách các account được chấp thuận. Tuy nhiên vẫn có token được bán với giá 1 NEAR trên marketplace B và không có cách nào để biết rằng token này đã được mua trên marketplace A bởi Josh. Cấu trúc token mới sẽ trông như sau:
Token: {
owner_id: Josh
approved_accounts_ids: []
}
Giả sử Josh đang thiếu tiền mặt và muốn đẩy nhanh NFT này đi và bán nó với giá gấp 10 lần giá trên marketplace B. Anh ta bán nó và vì lý do nào đó, marketplace được build theo cách mà nếu bạn cố gắng đưa một token lên để bán hai lần, nó sẽ giữ lại dữ liệu bán hàng của lần đầu. Điều này có nghĩa rằng từ góc nhìn của marketplace B, token vẫn được bán với giá 1 NEAR (đó là giá mà Benji đã niêm yết ban đầu).
Vì Josh đã chấp thuận cho marketplace thử và bán nó, cấu trúc token sẽ trông như sau:
Token: {
owner_id: Josh
approved_accounts_ids: [marketplace A, marketplace B]
}
Nếu sau đó Mike đến và mua NFT chỉ với 1 NEAR trên marketplace B, marketplace sẽ thử chuyển NFT và về mặt kỹ thuật, Josh đã chấp thuận marketplace và nó được nằm trong danh sách các approve account, giao dịch sẽ diễn ra bình thường.
Phương pháp
Bây giờ chúng ta đã xác định được vấn đề với giải pháp ban đầu, hãy nghĩ về những cách mà chúng ta có thể khắc phục nó. Điều gì sẽ xảy ra nếu bây giờ, thay vì chỉ theo dõi danh sách các approve account, bạn thêm một ID đặc biệt đi cùng với mỗi approve account. Các approve account mới bây giờ sẽ là một map thay vì một list. Nó sẽ map một account tới approval id
của nó.
Để điều này hoạt động, bạn cần đảm bảo rằng approval ID luôn luôn là một ID mới và unique. Nếu bạn đặt nó là một integer luôn tăng 1 đơn vị bất cứ khi nào bạn chấp thuận một account, nó sẽ hoạt động. Hãy xem xét cùng một kịch bản với giải pháp mới.
Benji bán NFT của mình với giá 1 NEAR trên marketplace A và marketplace B bằng cách chấp thuận cả hai marketplace. "Next approval ID" sẽ bắt đầu từ 0 khi NFT được mint lần đầu tiên và sẽ tăng lên từ con số đó. Điều này sẽ dẫn đến cấu trúc token như sau:
Token: {
owner_id: Benji
approved_accounts_ids: {
marketplace A: 0
marketplace B: 1
}
next_approval_id: 2
}
Khi Benji chấp thuận marketplace A, giá trị ban đầu của next_approval_id
bắt đầu bằng 0. Sau đó marketplace được chèn vào map và next approval ID được tăng lên. Quá trình này lại xảy ra đối với marketplace B và next approval ID lại được tăng lên, giá trị hiện tại của nó là 2.
Josh đến và mua NFT trên marketplace A với giá 1 NEAR. Lưu ý rằng next approval ID đang có giá trị là 2:
Token: {
owner_id: Josh
approved_accounts_ids: {}
next_approval_id: 2
}
Josh sau đó đẩy nhanh NFT này bởi vì anh ấy một lần nữa thiếu tiền mặt và chấp thuận marketplace B:
Token: {
owner_id: Josh
approved_accounts_ids: {
marketplace B: 2
}
next_approval_id: 3
}
Marketplace được chèn vào map và next approval ID được tăng lên. Từ góc nhìn của marketplace B, nó lưu trữ approval ID ban đầu có giá trị là 1 từ khi Benji bán NFT đó. Nếu Mike đi và mua NFT trên marketplace B với giá bán 1 NEAR ban đầu, NFT contract sẽ bị panic. Điều này là do marketplace đang cố gắng chuyển NFT với approval ID là 1 nhưng cấu trúc token cho thấy rằng nó nên có approval ID là 2.
Mở rộng các cấu trúc Token
and JsonToken
Bây giờ bạn đã hiểu giải pháp được đề xuất cho vấn đề ban đầu là cho phép account chuyển NFT của bạn, đã đến lúc thực hiện một số logic. Điều đầu tiên bạn nên làm là sửa đổi cấu trúc của Token
and JsonToken
để phản ánh những thay đổi mới. Hãy chuyển sang file nft-contract/src/metadata.ts
:
Loading...
You'll then need to initialize both the approved_account_ids
and next_approval_id
to their default values when a token is minted. Switch to the nft-contract/src/mint.ts
file and when creating the Token
struct to store in the contract, let's set the next approval ID to be 0 and the approved account IDs to be an empty object:
Loading...
Các approve account
Bây giờ bạn đã thêm hỗ trợ cho các approve account ID và next approval ID ở token level, đã đến lúc thêm logic để tạo và thay đổi các field đó thông qua một function gọi là nft_approve
. Function này sẽ chấp thuận một account để có quyền truy cập vào một token ID cụ thể. Let's move to the nft-contract/src/approval.ts
file and edit the internalNftApprove
function:
Loading...
Trước tiên, function sẽ xác nhận rằng người dùng đã đính kèm ít nhất một yoctoNEAR (chút nữa chúng ta sẽ triển khai). Điều này để đảm bảo security và trả phí storage. Khi ai đó chấp thuận một account ID, họ sẽ lưu trữ thông tin đó trên contract. Như bạn đã thấy trong hướng dẫn mint, bạn có thể yêu cầu smart contract account thanh toán cho storage, hoặc bạn có thể bắt người dùng thanh toán chi phí đó. Cách tiếp cận sau có khả năng mở rộng tốt hơn và đó là cách mà bạn sẽ làm việc trong suốt hướng dẫn này.
Sau khi xác nhận xong mà không có vấn đề gì, bạn sẽ nhận được token object và đảm bảo rằng chỉ chủ sở hữu mới call method này. Chỉ chủ sở hữu mới có thể cho phép các account khác transfer các NFT của họ. Sau đó, bạn nhận được next approval ID và insert account đã truyền vào trong map cùng với next approval ID. Nếu nó là một approval ID mới, cần phải thanh toán phí cho storage. Còn nếu nó không phải là một approval ID mới, không cần phải thanh toán phí storage và chỉ đính kèm 1 yoctoNEAR là đủ.
Sau đó, bạn tính toán lượng storage đang được sử dụng bằng cách thêm account mới đó vào map và tăng next_approval_id
của token lên 1 đơn vị. After inserting the token object back into the tokensById
map, you refund any excess storage.
Bạn sẽ nhận thấy rằng function chứa một tham số tùy chọn là msg
. Tham số này thực chất là nền tảng của tất cả các NFT marketplace trên NEAR.
Tích hợp marketplace
Nếu một message được truyền vào trong function, bạn sẽ tiến hành một cross contract call tới account đang được cấp quyền truy cập. Cross contract call này sẽ gọi function nft_on_approve
, function này sẽ parse message và hành động tương ứng. Hãy xem một trường hợp sử dụng hay gặp.
Chúng ta có một marketplace với kỳ vọng rằng các điều kiện bán hàng của nó sẽ được thông qua message field. Benji chấp thuận marketplace với function nft_approve
và truyền một JSON string để phác thảo các điều kiện bán qua message. Các điều kiện bán hàng này có thể trông giống như dưới đây:
sale_conditions: {
price: 5
}
Bằng cách để message field type chỉ là một string, điều này sẽ tổng quát hóa quy trình và cho phép user nhập các điều kiện bán hàng cho nhiều marketplace khác nhau. Tuy thuộc vào người chấp thuận để truyền vào một message thích hợp mà marketplace có thể giải mã và sử dụng đúng cách. Điều này thường được thực hiện thông qua frontend app của marketplace, ứng dụng này có thể biết cách cấu trúc msg
theo cách hữu ích.
Các Internal function
Now that the core logic for approving an account is finished, you need to implement the assertAtLeastOneYocto
and bytesForApprovedAccountId
functions. Move to the nft-contract/src/internal.ts
file and copy the following function right below the assertOneYocto
function.
Loading...
Tiếp theo, bạn sẽ cần copy logic để tính toán chi phí để lưu trữ một account ID là bao nhiêu byte. Đặt function này ở đầu trang:
Loading...
Bây giờ, logic để approve account đã hoàn thành, bạn cần thay đổi các hạn chế cho việc transfer.
Thay đổi các hạn chế cho việc transfer các NFT
Hiện tại, một NFT chỉ có thể transfer bởi người sở hữu nó. Bạn cần thay đổi hạn chế đó để những người đã được chấp thuận cũng có thể transfer các NFT. Ngoài ra, bạn sẽ làm điều đó để nếu một approval ID được truyền vào, bạn có thể tăng cường security và kiểm tra nếu cả hai account đang cố gắng transfer có nằm trong approve list hay không và chúng tương ứng đúng với approval ID. Điều này là để giải quyết vấn đề mà chúng ta đã gặp phải trước đó.
In the internal.ts
file, you need to change the logic of the internalTransfer
method as that's where the restrictions are being made. Thay đổi internal transfer function thành như sau:
Loading...
Việc này sẽ kiểm tra xem người gửi có phải là chủ sở hữu hay không và sau đó nếu họ không phải là chủ sở hữu, nó sẽ kiểm tra người gửi có trong approval list hay không. Nếu một approve ID được truyền vào function, nó sẽ kiểm tra approval ID thực tế của người gửi đã được lữu trữ trên contract có khớp với approve ID được truyền vào hay không.
Hoàn trả storage khi transfer
Trong khi bạn đang ở internal file, bạn sẽ cần thêm các method để hoàn lại tiền cho user đã trả cho việc lưu trữ các approve account trên contract khi một NFT được transfer. This is because you'll be clearing the approved_account_ids
object whenever NFTs are transferred and so the storage is no longer being used.
Loading...
Việc này sẽ hữu ích trong phần tiếp theo, nơi bạn sẽ thay đổi function nft_core
để thêm vào logic approval mới.
Changes to nft_core.ts
Head over to the nft-contract/src/nft_core.ts
file and the first change that you'll want to make is to add an approval_id
to the internalTransfer
function. Việc này để bất kỳ ai không phải là chủ sở hữu đang cố gắng transfer token, phải truyền vào một approval ID để giải quyết vấn đề đã thấy trước đó. If they are the owner, the approval ID won't be used as we saw in the internalTransfer
function.
For the nft_transfer
function, the only change that you'll need to make is to pass in the approval ID into the internalTransfer
function and then refund the previous tokens approved account IDs after the transfer is finished
Loading...
Next, you need to do the same to nft_transfer_call
but instead of refunding immediately, you need to attach the previous token's approved account IDs to nft_resolve_transfer
instead as there's still the possibility that the transfer gets reverted.
Loading...
Bạn cũng cần thêm các approve account ID của các token vào JsonToken
được trả về bởi nft_token
.
Loading...
Finally, you need to add the logic for refunding the approved account IDs in internalResolveTransfer
. Nếu quá trình transfer thành công, bạn nên hoàn lại tiền cho chủ sở hữu vì đã giải phóng storage bởi reset field approved_account_ids
của các token. Tuy nhiên, bạn nên revert quá trình transfer nếu không đủ tiền để hoàn lại cho bất kỳ ai. Vì người nhận đã sở hữu token, họ có thể đã thêm các approve account ID của riêng mình và vì vậy bạn nên hoàn lại tiền cho người gửi nếu người nhận đã làm vậy.
Loading...
Với việc hoàn thành điều đó, đã đến lúc tiếp tục và hoàn thành nhiệm vụ tiếp theo.
Kiểm tra một account được chấp thuận hay không
Giờ đây, logic cốt lõi đã được áp dụng cho chấp thuận và hoàn tiền cho các account, nên kể từ thời điểm này trở đi mọi việc sẽ diễn ra suôn sẻ. Bây giờ bạn cần triển khai logic để kiểm tra xem một account đã được chấp thuận hay chưa. Việc này cần một account và token ID cũng như một tùy chọn approval ID. Nếu approval ID không được cung cấp, nó đơn giản trả về việc account có được chấp thuận hay không.
Nếu một approval ID được cung cấp, nó sẽ trả về việc account có được chấp thuận và có cùng approval ID với account đã cung cấp hay không. Let's move to the nft-contract/src/approval.ts
file and add the necessary logic to the internalNftIsApproved
function.
Loading...
Bây giờ chúng ta hãy tiếp tục và thêm logic để thu hồi account
Thu hồi account
Bước tiếp theo trong hướng dẫn này là cho phép người dùng thu hồi một account được chỉ định khỏi quyền truy cập NFT của họ. Điều đầu tiên bạn sẽ muốn làm là yêu cầu một yocto cho mục đích security. Sau đó bạn cần đảm bảo rằng người gọi là chủ sở hữu của token. Nếu những điều đó đều được đáp ứng, bạn sẽ cần xóa account đã truyền vào khỏi các approve account ID của các token và hoàn lại tiền cho chủ sở hữu vì storage được giải phóng.
Loading...