本文介绍了以太坊中的 ERC777 token 标准。ERC777 试图改进 ERC20 的一些缺点,例如增加了 hooks 和 operators 的概念,并利用 ERC1820 注册合约来实现更灵活的功能。尽管 ERC777 在设计上有所创新,但由于其复杂性和潜在的安全隐患,未能获得广泛应用,最终被认为设计过度。
我来了,带来了另一个 ERC token 标准。之前我们讨论了ERC20,ERC721,和ERC1155。现在轮到 ERC777 了。
如果你还不知道什么是 ERC 以及为什么我们在以太坊生态系统中需要它们,请查看我之前关于这个主题的文章这里。
尽管 ERC777 被认为是官方标准,但它未能引起关注和采用。原因有很多,我们将在本文中讨论所有这些原因。但在讨论失败的原因之前,我们需要完整地理解 ERC。
可互换 Token 简史
正如我们已经知道的,ERC20 是第一个被提出的 token 标准,它的功能非常有限,这很明显,因为那是以太坊生态系统的最早阶段。
你可能已经知道的一个主要缺点是,将 token 发送到一个智能合约,而该智能合约并非设计用于处理任何 ERC20 token。在这种情况下,token 会永远卡在智能合约中。
目前,有价值数百万美元的 token 卡在智能合约中。
另一个被认为是糟糕 UX 和 ERC20 安全漏洞的问题是 approve
和 transferFrom
功能。这使得用户需要签署两笔交易,并且还会导致消费者双重支出的安全漏洞。
那么解决方案是什么?
在 ERC777 之前,提出了 ERC223。
它不是一个非常复杂的标准,相反,它只有两个额外的功能。
检查接收者地址是否为合约,如果是,则通过调用函数 tokenReceived
来检查它是否可以接收 token。这与 ERC721TokenReceiver
的概念非常相似。
ERC223 还声称通过定义一个新的函数来消除用户调用 approve
然后 transferFrom
的需求,该函数可以转移 token 并告诉接收者合约 token 已经被转移。
与此同时,我们得到了一个新的 ERC,即 ERC777,它包含了一些更多的功能,以及 ERC223 试图实现的功能。我们不能说 ERC777 是 ERC20 的扩展版本,因为它以一种非常不同的方式实现了该标准。尽管它是为了修复 ERC20 标准而来的,但它不像 ERC223 那样完全是从它派生出来的。
它提供了什么新功能?
它有一个类似于 ERC223 中 tokenReceived
的 hooks
概念,但 ERC777 为接收和发送 token 都提供了这个 hook。这些 hook 引入了更多的功能,比如在接收或发送 token 时回滚。我们稍后会讨论这个问题。
除了 hook 之外,它还有一个类似于 ERC721 的 Operators 概念,允许第三方地址代表持有者管理 token。
消除了增加 allowance 和减少 allowance 的过程。
让我们详细讨论一下。
首先,我们将查看接口,然后深入研究它。这样,我们可以更技术性地理解它提供的功能,而不是仅在理论上阅读它们。
interface ERC777Token {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function totalSupply() external view returns (uint256);
function balanceOf(address holder) external view returns (uint256);
function granularity() external view returns (uint256);
function defaultOperators() external view returns (address[] memory);
function isOperatorFor(
address operator,
address holder
) external view returns (bool);
function authorizeOperator(address operator) external;
function revokeOperator(address operator) external;
function send(address to, uint256 amount, bytes calldata data) external;
function operatorSend(
address from,
address to,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
function burn(uint256 amount, bytes calldata data) external;
function operatorBurn(
address from,
address to,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
//每次有 token 转移时
event Sent(
address indexed operator,
address indexed from,
address indexed to,
uint256 amount,
bytes data,
bytes operatorData
);
//每当 token 被铸造时
event Minted(
address indexed operator,
address indexed to,
uint256 amount,
bytes data,
bytes operatorData
);
//每当 token 被销毁时
event Burned(
address indexed operator,
address indexed from,
uint256 amount,
bytes data,
bytes operatorData
);
//当有人授权一个 operator 时
event AuthorizedOperator(
address indexed operator,
address indexed holder
);
// 当有人撤销一个 operator 时
event RevokedOperator(address indexed operator, address indexed holder);
}
有 13 个函数。该接口看起来与 ERC20 几乎相似,但有一些额外的功能,让我们讨论所有这些功能。
首先讨论 View 函数
此函数应返回 token 的名称。很明显 :’-)
function name() external view returns (string memory);
2. symbol 这应该返回 token 的符号。
例如,以太币的 ETH,币安币的 BNB 等。
function symbol() external view returns (string memory);
3. totalSupply 这应该返回流通中的已铸造 token 的总数。流通意味着不包括被销毁的 token 数量。
function totalSupply() external view returns (uint256);
4. balanceOf 顾名思义,这将返回任何地址的余额。是的,就像我们在 ERC20 中所做的那样,在映射中记录余额。
function balanceOf(address holder) external view returns (uint256);
5. granularity
这取代了 ERC20 中小数的概念。
Granularity 意味着 token 的最小分数。
将在 mint、burn 和 send 函数中传递的每个 token 值都应该是 granularity 的倍数,余额也是如此。默认情况下,granularity 应始终返回 1,除非有充分的理由使其更大。
就像 ERC20 中的小数一样,granularity 也应该是不可变的,并且在创建时设置。它不能为零。
function granularity() external view returns (uint256);
Operators 的概念与 ERC721 中的 Operators 非常相似。
它与 ERC20 的 approve 函数不同。因为在 approve/Allowance 功能中,持有者可以将一个值分配给任何其他地址,但在 ERC777 中,如果你分配了一个 operator,那么该地址可以花费你所有的 token。你不需要每次都为它们分配新的值。
6. defaultOperators
Default operators 是指在合约创建时设置的地址。Default operators 可以花费每个人的 token。
此函数应返回所有 default operators 的数组。
还有一个需要记住的是,default operators 以后不能更改。
function defaultOperators() external view returns (address[] memory);
7. isOperatorFor
此函数应该返回任何地址是否是给定地址的 operator,将两个地址作为参数传递。
请记住,这与 default operators 不同,因为这些 operators 是由 token 持有者分配的。
function isOperatorFor(address operator,address holder) external view returns (bool);
8. authorizeOperator
此函数应允许用户将任何地址指定为 operator。
持有者不应能够将其地址指定为 operator。
function authorizeOperator(address operator) external;
9. revokeOperator
顾名思义,此函数应用于撤销 operator。
function revokeOperator(address operator) external;
在进入 token 的转移之前,要记住的一件事是,每个涉及 token 移动的函数都应该调用 hook。
10. send 不要被这个名字弄糊涂了,它和我们都知道的 transfer 函数是一样的,只是名字改成了 send。另一个区别是参数的数量。还有一个额外的参数 data
。我们可以以一种方式定义 hook,使它们基于此 send
函数中给定的数据来执行。
这种机制看起来很熟悉,对吧?是的,它类似于以太币(以太坊的本地 currency)的转移。在官方 ERC 中也提到了“加粗这个标准使用了与以太币相同的理念加粗”。
function send(address to, uint256 amount, bytes calldata data) external;
11. operatorSend
此函数等效于 ERC20 中的 transferFrom 函数。
授权的 operator 可以使用此函数代表另一个地址发送 token。请注意,还有一个额外的 operatorData
参数,operator 可以使用该参数在交易中发送一些数据。
token 的持有者也可以使用此函数,在这种情况下,它的工作方式应该类似于 send 函数,带有 operatorData 的附加参数
function operatorSend(
address from,
address to,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
12. burn
持有者应该能够使用此函数销毁他们的 token。与往常一样,这应该减少 totalSupply 和持有者的余额。
虽然我们假设 token 被转储到零地址,但零地址的余额不应该有任何增加。
此函数还应该调用 hook。
function burn(uint256 amount, bytes calldata data) external;
13. operatorBurn
每当 operator 需要代表持有者销毁 token 时,他们应该使用此函数。
该功能将与销毁相同,只是 operator 应该能够调用此函数。
function operatorBurn(
address from,
address to,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
看到接口,我们已经得出结论,这与 ERC20 不同,尽管核心思想保持不变,但实现方式却大相径庭。
现在,ERC777 最重要的功能之一是它在可互换 token 中引入了 hook 的概念,并利用了 ERC1820 提供的可能性。
但是在了解两个 ERC 之间的关系之前,我们需要知道 ERC1820 到底提供了什么。
ERC1820 也被称为注册合约。为了更好地理解这一点,你需要回忆一下我们在我的 ERC721 文章中讨论过的 ERC165。
简而言之,合约使用 ERC165 来证明它们实现了某个接口。
每个合约分别实现 ERC165,但是如果我们在区块链上部署一个合约并将其用作注册合约呢?
这就是 ERC1820 使用的概念,即每个链都将拥有一个注册合约,其中每个其他合约或 EOA 都可以注册自己以及它们正在实现的相应接口。
ERC777 与 ERC1820 有什么关系?
还记得我们之前讨论过的 hook 吗?这就是 ERC1820 发挥作用的地方。Hook 在每个 token 标准中都起着重要的作用。因为它们允许我们在 token 的转移过程中定义一些自定义逻辑。该逻辑可以是任何东西,比如“加粗当有人发送 X 个 token 时,铸造一个 NFT加粗” 等。
我们在 ERC777 中有 2 个不同的 hook,tokensReceived
和 tokensToSend
。顾名思义,tokensToSend
将为发送者调用,而 tokensReceived
将在接收者端执行。让我们逐一看看它们。
tokensToSend hook
每当有 token 转移时,transfer 函数应该查询 ERC1820 注册合约,以查看发送者是否实现了 ERC777TokensSender
接口,如果实现了,则应调用 tokensToSend
hook。此 hook 应允许发送者回滚交易(这取决于该接口的钱包实现)。
在 token 转移之后,应该检查接收者是否实现了 ERC777TokensRecipient
接口。如果实现了,则应调用 tokensReceived
hook。
需要注意的重要事项是,如果接收者是一个合约并且它没有实现 ERC777TokensRecipient
接口,则调用应该回滚。但是,正如我们所知,ERC777 仍然向后兼容 ERC20,如果此函数是通过 transfer
或 transferFrom
函数调用的,那么即使接收者未实现该接口,它也应继续处理。
这些 hook 也可以用于拒绝传入或传出的 token,只需在提供任何条件为真时回滚 hook 即可。作者声称这可以用于拒绝垃圾邮件 token。
到目前为止,我们已经了解了关于 ERC777 的几乎所有内容。
尽管 ERC777 提供了更多的功能并修复了 ERC20 的批准/转移问题,但为什么没有人使用这个标准,为什么不推荐使用它?Openzeppelin 也删除了 ERC777 库。
到目前为止,有一点非常清楚,ERC777 比 ERC20 复杂得多,但我们需要确保在某些东西上增加复杂性应该提供值得增加的价值。
这就是社区从未喜欢 ERC777 的原因。它被认为是过度设计的。
正如我之前提到的,在更新状态之前调用发送者 hook 也是一种容易受到攻击的做法,因为它违反了 solidity 中的 Checks, Effects, and Interaction (CEI) 模型。开发者们提出了更多担忧。
最后,不建议使用 ERC777,相反,我们可以使用其他 ERC,例如 ERC2612、ERC1363 等,它们是 ERC20 的扩展。
这就是这篇文章的全部内容,希望你理解 ERC777 的逻辑以及为什么不使用它,请随时在此处提出你可能有的任何问题这里。
- 原文链接: decipherclub.com/the-fal...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!