Interface standard ERC721

définition

La norme ERC721 comprend quatre interfaces :

  • Contrat principal ERC721
  • Peut accepter la norme du jeton ERC721
  • Deux interfaces extensibles

Conditions que l'ERC721 doit remplir

  • la possession

    Comment gérer la propriété des jetons

  • créer

    Comment créer des jetons

  • Transfert et autorisation

    Comment le jeton est transféré et comment permettre à d'autres adresses d'avoir la possibilité de transférer

  • détruire

    Comment détruire les jetons

Propriété du jeton

Pratique ERC20

Du point de vue de la propriété, la correspondance entre le solde du jeton et l'adresse de l'utilisateur est réalisée via un mappage

mapping(address => uint256) balances

Si l'utilisateur achète le jeton ERC20, la propriété finale du jeton par l'utilisateur peut être vérifiée par le biais du contrat, car lorsque l'utilisateur achète le jeton, il existe un enregistrement dans le contrat indiquant le nombre de jetons que chaque adresse possède.

Pratique ERC721

Comme ERC721 est indivisible, ERC721 ne peut pas être mappé à une adresse comme ERC20, mais doit connaître chaque jeton unique qu'il possède. Ainsi, dans ERC721, la propriété est déterminée par un tableau d'indices/ID d'un jeton mappé à une adresse. Étant donné que la valeur de chaque jeton est unique, au lieu de simplement regarder le solde du jeton, chaque jeton créé par le contrat doit être soigneusement vérifié. Le contrat principal doit conserver une liste de tous les jetons créés par le contrat. Chaque jeton a son propre numéro d'index, qui est défini dans le tableau allTokens du contrat.

uint256[] internal allTokens

Dans le même temps, afin de savoir quels jetons une adresse possède, il est nécessaire de mapper l'index et le nombre de jetons à une adresse.

mapping(address => uint256[]) internal ownedTokens

En même temps, afin de savoir à quelle adresse appartient le jeton, il est nécessaire de mapper le tokenId et l'adresse

mapping(uint256 => address) internal tokenOwner

Afin de répondre au besoin de supprimer un jeton de la liste des propriétaires d'une adresse, les informations doivent être suivies. Le mappage OwnTokensIndex mappe chaque ID de jeton à leur position/index correspondant dans le tableau propriétaire. En même temps, mappez l'ID de jeton au tableau global allTokens.

// 将TokenId和拥有者的tokenList的指数做一个映射
mapping (uint256 => uint256) internal ownedTokensIndex
//将TokenId映射到全局的allTokens数组上
mapping(uint256 => uint256) internal allTokensIndex

Dans le même temps, une variable est introduite pour suivre le nombre de jetons ERC721 qu'une adresse a

mapping(address => uint256 ) internal ownedTokenCount

Création de jeton

Pratique ERC20

Dans la norme ERC20, il existe une variable **totalSupply_ pour enregistrer l'offre de tous les jetons disponibles. Le constructeur est utilisé pour définir la valeur initiale de la variable, la propriété, etc. Dans le même temps, la fonction mint()** est introduite pour gérer la demande d'augmentation de l'émission de jetons. (TotalSupply_ doit être mis à jour dans la fonction mint)

Pratique ERC721

Pour ERC721, comme chaque jeton est unique, chaque jeton doit être créé manuellement. Il existe deux fonctions addTokenTo() et _mint() sur l'offre totale dans le contrat ERC721 .

  • Mettre à jour toutes les variables de propriété globales

    1. Appelez la fonction addTokenTo() dans le contrat

    2. Appelez d'abord la fonction addTokenTo() dans le contrat ERC721 de la classe de base via super.addTokenTo().

La fonction a deux paramètres : to ou l'adresse du compte qui possède le jeton et tokenId ou l'ID unique du jeton.

1. Tout d'abord, dans le contrat ERC721BasicToken, vérifiez que l'ID de jeton n'appartient pas au contrat

2. Définissez le propriétaire de l'ID de jeton demandé et mettez à jour le nombre de jetons détenus par ce compte

3. Ajoutez ce nouveau jeton à la fin du tableau OwnTokens et enregistrez l'index du nouveau jeton

4. Mettre à jour le tableau du propriétaire

//在ERC721Token.sol里调用本函数
function addTokenTo(address _to,uint256 _tokenId) internal {
	super.addTokenTo(_to,tokenId);
	uint256 length = ownedTokens[_to].length;
	ownedTokens[_to].push(_tokenId);
	ownedTokensIndex[_tokenId] = length;
}
//在ERC721TokenBasicToken.sol里调用本函数
function addTokenTo(address _to,uint256 _tokenId) internal {
	require(tokenOwner[_tokenId] == address(0));
	tokenOwner[_tokenOd]=_to;
	ownedTokenCount[_to]=ownedTokensCount[_to].add(1)
}

On peut voir que l'utilisation de la fonction addTokenTo() pour mettre à jour l'adresse d'un certain utilisateur. Ensuite, le tableau allTokens, la fonction mint() est utilisée pour traiter allTokens.

  1. La fonction mint() passe d'abord à l'implémentation du contrat de la classe de base pour s'assurer que l'adresse de frappe n'est pas 0
  2. Appelez ensuite addTokenTo() pour rappeler la fonction addTokenTo() dans le contrat dérivé.
  3. La fonction mint() dans le contrat de base est terminée et le tokenId est ajouté au mappage allTokenIndex et au tableau allTokens.
  4. Dans le contrat ERC721 dérivé, utilisez mint() pour créer un nouveau jeton
function _mint(address _to,uint256 _tokenId) internal{
	super._mint(_to,_tokenId);
	allTokensIndex[_tokenId]=allTokens.length;
	allTokens.push(_tokenId);
}
function _mint(address _to,uint256 _tokenId) internal{
	require(_to!=address(0));
	addTokenTo(_to,_tokenId);
	Transfer(address(0),_to,_tokenId);
}

Quel est le rôle des métadonnées dans ERC721 ? Le jeton et l'ID de jeton ont été créés, mais il n'y a pas encore de données. OpenZeppelin fournit un exemple de mappage d'un ID de jeton à une chaîne d'URL.

mapping (uint256 => string) internal tokenURLs;

Afin de définir les données URL d'un jeton, la fonction setTokenURL() est introduite ici

1. Obtenez d'abord les informations de tokenID et d'URL via la fonction mint (), puis vous pouvez définir les données

2. Mapper sur un tokenID dans Token

Remarque : Avant de définir les données, il faut confirmer qu'un ID de jeton existe

function _setTokenURL(uint256 _tokenId,string _uri) internal{
	require(exists(_tokenId));
	tokenURLs[_tokenId] = _uri;
}
function exist(uint256 _tokenId) public view returns(bool){
	address owner = tokenOwner[_tokenId];
	return owner != address(0);
}

Transmission et autorisation

Pratique ERC20

transmission

Dans ERC20, vous pouvez directement utiliser la fonction transfer() pour transférer le jeton ERC20. Dans la fonction transfer(), spécifiez d'abord une adresse à envoyer et le nombre de jetons, puis mettez à jour le contrat ERC20.

function transfer(address _to,uint256 _value) public returns(bool)
{
	require(_to!=address(0));
	require(_value<=balances[msg.sender]);
	balances[msg.sender] = balances[msg.sender].sub(_value);
	balances[_to] = balances[_to].add(_value);
	Transfer(msg.sender,_to,_value);
	return ture;
}

autorisé

Dans la norme ERC20, il existe une variable globale autorisée, indiquant que l'adresse d'un propriétaire est mappée à une adresse autorisée et est mappée au nombre de jetons en même temps. Pour définir cette variable, dans la fonction Approve(), vous pouvez mapper la subvention au dépensier et à la valeur souhaités.

//Global variable
mapping (address => mapping(address=>uint256)) internal allowed;
function approve(address _spender,uint256 _value) public returns (bool){
	allowed[msg.sender][_spender] = _value;
	Approval(msg.sender,_spender,_value);
	return true;
}

Une fois qu'une autre adresse est autorisée à transférer le jeton, le processus de transport spécifique est le suivant

1. Le dépensier autorisé utilise la fonction transferFrom(), où le paramètre from de la fonction représente l'adresse du propriétaire d'origine ; to représente l'adresse du destinataire ; value représente le nombre de jetons

2. Pour vérifier que le propriétaire d'origine possède bien le nombre de jetons demandé

require(_value<=balance[_from])

3. Vérifiez si msg.sender est autorisé à transmettre le jeton

4. Mettre à jour le nombre de soldes cartographiés et autorisés'

function transferFrom(address _from,address _to,uint256 _value) public returns (bool){
	require(_to != address(0));
	require(_value <= balances[_from]);
	require(_value <= allowed[_from][msg.sender]);
	balances[_from] =balances[_from].sub(_value);
	balances[_to] = balances[_to].add(_value);
	allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
	Transfer(_from,_to,_value);
	return true;
}

Pratique ERC721

autorisé

Pour la norme ERC721, la fonction Approve() est utilisée pour autoriser l'ID du jeton. tokenApprovals est une variable globale qui mappe un index ou un ID de jeton à une adresse autorisée à transférer des jetons.

Dans la fonction approuve()

1. Vérifiez d'abord si la propriété ou msg.sender isApprovedForAll()

2. Ensuite, vous pouvez utiliser la fonction setApprovalForAll() pour accorder une adresse pour transférer et traiter tous les jetons

  • Le jeton doit appartenir à une adresse spécifique
  • Dans la variable globale operatorApprovals, l'adresse du propriétaire est mappée à l'adresse d'un dépensier autorisé, puis une variable bool est mappée. Cette variable par défaut est false, mais l'utilisation de la fonction setApprovalForAll() peut définir ce mappage sur true et autoriser L'adresse pour gérer tous les ERC721 détenus
mapping (uint256 => address) internal tokenApprovals;
mapping (address => mapping(address => bool)) internal operatorApprovals;
function approve(address _to,uint256 _tokenId) public {
	address owner = ownerOf(_tokenId);
	require(_to != owner);
	require(msg.sender == owner || isApprovedForAll(owner,msg.sender));
	if(getApproved(_tokenId) != address(0) || _to != address(0) ){
		tokenApprovals[_tokenId] = _to;
		Approval(_owner,_to,_tokenId);
	}
}
function isApprovedForAll(address _owner,address _operator) public view returns (bool){
	return operatorApprovals[_owner][_operator];
}
function getApproved(uint256 _tokenId) public view returns (address){
	return tokenApprovals[_tokenId];
}
function setApprovalForAll(address _to,bool _approved) public {
	require(_to != msg.sender);
	operatorApprovals[msg.sender][_to] = _approved;
	ApprovalForAll(msg.sender,_to,_approved);
}

transmission

Dans une implémentation complète, il existe deux méthodes

  • Dans la fonction transferFrom(), l'adresse de l'expéditeur et du destinataire et le tokenID du transfert sont définis, et un modificateur canTransfer() est utilisé pour s'assurer que msg.sender est autorisé ou est le propriétaire du jeton.

    1. Après avoir confirmé que les adresses de l'expéditeur et du destinataire sont légales, la fonction clearApproval() est utilisée pour supprimer l'autorisation du propriétaire d'origine du jeton, c'est-à-dire que le propriétaire d'origine n'a plus l'autorité d'autorisation, de sorte que le autorisation précédente Le dépensier n'est plus en mesure de transférer des jetons.

    2. Plus tard, dans l'implémentation complète du contrat ERC721, la fonction removeTokenFrom() est appelée, ce qui est similaire à l'implémentation du contrat de classe de base d'ERC721, et la fonction addTokenTo() est appelée pour appeler la fonction removeTokenFrom() dans le contrat de classe de base, comme vous pouvez le voir, le jeton spécifié est supprimé du mappage OwnTokensCount, du mappage tokenOwner.

    3. De plus, le dernier jeton du tableau OwnToken du propriétaire doit être déplacé vers la position d'index du jeton transféré, et la longueur du tableau doit être réduite d'un

    4. Enfin, utilisez la fonction addTokenTo() pour ajouter l'index/id du jeton au nouveau propriétaire.

modifier canTransfer (uint256 _tokenId){
	require(isApprovedOrOwner(msg.sender,_tokenId));
	_;
}
function isApprovedOrOwner(address _spender,uint256 _tokenId) internal view returns(bool){
	address owner = ownerOf(_tokenId);
	return _spender == owner || getApprived(_tokenId) == _spender ||isApprovedForAll(owner,_spender);
}
function transferFrom(address _from,address _to,uint256 _tokenId) public canTransfer(_tokenId){
	require(_from != address(0));
	require(_to !=address(0));
	clearApproval(_from,_tokenId);
	removeTokenFrom(_from,_tokenId);
	addTokenTo(_to,_tokenId);
	Transfer(_from,_to,_tokenId);
}
function clearApproval(address _owner,uint256 _tokenId) internal {
	super.removeTokenFrom(_from,_tokenId);
	uint256 tokenIndex = ownedTokensIndex[_tokenId];
	uint256 lastTokenIndex = ownedTokensIndex[_from].length.sub(1);
	uint256 lastToken = ownedTokens[_from][lastTokenIndex];
	ownedTokens[_from][tokenIndex] = lastToken;
	ownedTokens[_from][lastTokenIndex] = 0;
	owned[_from].length--;
	ownedTokensIndex[_tokenId]=0;
	ownedTokensIndex[lastToken] = tokenIndex;
}
function removeTokenFrom(address _from,uint256 _tokenId) internal{
	require(ownerOf(_tokenId)==_from);
	ownedTokenCount[_from]=ownedTokenCount[_from].sub(1);
	tokenOwner[_tokenId]=address(0)
}

Les comptes détenus en externe peuvent utiliser le contrat ERC721 complet pour échanger des jetons ; mais si le jeton est envoyé à un contrat qui n'a pas pour fonction d'échanger et de transférer le jeton via le contrat ERC721 d'origine, le jeton sera perdu et il n'y aura aucun moyen de le récupérer il. En réponse à ce problème, ERC223 a été proposé, et ERC223 a été amélioré sur la base de ERC20 pour empêcher une telle transmission erronée.

Afin de résoudre les problèmes ci-dessus, l'implémentation complète de la norme ERC721 introduit une fonction safeTransferFrom().

Avant de discuter de cette fonction, examinez d'abord certaines exigences dans ERC721Holder.sol qui implémente l'interface ERC721Receiver.sol.

ERC721Holder.sol fait partie du portefeuille et également d'une vente aux enchères ou d'un contrat de courtage.

L'objectif d'EIP165 est de créer une norme pour publier et découvrir quelles interfaces un contrat intelligent implémente. Alors comment découvrir une interface ?

Une valeur magique ERC721_RECEIVED est utilisée ici, qui est la signature de la fonction onERCReceived(). Une signature de fonction correspond aux 4 premiers octets de la chaîne de signature standard. Dans ce cas, il peut être calculé par bytes(keccak256("onERC721Received(address,uint256,bytes)")).

Utilisez la signature de la fonction pour vérifier si elle est utilisée dans le bytecode du contrat, afin de déterminer si la fonction est appelée.

Chaque fonction du contrat a une signature unique, et lors de l'appel du contrat, EVM utilise une série d'instructions switch/case pour trouver la signature de la fonction, trouve la fonction correspondante via la signature de la fonction, puis exécute la fonction correspondante.

Par conséquent, dans le contrat ERCHolder, seules la fonction onERCReceived() et la signature de fonction peuvent être trouvées pour correspondre à la variable ERC721_RECEIVED dans l'interface ERC721Received.

contract ERC721Receiver{
	/**
	*@dev 如果接收到NFT则返回魔术值,魔术值等于
	*‘bytes(keccak256("onERC721Received(address,uint256,bytes)"))’
	*也可以通过‘ERC721Receiver(0).onERC721Received.selector’获取
	*/
	bytes constant ERC721_RECEIVED = 0xf0b9e5ba;
	/**
    *@notice处理函数当收到一个
    *@devERC721合约在'safetransfer'后调用这个函数在收到NFT的时候
    *这个函数可能抛出异常,导致回退以及拒绝Transfer
    *这个函数可能使用20000GAS。如果返回的不是魔术值,则必须回退
    *Note:合约地址时msg.sender
    *@para_from 发送地址
    *@para_tokenId 被传送的NFT ID
    *@para_data 额外数据,没有指定的数据格式
    *@retutn 'bytes4(keccak("onERC721Received(address,uint256,bytes)"))'
    */
    function onERC721Received(address _from,uint256 _tokenId,bytes _data) public retyrbs(bytes4);
}
contract ERC721Holder is ERC721Received{
	function onERC721Received(address,uint256,bytes) public retyrbs(bytes4){
		return ERC721_RECEIVED;
	}
}

Désormais, le contrat ERC721Holder n'est pas un contrat complet pour la gestion des jetons ERC721. Ce module est utilisé pour fournir une méthode standard pour vérifier si l'interface standard ERC721Receive est utilisée. Il est nécessaire d'hériter ou de dériver le contrat ERC721Holder pour appeler le code dans le portefeuille ou le contrat d'enchères pour traiter le jeton ERC721. Même pour les jetons d'entiercement, une telle fonctionnalité est nécessaire pour appeler des fonctions de contrat afin de transférer des jetons hors du contrat en cas de besoin.

//option 1
function safeTransferFrom(address _from,address _to,uint256 _tokenId) public canTransfer(_tokenId){
	safeTransferFrom(_from,_to,_tokenId,"");
}
//option 2
function safeTransferFrom(address _from,address _to,uint256 _tokenId bytes_data) public canTransfer(_tokenId){
    transferFrom(_from,_to,_tokenId);
    require(checkAndCallSafeTransfer(_from,_to,_tokenId,_data))
}
function checkAndCallSafeTransfer(address _from,address _to,uint256 _tokenId,bytes _data) internal returns (bool){
	if(!_to.isContract()){
		return true;
	}
	bytes4 retval = ERC721Received(_to).onERC721Received(_from,_tokenId,_data);
	return (retval==ERC721_RECEIVED);
}
function isContract(address addr) internal view returns(bool){
	uint256 size;
	assembly {size:=extcodesize(addr)}
	return size>0;
}

Le principe de fonctionnement de la fonction safeTransferFrom() est décrit ci-dessous. Vous pouvez choisir l'option 1 pour transférer le jeton, dans ce cas, l'appel de la fonction safeTransferFrom() ne nécessite aucun paramètre ; vous pouvez également choisir l'option 2, en utilisant le paramètre bytes_data. De même, la fonction transferFrom() est utilisée pour transférer la propriété du jeton de l'adresse d'expédition à l'adresse de destination. En même temps, la fonction checkAndCallSafeTransfer() est appelée et l'adresse de destination est d'abord vérifiée si l'adresse de destination est une adresse de contrat via le package de bibliothèque AddressUtils.sol. Le processus d'implémentation peut être compris via la fonction isContract(). Après avoir confirmé s'il s'agit d'une adresse de contrat, vérifiez si la signature de fonction de onERC721Received() est conforme à l'interface standard de l'interface attendue. S'il ne correspond pas, la fonction transferFrom() sera révoquée car il est déterminé que le contrat à l'adresse to n'implémente pas l'interface attendue.

détruire

Pratique ERC20

Pour la norme ERC20, du fait qu'elle n'opère qu'un solde mappé, il lui suffit de détruire le jeton d'une adresse spécifique. L'adresse peut être une adresse d'utilisateur ou de contrat. Dans la fonction burn() ci-dessous, la variable value est utilisée pour spécifier le nombre de jetons qui seront détruits. Le propriétaire du jeton à détruire est spécifié par msg.sender, il est donc nécessaire de mettre à jour son solde d'adresses, puis de réduire l'offre totale de jeton totalSupply. Ici Burn et Transfer sont des événements

function burn (uint256 _value) public{
	require(_value <= balances[msg.sender]);
	address burner = msg.sender;
	balances[burner] = balances[burner].sub(_value);
	totalSupply_ = totalSupply_.sub(_value);
	Burn(burner,_value);
	Transfer(burner,address(0),_value);
}

Pratique ERC721

Pour les jetons ERC721, il est nécessaire de s'assurer que l'ID ou l'index de jeton spécifique est supprimé. Semblable aux fonctions addTokenTo() et mint(), la fonction burn() utilise super pour appeler l'implémentation ERC721 de base.

1. La fonction clearApproval() n'est pas appelée en premier

2. Supprimez ensuite la propriété du jeton via removeTokenFrom () et déclenchez l'événement Transfer pour notifier le frontal

3. Supprimez les métadonnées associées au jeton

4. Enfin, tout comme pour la suppression de la propriété du jeton, réorganisez le tableau allTokens et remplacez la position d'index tokenId par le dernier jeton du tableau

function burn(address _owner,uint256 _tokenId)internal{
	super._burn(_owner,_tokenId);
	//清除metadata(if any)
	if(bytes(tokenURLs[_tokenId]).length !=0){
		delete tokenURLs[_tokenId];
	}
	//重排所有Token的数组
	uint256 tokenIndex = allTokenIndex[_tokenId];
	uint256 lastTokenIndex = allTokens.length.sub(1);
	uint256 lastToken = allTokens[lastTokenIndex];
	allTokens[tokenIndex] = lastToken;
	allTokens[lastTokenIndex]=0;
	allTokens.length--;
	allTokensIndex[_tokenId]=0;
}
function burn(address _owner,uint256 _tokenId) internal{
	clearApproval(_owner,_tokenId);
	removeTokenFrom(_owner,_tokenId);
	Transfer(_owner,address(0),_tokenId);
}

interface portefeuille

Les applications de portefeuille doivent implémenter l'interface de portefeuille. Un ERC721TokenReceiver légal doit implémenter la fonction :

function onERC721Received(address operator,address from,uint256 tokenId,bytes data) external returns(bytes);

et renvoie :

bytes(keccak256("onERC721Received(address,address,uint256,bytes)"))

Un récepteur illégal n'est pas la première fonction ou renvoie autre chose, ce qui suit est un retour légal :

contract ValidReceiver is ERC721TokenReceiver{
	function onERC721 Receiver(address operator,address from,uint256 tokenId,bytes data) external returns(bytes){
		return bytes(keccak256("onERC721Receiver(address,address,uint256,bytes)"));
	}
}

L'exemple suivant est un retour illégal

contract InvalidReceiver is ERC721TokenReceiver{
	function onERC721Receiver(address operator,address from,uint256 tokenId,bytes data) external returns(bytes){
		return bytes(keccak256("some invalid return data"));
	}
}

extension de métadonnées

Les extensions de métadonnées donnent au contrat de jeton un nom et un code (comme un jeton ERC20) et donnent à chaque jeton des données supplémentaires qui rendent le jeton unique. Les extensions énumérables facilitent le tri des jetons, pas seulement par tokenID. L'extension de métadonnées est facultative et l'interface de métadonnées permet aux contrats intelligents d'obtenir des métadonnées sur des jetons inséparables, tels que des noms et d'autres détails.

Le contrat déclaré ici est une interface héritée du contrat TokenERC721.sol et de l'extension ERC721Metadata.

contract TokenERC721Metadata is TokenERC721,ERC721Metadata{

L'extension Metadata comprend les trois fonctions suivantes :

function name() external view returns (string _name);
function symbol() external view returns (string _symbol);
function tokenURL(uint256 _tokenId) external view returns (string);

Constructeur

constructor(uint _initialSupply,string _name,string _symbol,string _uriBase)
public TokenERC721(_initialSupply){
	_name=_name;
	_symbol=_symbol;
	_uriBase=bytes(_uriBase);
	
	//Add to ERC165 Interface Check
	supportedInterface[
		this.name.selector ^
		this.symbol.selector ^
		this.tokenURL.selector ^
	]=true;
}
function name() external view returns(string _name){
	_name=_name;
}
function symbol() external view returns(string _symbol){
	_symbol=_symbol;
}
function tokenURL(uint256 _tokenId) external view returns (string){
	require(isValidToken(_tokenId));
	uint maxLength=78;
	bytes memory reversed = new bytes(maxLength);
	uint i=0;
	//循环并且将字节加入数组
	while(_tokenId!=0){
		uint remainder = _tokenId%10;
		_tokenId/=10;
		reversed[i++]=bytes(48 + remainder);
	}
	//分配生成最终数组
	bytes memory s = new bytes(_uriBase.length + i);
	uint j;
	for(j=0;j<_uriBase.length;j++){
		s[j]=_uriBase[j];
	}
	//将tokenId加入最后的数组
	for(j=0;j<i;j++){
		s[j+_uriBase.length] = reversed[i-1-j];
	}
	return string(s);
}

extension énumérable

  • Définition des interfaces
  • exemple
  • approvisionnement total
  • tokenByIndex
  • tokenOfOwnerByIndex
  • transfert à partir de
  • burnToken
  • issueTokens

Norme ERC165

ERC165 n'a qu'une seule fonction, qui est utilisée pour vérifier si l'empreinte du contrat correspond à l'empreinte de l'interface spécifiée

interface ERC165{
	///@notice 查询一个合约是否实现了某个接口
	///@param interfaceID ERC165标准里指定的接口ID
	///@dev 接口定义在ERC165标准中,这个函数使用的燃料费少于30000Gas
	///@return 如果合约实现了指定的接口,则返回‘true’
	///并且"interfaceID"不是0xffffffff,否则返回"false"
	function supportInterface(bytes interfaceID) external view returns (bool);
}

Pour réaliser ERC721, ERC165 doit être réalisé

Je suppose que tu aimes

Origine blog.csdn.net/weixin_45976751/article/details/126246500
conseillé
Classement