为什么会有ERC1363标准?我们知道,ERC20是用于在以太坊区块链上创建和管理代币的流行标准。它定义了一组函数和事件,用于转账、批准和查询代币余额。虽然ERC20标准已经成功用于创建各种代币,但它也存在一些已知的缺点和漏洞,这些漏洞被黑客利用,盗取了很多代币,让项目损失惨重。
我们知道,ERC20是用于在以太坊区块链上创建和管理代币的流行标准。它定义了一组函数和事件,用于转账、批准和查询代币余额。虽然 ERC20 标准已经成功用于创建各种代币,但它也存在一些已知的缺点和漏洞,这些漏洞被黑客利用,盗取了很多代币,让项目损失惨重。
ERC1363 是对 ERC20 标准的扩展,它引入了在转账或批准后执行回调函数的功能。这意味着符合 ERC1363 的代币除了具有 ERC20 代币的所有功能之外,还具有以下一些特点和优势
提高可组合性: ERC1363 代币可以与其他智能合约无缝交互,并在转账或批准发生时触发操作。这使得它们适用于需要代币与其他去中心化应用程序 (dApp) 集成的各种用例。
增强安全性: ERC1363 代币可以用来防止代币丢失或锁定在合约中。例如,在将代币转账给接收者合约之前,可以调用回调函数来检查接收者合约是否有效。这可以帮助防止代币意外发送到无法处理它们的合约中。
改善用户体验: ERC1363 代币可以用于创建更原子性的交易,并减少用户需要确认的交易数量。例如,ERC1363 代币可以用于将代币转账和批准操作组合成单个交易。这可以提高交易速度并改善用户体验。
在手把手教你实现TokenBank智能合约中,假设用户向合约转账 ERC20 代币。由于没有机制可以查看是谁进行了转账,智能合约无法为转账用户记账。
一个比较典型的解决方案是:接收者使用 transferFrom 将代币转账给自己,但是前提条件是:代币发送方批准接收智能合约代表发送方转账代币。
contract ReceivingContract {
function deposit(uint256 amount) external {
// 如果未经批准或用户余额不足,将会回滚
ERC20(token).transferFrom(msg.sender, address.this, amount);
// 为账户记账
deposits[msg.sender] += amount;
}
}
存款人调用接收智能合约的函数(在上面的示例代码中为 deposit)来从发送方转账代币到合约。由于合约知道它从用户那里转账了代币,因此能够正确记账。
然而,为了批准合约转账代币,需要增加额外的交易费用。并且前置的授权操作,会让用户感觉有些繁琐。
此外,用户在批准合约后应将批准设置为零,否则存在合约被利用的风险,可能导致合约从用户那里提取更多 ERC20 代币。
转账Hook是接收智能合约中的预定义函数,当它接收到代币时将被调用。也就是说,代币合约在接收到转账指令后,先执行转账操作,执行完毕之后,会在接收地址上调用预定义函数。
如果这个预定义函数不存在、回滚或未返回预期的成功值,则这笔转账交易会回滚。
要实现ERC1363这个标准,ERC20需要额外的函数(稍后会解释)来转账代币并同时触发接收方的转账Hook,并且接收方必须根据标准实现转账Hook。
对于接收ERC1363代币的合约,我们要想通知到它,它就必须实现 IERC1363Receiver,(请查看OpenZeppelin实现),其中包含一个名为 onTransferReceived 的函数:
这个函数成功的时候会返回:
bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))
pragma solidity ^0.8.20;
interface IERC1363Receiver {
function onTransferReceived(
address operator,
address from,
uint256 value,
bytes calldata data
) external returns (bytes4);
}
在接收合约实现此函数时,请始终检查msg.sender是否是你希望接收的ERC1363合约的代币,因为任何人都可以使用任意值调用 onTransferReceived()。
下面是一个代码实例,这个合约可以接收指定的ERC1363代币:
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/interfaces/IERC1363Receiver.sol";
import "@openzeppelin/contracts/interfaces/IERC1363.sol";
contract TokenReceiver is IERC1363Receiver {
address internal erc1363Token;
// 部署此合约的时候,erc1363Token合约地址作为入参传入
constructor(address erc1363Token_) {
erc1363Token = erc1363Token_;
}
mapping(address user => uint256 balance) public balances;
function onTransferReceived(
address operator,
address from,
uint256 value,
bytes calldata data
) external returns (bytes4) {
require(msg.sender == erc1363Token, "not the expected token");
balances[from] += value;
return this.onTransferReceived.selector;
}
function withdraw(uint256 value) external {
require(balances[msg.sender] >= value, "balance too low");
balances[msg.sender] -= value;
IERC1363(erc1363Token).transfer(msg.sender, value);
}
}
接收代币的合约知道自己收到ERC20代币的传统方式是使用transferFrom函数,该函数需要首先进行代币额度批准,但是使用 ERC1363 后,合约能够知道自己已收到代币,并且还能够跳过批准步骤,因为 transferAndCall 将EOA账户通过 transfer 将代币转账给接收者合约(无需批准)并调用 onTransferReceived 函数。
这个新代币标准的问题在于现有协议无法使用它们,为了最大化向后兼容性,ERC1363 是一种 ERC20 代币,它添加了旧协议不需要使用的额外函数,并不会影响原有功能。
所有现有的 ERC20 函数:name、symbol、decimals、totalSupply、balanceOf、transfer、transferFrom、approve 和 allowance 的行为都与 ERC20 标准规定的完全一致。
ERC1363 标准添加了新函数到 ERC20,以便旧协议仍然可以与 ERC1363 代币交互,就像与 ERC20 代币一样。但是,如果需要,新协议可以利用 ERC1363 上的转账Hook。
要成为符合 ERC1363 标准的代币,代码还必须实现六个额外的函数:
顾名思义,这些函数将执行 ERC20 操作,然后调用接收方的Hook函数。
每个函数都有两个版本,一个带有数据参数,一个不带。数据参数是为了发送方能够将数据转发给接收合约。
// 有两个 transferAndCall 函数,
// 一个带有数据参数,一个不带
function transferAndCall(
address to,
uint256 value
) external returns (bool);
function transferAndCall(
address to,
uint256 value,
bytes calldata data
) external returns (bool);
// 有两个 transferFromAndCall 函数,
// 一个带有数据参数,一个不带
function transferFromAndCall(
address from,
address to,
uint256 value
) external returns (bool);
function transferFromAndCall(
address from,
address to,
uint256 value,
bytes calldata data
) external returns (bool);
// 有两个 approveAndCall 函数,// 一个带有数据参数,一个不带```
function approveAndCall(
address spender,
uint256 value
) external returns (bool);
function approveAndCall(
address spender,
uint256 value,
bytes calldata da...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!