什么是函数选择器Solidity的函数选择器(FunctionSelector)是EVM中用于标识智能合约中特定函数的唯一4字节(8位十六进制)标识符。它本质上是函数签名的Keccak-256哈希值的前4个字节。主要用于在低级调用(如call、delegatecall、sta
Solidity 的函数选择器(Function Selector)是 EVM 中用于标识智能合约中特定函数的唯一 4 字节(8 位十六进制)标识符。它本质上是函数签名的 Keccak-256 哈希值的前 4 个字节。主要用于在低级调用(如call、delegatecall、staticcall)中指定要调用的目标函数,它是 EVM(以太坊虚拟机)在解析交易或消息调用时,用来确定应该执行合约中哪个函数的关键机制。
函数选择器是函数签名的 Keccak-256 (SHA-3) 哈希值的前 4 个字节(最高位的 4 个字节)。主要可以分为三步:
1. 函数签名(Function Signature):
函数签名由函数名和参数类型组成,不包括返回值。
```js
functionName(typeN1, typeN2...)
```注意:
不包括参数名。
不包括空格。
不包括返回类型。
使用参数的规范类型名称(全称且区分大小写)。 例如:
uint 必须写为 uint256int 必须写为 int256byte 必须写为 bytes1示例:
transfer(address,uint256)approve(address,uint256)balanceOf(address)2. 计算函数 Keccak-256 哈希:
3. 提取前 4 字节:
a9059cbbtransfer(address,uint256) 的选择器: 0xa9059cbb使用案例:
function callTransfer(address _token, address _to, uint256 _amount) public {
    // 生成包含选择器和参数的 calldata
    bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", _to, _amount);
    // 发起调用
    (bool success, ) = _token.call(data);
    require(success, "Transfer call failed");
}在 Solidity 中,abi.encode 和相关的方法主要用于将数据编码成字节数组,便于存储、传输或计算哈希值。这些方法是以太坊 ABI 编码标准的一部分。
将参数编码为标准 ABI 格式的字节数组,不带任何长度信息,适合用于哈希计算。
pragma solidity ^0.8.0;
contract ABIEncodeExample {
    function encodeExample(uint256 a, string memory b) public pure returns (bytes memory) {
        return abi.encode(a, b);
    }
}0x
0000000000000000000000000000000000000000000000000000000000000001 // uint256 a
0000000000000000000000000000000000000000000000000000000000000040 // 偏移量(字符串 b 的起始位置)
0000000000000000000000000000000000000000000000000000000000000005 // 字符串 b 的长度
68656c6c6f000000000000000000000000000000000000000000000000000000 // 字符串 b 的内容(hello)以紧凑格式编码参数。
pragma solidity ^0.8.0;
contract ABIEncodePackedExample {
    function encodePackedExample(uint256 a, string memory b) public pure returns (bytes memory) {
        return abi.encodePacked(a, b);
    }
}编码结果:对 encodePackedExample(1, "hello") 的结果。
0x
010000000000000000000000000000000000000000000000000000000000000068656c6c6f将数据编码,并在开头添加函数选择器。
用于构造函数调用数据
选择器是目标函数签名的前4字节
pragma solidity ^0.8.0;
contract ABIEncodeWithSelectorExample {
    function encodeWithSelectorExample(address recipient, uint256 amount) public pure returns (bytes memory) {
        return abi.encodeWithSelector(bytes4(keccak256("transfer(address,uint256)")), recipient, amount);
    }
}编码结果:对 encodeWithSelectorExample(0xAbCdEf0000000000000000000000000000000000, 100) 的结果:
    0xa9059cbb // 函数选择器 transfer(address,uint256)
000000000000000000000000abcdef0000000000000000000000000000000000 // recipient
0000000000000000000000000000000000000000000000000000000000000064 // amount根据字符串形式的函数签名生成编码数据。
pragma solidity ^0.8.0;
contract ABIEncodeWithSignatureExample {
    function encodeWithSignatureExample(address recipient, uint256 amount) public pure returns (bytes memory) {
        return abi.encodeWithSignature("transfer(address,uint256)", recipient, amount);
    }
}编码结果:对 encodeWithSelectorExample(0xAbCdEf0000000000000000000000000000000000, 100) 的结果
0xa9059cbb // 函数选择器 transfer(address,uint256)
000000000000000000000000abcdef0000000000000000000000000000000000 // recipient
0000000000000000000000000000000000000000000000000000000000000064 // amount在低级调用(如 call)中,函数选择器用于标识目标函数。
 contract Example {
    function callTransfer(address target, address recipient, uint256 amount) public {
        bytes memory data = abi.encodeWithSelector(
            bytes4(keccak256("transfer(address,uint256)")),
            recipient,
            amount
        );
        (bool success, ) = target.call(data);
        require(success, "Call failed");
    }
}当合约接收到调用但没有匹配函数时,会触发 fallback 函数,开发者可以通过 msg.data 获取函数选择器。
 contract FallbackExample {
   receive() external payable {}
        bytes4 selector = bytes4(msg.data[:4]); // depositETH 函数选择器
        // 根据 selector 执行相应逻辑
    }
     receive() external payable {
        depositETH()
    }
    // 上面两个 receive 方法的效果有
    function depositETH() public {
    }
}在模块化合约(如钻石标准)中,函数选择器用于路由到不同的实现模块。
 
                如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!