Ale*_*hin 10 solidity rsk hardhat
我想创建一个包含功能的应付令牌transferAndCall(TokenReceiver to, uint256 amount, bytes4 selector)。通过调用该函数,您可以将代币转移到TokenReceiver智能合约地址,然后调用接收者,接收者又调用接收者onTransferReceived(address from,uint tokensPaid, bytes4 selector)中指定的函数。bytes4 selector
请注意,这与 ERC1363 类似/受到ERC1363的启发。
这是我的应收令牌的简化版本:
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MeowToken is ERC20 {
constructor() ERC20("MeowToken", "MEO") {
ERC20._mint(msg.sender, 10_000_000);
}
function transferAndCall(
TokenReceiver to,
uint256 amount,
bytes4 selector
) external {
ERC20.transfer(address(to), amount);
to.onTransferReceived(msg.sender, amount, selector);
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个令牌接收器:
contract TokenReceiver {
address acceptedToken;
event PurchaseMade(address from, uint tokensPaid);
modifier acceptedTokenOnly () {
require(msg.sender == address(acceptedToken), "Should be called only via the accepted token");
_;
}
constructor(address _acceptedToken) {
acceptedToken = _acceptedToken;
}
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
require(success, "Function call failed");
}
function purchase(address from, uint tokensPaid) public acceptedTokenOnly {
emit PurchaseMade(from, tokensPaid);
}
}
Run Code Online (Sandbox Code Playgroud)
我想确保接收器上的公共函数仅通过应付令牌调用。因此我acceptedTokenOnly给它们都添加了修饰符。然而,添加修改器后,我的测试开始失败:
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MeowToken is ERC20 {
constructor() ERC20("MeowToken", "MEO") {
ERC20._mint(msg.sender, 10_000_000);
}
function transferAndCall(
TokenReceiver to,
uint256 amount,
bytes4 selector
) external {
ERC20.transfer(address(to), amount);
to.onTransferReceived(msg.sender, amount, selector);
}
}
Run Code Online (Sandbox Code Playgroud)
1) Transfer and call
Transfer Tokens and call Purchase:
Error: VM Exception while processing transaction: reverted with reason string 'Function call failed'
Run Code Online (Sandbox Code Playgroud)
为什么会出现这种情况?如何确保接收者的函数仅由接受的令牌调用?
作为参考,我正在 Hardhat 中开发和测试智能合约并在RSK上部署。
小智 9
当你这样做时:
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
Run Code Online (Sandbox Code Playgroud)
您正在拨打外部电话,这意味着它将msg.sender变成address(this).
acceptedTokenOnly现在,函数期间的修饰符purchase将失败,因为msg.sender不再是令牌。
建议将功能更改为:
function purchase(address from, uint tokensPaid) public {
require(msg.sender == address(this), "wrong sender");
emit PurchaseMade(from, tokensPaid);
}
Run Code Online (Sandbox Code Playgroud)
问题是,您使用的是低级call方法,此处:\n\xe2\x80\x8b
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));\nRun Code Online (Sandbox Code Playgroud)\nmsg.sender\xe2\x80\x8b\n这会将inside的值onTransferReceived从接受的令牌更改为接收者本身。
这是实现您想要的效果的一种方法:\n\xe2\x80\x8b\n替换call为delegatecall。\n这将立即解决您的问题。\n与 不同call,它将delegatecall代表调用者智能合约调用您的函数:\n\xe2 \x80\x8b
function onTransferReceived(\n address from,\n uint tokensPaid,\n bytes4 selector\n ) public acceptedTokenOnly {\n (bool success,) = address(this).delegatecall(abi.encodeWithSelector(selector, from, tokensPaid));\n require(success, "Function call failed");\n }\nRun Code Online (Sandbox Code Playgroud)\n
除了从 切换call到delegatecall,正如 @Juan 的回答中提到的,还有一种更“手动”的方法:\n\xe2\x80\x8b\n不要call完全使用,而是按名称调用函数。\n这可以使用来完成if ... else将selector参数与预期功能选择器进行比较的控制结构 ( ) purchase:\n\xe2\x80\x8b
function onTransferReceived(\n address from,\n uint tokensPaid,\n bytes4 selector\n ) public acceptedTokenOnly {\n if (selector == this.purchase.selector) {\n purchase(from, tokensPaid);\n } else {\n revert("Call of an unknown function");\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n\xe2\x80\x8b\n虽然这样做比较繁琐,但从安全角度来看可能更可取。\n例如,如果您希望将允许通过此机制调用的函数列入\n白名单。 \n请注意,使用call/ 的方法delegatecall暴露了任意(可能是非预期)函数执行的潜在漏洞。
| 归档时间: |
|
| 查看次数: |
313 次 |
| 最近记录: |