智能合约中如何解码bytes calldata并调用对应函数
解决方案
你当前的链下calldata编码逻辑无需调整,链上只需要按照ABI编码规则做切片解码、匹配函数选择器后调用对应逻辑即可,以下是针对两个问题的具体实现:
1. 链上解码bytes类型calldata
ERC1363传入的data参数是标准ABI编码的字节数组,Solidity原生支持字节切片与abi.decode解码,不需要引入额外工具库:
- 字节数组前4位固定为函数选择器(即你链下计算的
buySigHash),可以直接通过字节切片提取 - 4位之后的内容是函数参数的ABI编码段,直接传入
abi.decode,按照你链下编码的参数类型传入类型元组即可完成解码
注意:buy函数需要的sender(转账发起方)、tokensPaid(转账金额)是ERC1363代币合约在调用onTransferReceived时直接传入的可信值,不需要从用户传入的data中解析,避免用户伪造参数。
2. 提取选择器并调用目标函数
根据你的业务场景,可以选择两种实现方式:
方式一:单函数场景最简实现(适配你当前的购买逻辑)
直接校验选择器是否匹配buy函数,解码参数后直接调用即可,安全性最高,gas成本最低:
function onTransferReceived( address operator, address sender, uint256 tokensPaid, bytes calldata data ) external override (IERC1363Receiver) returns (bytes4) { // 首先校验调用方为你认可的ERC1363代币合约,阻断假代币调用 require(msg.sender == address(acceptedToken), "caller is not accepted ERC1363 token"); // 提取前4字节作为函数选择器 bytes4 targetFuncSelector = bytes4(data[:4]); // 校验选择器匹配buy函数 require(targetFuncSelector == this.buy.selector, "unsupported function call"); // 解码4字节之后的参数段,对应链下编码的productAmount、color两个参数 (uint256 productAmount, bytes3 color) = abi.decode(data[4:], (uint256, bytes3)); // 传入可信的sender、tokensPaid与解码得到的业务参数,调用buy逻辑 buy(sender, tokensPaid, productAmount, color); return this.onTransferReceived.selector; }
方式二:多函数通用实现(后续扩展多场景时使用)
如果后续需要支持多个通过transferAndCall触发的函数,可以使用低级别call实现通用路由,不需要为每个函数写单独的解码逻辑:
function onTransferReceived( address operator, address sender, uint256 tokensPaid, bytes calldata data ) external override (IERC1363Receiver) returns (bytes4) { require(msg.sender == address(acceptedToken), "caller is not accepted ERC1363 token"); require(data.length >= 4, "invalid calldata length"); // 提取函数选择器 bytes4 targetFuncSelector = bytes4(data[:4]); // 把链上可信的sender、tokensPaid与用户传入的业务参数拼接为完整的函数调用calldata // 注意:所有可触发的函数前两个参数必须固定为(address sender, uint256 tokensPaid) bytes memory fullFuncCalldata = abi.encodePacked( targetFuncSelector, abi.encode(sender, tokensPaid), data[4:] ); // 低级别调用当前合约对应函数 (bool callSuccess, ) = address(this).call(fullFuncCalldata); require(callSuccess, "target function execution failed"); return this.onTransferReceived.selector; }
注意:使用通用路由实现时,所有可被触发的业务函数(比如当前的
buy)必须保留调用方校验,禁止外部账户直接绕开转账流程调用这些函数。
测试说明
你现有的Hardhat测试用例不需要修改,链上解码逻辑和链下编码规则完全匹配,执行时会正常触发PurchaseMade事件。
内容的提问来源于stack exchange,提问作者Aleks Shenshin
相关产品推荐
相关产品推荐

