Approvals
In this tutorial you'll learn the basics of an approval management system which will allow you to grant others access to transfer NFTs on your behalf. This is the backbone of all NFT marketplaces and allows for some complex yet beautiful scenarios to happen. If you're joining us for the first time, feel free to clone this repository and checkout the 4.core
branch to follow along.
git checkout 4.core
If you wish to see the finished code for this Approval tutorial, you can find it on the 5.approval
branch.
Introduction
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. As we've been doing in the previous tutorials, let's break down the problem into smaller, more digestible, tasks. Let's first define some of the end goals that we want to accomplish as per the approval management extension of the standard. We want a user to have the ability to:
- 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.
If you look at all these goals, they are all on a per token basis. This is a strong indication that you should change the Token
struct which keeps track of information for each token.
Allow an account to transfer your NFT
Let's start by trying to accomplish the first goal. How can you grant another account access to transfer an NFT on your behalf?
The simplest way that you can achieve this is to add a list of approved accounts to the Token
struct. When transferring the NFT, if the caller is not the owner, you could check if they're in the list.
Before transferring, you would need to clear the list of approved accounts since the new owner wouldn't expect the accounts approved by the original owner to still have access to transfer their new NFT.
The problem
On the surface, this would work, but if you start thinking about the edge cases, some problems arise. Often times when doing development, a common approach is to think about the easiest and most straightforward solution. Once you've figured it out, you can start to branch off and think about optimizations and edge cases.
Let's consider the following scenario. Benji has an NFT and gives two separate marketplaces access to transfer his token. By doing so, he's putting the NFT for sale (more about that in the marketplace integrations section). Let's say he put the NFT for sale for 1 NEAR on both markets. 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 then comes along and purchases the NFT on marketplace A for 1 NEAR. This would take the sale down from the marketplace A and clear the list of approved accounts. Marketplace B, however, still has the token listed for sale for 1 NEAR and has no way of knowing that the token was purchased on marketplace A by Josh. The new token struct would look as follows:
Token: {
owner_id: Josh
approved_accounts_ids: []
}