Solidity极简入门: 29. 函数选择器Selector

我最近在重新学solidity,巩固一下细节,也写一个“Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。

欢迎关注我的推特:@0xAA_Science

WTF技术社群discord,内有加微信群方法:链接

所有代码和教程开源在github(1024个star发课程认证,2048个star发社群NFT): github.com/AmazingAng/WTFSolidity


selector

当我们调用智能合约的函数时,发送的字节码的前4个字节是selector(函数选择器)。这一讲,我们将介绍selector是什么,以及如何使用。

msg.data

msg.datasolidity中的一个全局变量,值为完整的calldata(调用函数的字节码)。

在下面的代码中,我们可以通过Log事件来输出调用mint函数的calldata

    // event 返回msg.data
    event Log(bytes data);

    function mint(address to) external{
        emit Log(msg.data);
    }

当参数为0x2c44b726ADF1963cA47Af88B284C06f30380fC78时,输出的calldata

0x6a6278420000000000000000000000002c44b726adf1963ca47af88b284c06f30380fc78

这段很乱的字节码可以分成两部分:

前4个字节为函数选择器selector:
0x6a627842

后面32个字节为输入的参数:
0000000000000000000000002c44b726adf1963ca47af88b284c06f30380fc78

其实calldata就是告诉智能合约,我要调用哪个函数,以及参数是什么。

函数签名selector

selector定义为函数签名的哈希的前4个字节,那么函数签名是什么?

其实在第21讲中,我们简单介绍了函数签名,为"函数名(逗号分隔的参数类型)"。举个例子,上面代码中mint的函数签名为"mint(address)"。在智能合约中,不同的函数有不同的函数签名,因此我们可以通过函数签名来确定要调用哪个函数。

注意,在函数签名中,uintint要写为uint256int256

我们写一个函数,来验证mint函数的selector是否为0x6a627842。大家可以运行下面的函数,看看结果。

    function mintSelector() external pure returns(bytes4 mSelector){
        return bytes4(keccak256("mint(address)"));
    }

使用selector

我们可以利用selector来调用目标函数。例如我想调用mint函数,我只需要利用abi.encodeWithSelectormint函数的selector和参数打包编码,传给call函数:

    function callWithSignature() external returns(bool, bytes memory){
        (bool success, bytes memory data) = address(this).call(abi.encodeWithSelector(0x6a627842, "0x2c44b726ADF1963cA47Af88B284C06f30380fC78"));
        return(success, data);
    }

在日志中,我们可以看到mint函数被成功调用,并输出Log事件。

总结

这一讲,我们介绍了什么是函数选择器selector),它和msg.data函数签名的关系,以及如何使用它调用目标函数。

Subscribe to 0xAA
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.