You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

智能合约中如何解码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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:39:19