之前学习了下ERC165标准,理解ERC165标准,发现ERC标准是一环扣一环,怪是越打越多的。希望以本文记录一下ERC1820的一些细节和主要实现,不会具体分析合约方法。
【ERC1820总览】
下面要介绍的ERC1820是以太坊区块链世界中的”中心化注册表”,任何人可以通过它查询哪些合约或EOA地址是否支持哪些接口,以更加确定性的方式去交互。
当然你也可以为一个地址注册接口,使别人也可以随时查询到你的合约或EOA地址是否支持指定的接口。
.
ERC1820标准的功能主要为查询、和注册。
查询
查询一个指定的地址(可以是EOA,也可以是合约),是否实现了指定的接口(这里兼容ERC165接口)。
.
注册
注册可以达到三个效果,你可以选择如何注册。
- 为一个合约地址注册指定的已经实现的接口
- 为一个合约地址注册指定的已经实现的接口,但是可以注册为代理实现者(一个外部合约实现这个接口)。
- 为一个EOA地址,注册一个接口,并指定代理实现合约地址。
.
通过不同的身份理解ERC1820
ERC1820有三个身份,分别是 目标地址(target)、管理者(manager)、实现者(implementer),这三个身份分别有不同的作用。
目标地址(target)
1.默认要为哪个地址查询(或实现)接口
2.查询接口也是通过这个地址 + 要查的接口Hash 来进行查询
管理者(manager)
1.只有manager才能改变一个地址的接口
2. 一个目标地址的默认manager是这个目标地址本身
3.manager可以设置修改一个新的manager
实现者(implementer)
1.默认是目标地址自身
2.当为EOA地址注册接口,或希望为合约代理实现接口,此时implementer就是那个代理实现合约。
3.在通过erc1820查询接口是否注册时,返回的就是implementer地址。
了解了这三个身份,就可以很好的理解ERC1820的设计。
【主要方法理解】
下面列出ERC1820的主要方法,并了解如何使用
//【作用:查询一个目标地址是否实现了某个接口】
//1.未注册返回0地址,注册则返回 实现者(implementer)地址
//2.接口Hash(_interfaceHash)的计算方法
//keccak256(abi.encodePacked(_interfaceName));
//3.这里参数_interfaceName 就是接口的字符串,比如"IERC20Token"
//4._interfaceHash也可以兼容传入erc165接口ID,但需要填充满bytes32(erc165接口ID是bytes4)
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address);
// 【作用:设置接口实现者】
//1._addr必须是msg.sender,所以这就意味着EOA地址的接口注册也需要这个EOA来完成。
//2.若是为自己实现接口,默认_addr和_implementer是同一个地址
//3.ERC1820中只能兼容查ERC165接口,但不能为ERC165注册。 所以_interfaceHash只能是ERC1820接口。
//4.为了方便多签,若把_addr传入0地址,则默认会把_addr设为msg.sender。这样多签可以复用同样的交易数据。
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external;
// 【作用:设置地址的管理者】
//_addr必须为原manager,默认是目标地址
function setManager(address _addr, address _newManager) external;
// 【作用:获取地址的管理者】
function getManager(address _addr) external view returns(address);
//【 作用:计算接口哈希值】
//此方法是ERC1820标准为了方便人们算接口Hash提供的,其实也只是keccak256 + encodePacked 接口名称。
function interfaceHash(string calldata _interfaceName) external pure returns(bytes32);
// 【作用:更新 ERC165 缓存】
//此方法必须手动调用更新缓存,ERC1820不会主动更新缓存。
//如果你想通过ERC1820储存你项目的ERC165接口,那么当你每次接口改变,最好同时也调用此方法更新缓存记录。
function updateERC165Cache(address _contract, bytes4 _interfaceId) external;
// 检查合约是否实现了 ERC165 接口
function implementsERC165Interface(address _contract, bytes4 _interfaceId) external view returns (bool);
// 当没有ERC165缓存时,以staticall方式去目标合约查。
function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) external view returns (bool);
ERC165和1820查询实现不同细节对比】
ERC1820的实现中有很多值得学习和需要细心发现的细节。
- ERC165和ERC1820接口查询区别
- ERC165中使用bytes4查询
- ERC1820中使用Bytes32查询
-即使兼容ERC165查询,也需要用bytes32查
.
- ERC165通过返回的bool,而ERC1820则通过返回的”地址”判断接口是否存在
- 1.ERC1820中对于代理实现的合约需要返回 ERC1820_ACCEPT_MAGIC 标识符,这是因为要防止代理合约没有实现canImplementInterfaceForAddress,但却因为实现了带bool返回值的fallback方法而出现误判。
- 2.所以从这个角度来看,ERC165标准要发送两次staticcall(一次预期返回false,一次要查询的接口)来确认一个ERC1665接口是否存在其实是必要的。
【部署】
部署原理
对于ERC1820的部署,其实需要先简单理解一下交易签名。
- 1.以太坊区块链上所有的write操作(包括write合约方法和交易),无论通过什么方式发送,本质上都是打包你交易中的nonce、from、to、data、gas、r、v、s等信息。然后使用ECDSA签名,得到一个交易字符串。
- 2.ERC1820官方提案中就是提供了这样一个用于部署的交易字符串
为了实现任何人可以在任何链上,都能够部署得到同样的ERC1820合约地址,达到统一的效果,采用了一种无需私钥的部署方式。
知识点:
- 以太坊交易签名的时候,from地址由于可控,所以是不被包含在签名里面的。
- .最终上链的交易中的from地址,是节点通过签名恢复出的from地址。但你可以自定义签名(比如r、v、s),其中很多签名都可以恢复出一个对应的随机地址。
3.所以ERC1820的合约部署的r、v、s是自定义的
如下:ERC1820的签名r、v、s数据
v: 27,
r: 0x1820182018201820182018201820182018201820182018201820182018201820'
s: 0x1820182018201820182018201820182018201820182018201820182018201820'
这里有一些细节
- 1为了实现在所有链部署都得到同样的地址,所以不能使用EIP155的标准。
- EIP155比默认情况下多一个链ID,会导致同样的交易数据在不同的链部署后地址不同。
- .这种方式恢复出的地址,是一次性的,部署后的from会是”0xa990077c3205cbDf861e17Fa532eeB069cE9fF96″
- 但之后由于相同的nonce已经使用,无法再恢复出这个地址。
- 部署后的ERC1820合约地址,在任何链上都会是:0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
- 这个交易中默认给了比较高的gas price 100 gwei,也是为了在链上gas波动情况下,增加保障。
部署步骤
上面是分析了一下部署原理,下面说一下步骤。
由于部署后的from是0xa990077c3205cbDf861e17Fa532eeB069cE9fF96
- 1.所以部署前需要先转入0.08ETH到这个地址。
- 2.使用交易发送方法如ethers.js的 sendTransaction() 方法,发送提案中提供的原始部署交易。
【思考与总结】
关于ERC1820可以为EOA地址注册接口实现的思考
理想来说,作为开发者或项目方,你可以为一个指定的用户(EOA)地址,实现一些特定的逻辑。
比如当用户收到代币、就代他进行投票、或存款之类的操作。 如ERC777就默认实现了转账接收钩子(tokensReceived)。
但这个为EOA地址实现接口功能,2点原因导致使用受限;
- 1.需要是标准场景下的操作
- 比如ERC777的默认转账回调,在用户收到代币的情况下,才会执行。
- 缺少场景丰富又同时有共识的标准。
- 2.或者是自身项目内部生态
- 如果想为指定EOA用户实现指定的接口功能实现,那你可能要自己定义接口,又要为用户去注册和实现。这种情况也许只在自己项目生态中比较好实现了。
另外还有一个不算友好的地方,ERC1820中只能由msg.sender来为自己注册接口,所以这个EOA用户也只能自己去调用方法,对用户的要求比较"高"。
【总结】
最近抽空学习ERC1820的官方提案,断断续续用了大概一周多的时间。
ERC1820的合约实现的也有很多很巧妙的地方,很值得去学习。这过程中涉及到一些相关的内容、比如以太坊的签名与交易、无秘钥部署、EIP155、ERC165等..
感觉想把ERC1820通过一篇文章写的非常清晰,是挺不容易的,只能算一些笔,还是感觉有不少收获的。
另外看到以太坊主网的ERC1820合约部署五年多,也没几个人调用注册,真是可惜了这么好的合约。
评论前必须登录!
注册