erc1820介绍

165主要是为了 检查某个合约地址是否实现了某些接口

可以为任何地址设置接口实现的的合约,必须调用ERC1820注册表的以下函数:

function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external

为接口设置实现地址

可以为任何地址设置接口实现的的合约,必须调用ERC1820注册表的以下函数:

function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external

设置实现某个地址上某个接口的合约地址。需要由管理员来设置。每个地址是他自己的管理员,查看下面管理员部分了解详情。

注意: 如果 _addr 和 _implementer 是两个不同的地址,则: - _implementer 必须实现 ERC1820ImplementerInterface 接口(下面详细介绍)。 - 在_implementer上用函数 _addr _interfaceHash 调用 canImplementInterfaceForAddress 时,必须返回 ERC1820_ACCEPT_MAGIC

注意_interfaceHash 不能是 ERC165 接口,即不能以28个字节0结尾。

注意_addr 可以为 0, 则假定为 msg.sender 。

此默认值通过multisig简化了交互,其中要签名的交易数据是常量,而不用管multisig实例的地址。

  • 接口id: 29965a1d
  • 参数:
    • _addr: 要关联的地址(如果为 0, 则假定为 msg.sender
    • _interfaceHash 实现接口的Keccak256 hash, 如 web3.utils.keccak256('ERC777TokensRecipient') 用于ERC777TokensRecipient接口
    • _implementer : 实现合约地址
    •  

获取地址接口的实现合约

任何人都可以使用 getInterfaceImplementer 函数查询ERC1820注册表以获取代表某个地址实现接口的合约的地址。

function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address)

查询地址是否实现了接口以及通过哪个合约实现。

注意: 如果_interfaceHash的最后28个字节是零(0),那么前4个字节被认为是ERC165接口,注册表合约应该将调用转发到 _addr 合约,检查它是否实现了 ERC165接口(_interfaceHash的前4个字节)。

注册表合约还应缓存ERC165查询以减少gas消耗。 任何人都可以调用 erc165UpdateCache 函数来更新合约是否实现了一个接口。

注意_addr 可以为 0, 则假定为 msg.sender。此缺省值与setInterfaceImplementer函数的行为一致,并通过multisigs简化了交互,其中要签名的交易数据是常量,而不管multisig实例的地址如何。

  • 接口id: aabbb8ca
  • 参数:
    • _addr: 要查询的地址(如果为 0, 则假定为 msg.sender
    • _interfaceHash 接口名的Keccak256 hash, 如 web3.utils.keccak256('ERC777Token')
  • 返回: 实现合约地址, 如果没有返回 0 地址。

接口实现 (ERC1820ImplementerInterface)

 

接口实现 (ERC1820ImplementerInterface)

interface ERC1820ImplementerInterface {
    /// @notice 指示合约是否为地址 “addr” 实现接口 “interfaceHash”。
    /// @param interfaceHash interfaceHash 接口名称的 keccak256 哈希值
    /// @param addr 为哪一个地址实现接口
    /// @return ERC1820_ACCEPT_MAGIC 只有当合约为地址'addr'实现'interfaceHash'时返回 ERC1820_ACCEPT_MAGIC
    function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
}

任何合约要被注册为某地址的接口实现必须实现上面的接口。 此外,如果它代表不同的地址实现接口,合约也必须实现上面的ERC1820ImplementerInterface

function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32)

示合约是否为地址 “addr” 实现接口 “interfaceHash”。

当合约为地址’addr’实现’interfaceHash’时必须返回 ERC1820_ACCEPT_MAGIC,如果没有实现一定不要返回ERC1820_ACCEPT_MAGIC

  • 接口id: f0083250
  • 参数:
    • _interfaceHash 接口hash
    • addr: 要实现接口的地址
  • 返回: 仅当实现了返回 ERC1820_ACCEPT_MAGIC。

特殊值ERC1820_ACCEPT_MAGIC被定义为字符串"ERC1820_ACCEPT_MAGIC"keccka256哈希。

bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));

返回 ERC1820_ACCEPT_MAGIC 而不是布尔值的原因是为了防止合约未实现canImplementInterfaceForAddress但实现了不抛出的回退函数。 在这种情况下,由于canImplementInterfaceForAddress不存在,所以调用了回退函数,而没有抛出的情况下执行回退函数并返回“1”。 会看起来好像canImplementInterfaceForAddress返回了 true

ERC1820标准定义了一个通用注册表合约,任何地址(合约或普通用户帐户)都可以注册它支持的接口以及哪个智能合约负责接口实现。

ERC1820标准定义智能合约和普通用户帐户可以向注册表发布其实现了哪些功能(普通用户帐户通过代理合约实现)

任何人都可以查询此注册表,询问哪个地址是否实现了给定的接口以及哪个智能合约处理实现逻辑。

ERC1820注册表合约可以部署在任何链上,并在所有链上的地址是相同的。

接口的后28个字节都为0的话,会认为是 ERC165 接口,并且注册表将转发到合约以查看是否实现了接口。

此合约还充当 ERC165 缓存,以减少 gas 消耗。

在以太坊上有很多方法定义伪自省,ERC165不能由普通用户帐户使用。 ERC672 则使用了反向 ENS,反向 ENS 有两个问题:增加了不必要的复杂度,其次,ENS 是由多签控制的中心化合约。 从理论上讲,这种多签能够修改系统。

ERC1820标准比 ERC672 简单得多,并且完全去中心化。

此标准还为所有链提供一个唯一(相同的)地址。从而解决了解决不同链的查找注册表地址的问题。

管理员

地址管理员(常规帐户或合约)是唯一允许注册地址接口实现的实体。 默认情况下,任何地址都是自己的管理员。

管理员可以通过在注册表合约上调用“setManager”将其角色转移到另一个地址。

setManager 函数

function setManager(address _addr, address _newManager) external

设置 _newManager 作为 _addr 的管理员。 新的管理员可以为 _addr 调用 setInterfaceImplementer 。

如果 _newManager 是 0x0, 则管理员重置为自身。

  • 接口id: 5df8122f
  • 参数:
    • _addr: 要设置的地址
    • _newManager: 新的管理员

getManager 函数

function getManager(address _addr) public view returns(address)

获取地址的管理员

  • 接口id: 3d584063
  • 参数:
    • _addr: 要查询的地址
  • 返回地址的管理员
pragma solidity 0.5.3;
// IV is value needed to have a vanity address starting with '0x1820'.
// IV: 53759

/// @dev 如果合约为其他的地址实现了接口, 则必须实现这个接口。
interface ERC1820ImplementerInterface {
    /// @notice 指示合约是否为地址 “addr” 实现接口 “interfaceHash”。
    /// @param interfaceHash 接口名称的 keccak256 哈希值
    /// @param addr 为哪一个地址实现接口
    /// @return 只有当合约为地址'addr'实现'interfaceHash'时返回 ERC1820_ACCEPT_MAGIC
    function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
}


/// @title ERC1820 伪自省注册表合约
/// @notice 该合约是ERC1820注册表的官方实现。
contract ERC1820Registry {
    /// @notice ERC165 无效 ID.
    bytes4 constant internal INVALID_ID = 0xffffffff;
    /// @notice ERC165 的 supportsInterface 接口ID (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
    bytes4 constant internal ERC165ID = 0x01ffc9a7;
    /// @notice 如果合约代表某个其他地址实现接口,则返回Magic值。
    bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));

    /// @notice 映射地址及接口到对应的实现合约地址
    mapping(address => mapping(bytes32 => address)) internal interfaces;
    /// @notice 映射地址到管理者
    mapping(address => address) internal managers;
    /// @notice 每个地址和erc165接口的flag,指示是否被缓存。
    mapping(address => mapping(bytes4 => bool)) internal erc165Cached;

    /// @notice 表示合约是'addr'的'interfaceHash'的'实现者'。
    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
    /// @notice 表示'newManager'是'addr'的新管理者的地址。
    event ManagerChanged(address indexed addr, address indexed newManager);

    /// @notice 查询地址是否实现了接口以及通过哪个合约实现的。
    /// @param _addr 查询地址(如果'_addr'是零地址,则假定为'msg.sender')。
    /// @param _interfaceHash 查询接口,它是接口名称字符串的 keccak256 哈希值
    /// 例如: 'web3.utils.keccak256("ERC777TokensRecipient")' 表示 'ERC777TokensRecipient' 接口.
    /// @return 返回实现者的地址,没有实现返回 ‘0’
    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
        address addr = _addr == address(0) ? msg.sender : _addr;
        if (isERC165Interface(_interfaceHash)) {
            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
        }
        return interfaces[addr][_interfaceHash];
    }

    /// @notice 设置某个地址的接口由哪个合约实现,需要由管理员来设置。(每个地址是他自己的管理员,直到设置了一个新的地址)。
    /// @param _addr 待设置的关联接口的地址(如果'_addr'是零地址,则假定为'msg.sender')
    /// @param _interfaceHash 接口,它是接口名称字符串的 keccak256 哈希值
    /// 例如: 'web3.utils.keccak256("ERC777TokensRecipient")' 表示 'ERC777TokensRecipient' 接口。
    /// @param _implementer 为地址'_addr'实现了 '_interfaceHash'接口的合约地址
    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
        address addr = _addr == address(0) ? msg.sender : _addr;
        require(getManager(addr) == msg.sender, "Not the manager");

        require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
        if (_implementer != address(0) && _implementer != msg.sender) {
            require(
                ERC1820ImplementerInterface(_implementer)
                    .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
                "Does not implement the interface"
            );
        }
        interfaces[addr][_interfaceHash] = _implementer;
        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
    }

    /// @notice 为地址_addr 设置新的管理员地址_newManager, 新的管理员能给'_addr' 调用 'setInterfaceImplementer' 设置是实现者。
    ///  (传 '0x0' 为地址_addr 重置管理员)

    function setManager(address _addr, address _newManager) external {
        require(getManager(_addr) == msg.sender, "Not the manager");
        managers[_addr] = _newManager == _addr ? address(0) : _newManager;
        emit ManagerChanged(_addr, _newManager);
    }

    /// @notice 获取地址 _addr的管理员
    function getManager(address _addr) public view returns(address) {
        // By default the manager of an address is the same address
        if (managers[_addr] == address(0)) {
            return _addr;
        } else {
            return managers[_addr];
        }
    }

    /// @notice 计算给定名称的接口的keccak256哈希值。
    function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
        return keccak256(abi.encodePacked(_interfaceName));
    }

    /* --- ERC165 相关方法 --- */

    /// @notice 更新合约是否实现了ERC165接口的缓存。
    function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
        interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
            _contract, _interfaceId) ? _contract : address(0);
        erc165Cached[_contract][_interfaceId] = true;
    }

    /// @notice 检查合约是否实现ERC165接口。
    //  如果未缓存结果,则对合约地址进行查找。 如果结果未缓存或缓存已过期,则必须通过使用合约地址调用“updateERC165Cache”手动更新缓存。
    /// @param _contract 要检查的合约地址。
    /// @param _interfaceId 要检查ERC165接口。
    /// @return True 如果合约实现了接口返回 true, 否则false.
    function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
        if (!erc165Cached[_contract][_interfaceId]) {
            return implementsERC165InterfaceNoCache(_contract, _interfaceId);
        }
        return interfaces[_contract][_interfaceId] == _contract;
    }

    /// @notice 在不使用或更新缓存的情况下检查合约是否实现ERC165接口。
    /// @param _contract 要检查的合约地址。
    /// @param _interfaceId 要检查ERC165接口。
    /// @return True 如果合约实现了接口返回 true, 否则false.
    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
        uint256 success;
        uint256 result;

        (success, result) = noThrowCall(_contract, ERC165ID);
        if (success == 0 || result == 0) {
            return false;
        }

        (success, result) = noThrowCall(_contract, INVALID_ID);
        if (success == 0 || result != 0) {
            return false;
        }

        (success, result) = noThrowCall(_contract, _interfaceId);
        if (success == 1 && result == 1) {
            return true;
        }
        return false;
    }

    /// @notice 检查_interfaceHash 是否是ERC165接口(以28个零结尾)。
    /// @param _interfaceHash 要检查接口 hash。
    /// @return  如果 '_interfaceHash'是ERC165接口返回 True, 否则返回false
    function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
        return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
    }

    /// @dev 调用合约接口,如果函数不存在也不抛出异常。
    function noThrowCall(address _contract, bytes4 _interfaceId)
        internal view returns (uint256 success, uint256 result)
    {
        bytes4 erc165ID = ERC165ID;

        assembly {
            let x := mload(0x40)               // Find empty storage location using "free memory pointer"
            mstore(x, erc165ID)                // Place signature at beginning of empty storage
            mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature

            success := staticcall(
                30000,                         // 30k gas
                _contract,                     // To addr
                x,                             // Inputs are stored at location x
                0x24,                          // Inputs are 36 (4 + 32) bytes long
                x,                             // Store output over input (saves space)
                0x20                           // Outputs are 32 bytes long
            )

            result := mload(x)                 // Load the result
        }
    }
}

 

 

 

 

1  设置账户管理员, 管理员可以设置接口实现 

function setManager(address account, address newManager) external;

2  或者账户管理员

function getManager(address account) external view returns (address);

3  account管理员才能调用这个接口,   

canImplementInterfaceForAddress
function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;

function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);

function interfaceHash(string calldata interfaceName) external pure returns (bytes32);

6

function updateBAC165Cache(address account, bytes4 interfaceId) external;

7

function implementsBAC165Interface(address account, bytes4 interfaceId) external view returns (bool);

function implementsBAC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ERC-721是以太坊生态系统中的一个应用级标准,用于创建和管理非同质化代币(NFT)。NFT是一种独特的数字资产,每个代币都有独特的属性和价值。ERC-721协议定义了一组规则和方法,使得开发者可以在智能合约内跟踪和转移NFT。\[1\] 要创建ERC-721代币,你可以按照一些教程和指南进行操作。例如,有一篇名为《如何创建和部署ERC-721(NFT)的详细解释 - 逐步指南 | QuickNode》的文章可以提供给你一些步骤和指导。\[2\] 需要注意的是,单纯的ERC-721协议只包含了代币的管理、持有和交易功能,并没有包括代币元数据的相关内容和一些实用的功能支持。因此,通常会结合使用ERC721标准、IERC721Enumerable和IERC721Metadata接口,并根据需要添加自定义的功能来创建更完整的NFT合约。\[3\] #### 引用[.reference_title] - *1* *2* [如何创建和部署ERC-721(NFT)?](https://blog.csdn.net/m0_59337285/article/details/120736435)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [ERC721关于NFT的学习和理解](https://blog.csdn.net/qq_39286701/article/details/121008757)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值