In the first article in this series, I introduced the concept of a non-fake token (NFT) and the need for the ERC721 (draft) standard. In this article, we will first introduce the ERC721 standard interface and decompose some requirements. There will also be a brief but important detour on the ERC165 standard. "Todd Quackenbush's display of a toolkit on Unsplash shows tool interfaces and ERC165 standards such as hammers, axes, box cutters, iron and flashlights.
The ERC721 standard states: "Every contract conforming to ERC-721 must implement the ERC721 and ERC165 interface"
If you never need to write a solidity protocol and work with a contract written by another developer, you might want to know what an interface is. ”。 Whether or not you want to know, "What is the ERC621 interface." ”。 So let's answer these two questions. Interface
An interface is basically an abstract contract, but the only thing you can define is a function that is not implemented. This is a summary written in solidity code that ensures that contracts written by other developers work well together without having to know each other's code base.
For example, if an interface defines a function balanceof as
So you know that any contract that implements the interface will have a function called balanceof, which takes a parameter (address) and returns a value (UINT256). You also know that the variability of this function is view, which means that the function can read the contract state, but not modify it, and it has an external modifier, which has an effect on the gas consumption and how the function should be invoked. If the modifier or return value of your contract function does not match the value defined in the interface, it causes the compiler to give typeerrors and cannot compile.
So, with just one interface, you can tell other developers about some of the features of your contract and how to use them. Simple.
But this raises the question of not looking at your contract code and how other developers know you've used the given interface. The whole point of this interface is that we don't have to know each other's code base. The answer is: ERC165 standard. ERC165 Standard "What ... Another ERC standard. How deep the rabbit hole is. “
Don't worry, this is the only one we're going to discuss, and it's very simple-it has only one feature. The ERC165 standard is just a way to check if your contract fingerprint matches the fingerprint of any given interface. Let's take a look at the entire ERC165 standard interface:
Interface ERC165 {
///@notice query whether the contract implements an interface
///@param InterfaceID interface identifier, such as///
specify ERC-165 in///
@dev interface identifier is specified in
///ERC-165. This feature uses less than 30,000 gases.
///@ If the contract implements ' InterfaceID ', @return ' true '
///and ' InterfaceID ' is not 0xffffffff, otherwise ' false '
function Supportsinterface (bytes4 interfaceid) external view Returns (bool);
Therefore, your contract must have a function supportsinterface that accepts an argument that represents InterfaceID (BYTES4), and returns True (BOOL) if the interface is supported. Where InterfaceID is defined in the ERC165 standard as "XOR or all feature selectors in the interface."
Or in Simple English, you can give this feature any interface to the fingerprint (InterfaceID), and it will tell you whether it matches any of your contract's fingers.
As for getting a function selector, there are two simple ways to do it. As an example, I'll use the previous balanceof function. To save you from scrolling, it is defined as:
function balanceof (address _owner) external view Returns (UINT256) {
//...
Our final ERC721 contract has implemented this feature, which is why I added curly braces, but it doesn't matter when it comes to the function selector-you'll see why. Two ways to get a function selector for the above function are:
or manual use
Both will return to 0x70a08231, although the first one looks cleaner, but we occasionally need a second if the interface uses overloaded functions. You will notice that the function selector does not care about the parameter name, modifier, variability, return value, or the contents of the function. Just the function name and parameter type. That's why it doesn't matter if I say this feature is implemented.
But we need the "XOR of all function selectors in the interface" in InterfaceID. Let's assume that an interface consists of three functions: Function1 (), function2 (), and Function3 ().
InterfaceID just:
Very simple. Now, remember, the reason we started the ERC165 discussion is because our ERC721 protocol needs to implement the ERC165 interface, so let's write our ERC165 implementation (the ERC721 protocol will inherit it) to wrap it.
When it comes to solidity, we want to reduce the use of gas. Making unnecessary calculations can cost users money and waste network resources. The ERC165 standard actually requires the Supportsinterface function to "use less than 30,000 gases". So every time someone calls Supportsinterface, do not recalculate InterfaceID, but let the interface ID that we support be saved in the map.
Start our contract, CheckERC165, like this:
Contract CheckERC165 is ERC165 {
In this way, the Supportsinterface function only needs to return a value from the map, and the following is the implementation of the entire function:
function Supportsinterface (bytes4 interfaceid) external view Returns (BOOL) {
returns supportedinterfaces [InterfaceID];
Big. Our contract now implements all of the functions (only one) in the ERC165 interface. Let's add ERC165 InterfaceID to the supportedinterfaces and bring this thing to a full circle.
The solidity Version 0.4.22 was recently released, providing a simpler constructor function name for our constructor function. So let's construct a constructor and add the InterfaceID of the ERC165 interface to it.
Constructor () public {
supportedinterfaces [this.supportsInterface.selector] = true;
Now, if someone uses the InterfaceID of the ERC165 Standard Interface (0X01FFC9A7) to invoke Supportsinterface, it returns true.
This is ERC165. The complete contract can be found on my github. We'll use the ERC721 implementation later, and it's also a handy implementation when dealing with generic interfaces. ERC721 interface
Now that we know the interface, let's go back to ERC721. You will notice that the ERC721 standard actually contains four different interfaces. A major part of a general ERC721 contract, one for contracts that can receive ERC721 tokens, and two for optional extensions that add additional functionality. We'll now ignore the extensions and quickly look at the main interface and the receiver. Coping functions and variability
The immediate highlight for me is that the four features in the ERC721 interface have due modifiers. That is, two safetransferfrom functions, Transferfrom and approve. Every time a transfer or grant of a token is controlled, it is not meaningful for the ERC721 contract to always be paid.
You can imagine that this may be the case-if you set up a market for NFT, then there is no doubt that you will deal with the payment-but it is hard to imagine that the token owner must pay for the control of the token. In fact, the reason for dealing with modifiers is not very sexy, and all of this boils down to variability.
From the ERC721 Standard considerations section: "Variability guarantees are weak to strong: payable, implied non-payment, view and pure." ”
Thus, by marking these functions as payable, this is merely an argument by the author that there is no limit to the variability required. In this particular case, payable is just a way of clearly expressing no limits. Your implementation may be more stringent, but not weaker.
There are two ways to solve this problem, you can: keep these functions as chargeable, simply return any ether sent with the transaction (or keep it, if this is your design), or remove the due modifier, but you must perform the same action in the ERC721 interface copy. To avoid triggering typeerror.
Remember from the front that the function selector is not affected by modifiers, so this does not interfere with InterfaceID. The same is true for the external feature, which you should change to public whenever you want. Erc721tokenreceiver
Before I finish, I want to quickly introduce the Erc721tokenreceiver interface. This is not something we need to inherit from our token contract. As the name suggests, it is a contract interface that can receive ERC721 tokens.
Since I'm not going to introduce a wallet contract in this series, I can make some virtual wallets quickly and we'll use those virtual wallets for testing later. A valid receiver and an invalid receiver.
For our purposes, the only important information is valid Erc721tokenreceiver will implement this functionality
and return
An invalid will not execute the function, or literally return any other content. So let's define our two receivers as follows:
Contract Validreceiver is erc721tokenreceiver {
function onerc721received (address _from,uint256 _tokenid, byte data) external return (BYTES4) {
Return to Bytes4 (keccak256 ("onerc721received (address,uint256,bytes)"));
} Bytes4 (keccak256 ("onerc721received (address,uint256,bytes)"));
}
And
Contract Invalidreceiver is erc721tokenreceiver {
function onerc721received (address _from,uint256 _tokenid, byte data) external return (BYTES4) {
Returns BYTES4 (keccak256 ("Some Invalid return data");
} Bytes4 (keccak256 ("Some Invalid return data");
}
We won't be using these tests long after, so please save them and put them in your pockets. I just want to introduce the interface as we outline it. Wrap it up.
So after all, you should be familiar with interfaces and have a ERC165 implementation that you can use to indicate which interfaces your contract implements. We also introduced the variability assurance in the ERC721 standard, and soon wrote some virtual wallet contracts for future testing.
In the next article, we'll use all the content we've described so far and start writing the actual ERC721 token contract.
Https://medium.com/coinmonks/jumping-into-solidity-the-erc721-standard-part-2-383438734de5