当一个函数被调用时,calldata 的前 4 个字节指定调用哪个函数。
这 4 个字节称为函数选择器。
举个例子,下面这段代码。 它使用 call 在地址 addr 的合约上执行转账。
addr.call(abi.encodeWithSignature("transfer(address,uint256)", 0xSomeAddress, 123))
abi.encodeWithSignature(....) 返回的前 4 个字节是函数选择器。
如果在代码中预先计算并内联函数选择器,也许可以节省少量气体?
以下是函数选择器的计算方式。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract FunctionSelector {
/*
"transfer(address,uint256)"
0xa9059cbb
"transferFrom(address,address,uint256)"
0x23b872dd
*/
function getSelector(string calldata _func) external pure returns (bytes4) {
return bytes4(keccak256(bytes(_func)));
}
}
在上面的示例中,我们定义了一个名为 FunctionSelector
的合约。该合约包含两个函数:
foo()
函数返回当前函数选择器(即this.foo.selector
)。bar(bytes memory _data)
函数将_data
参数解码为bytes4
值,并将其作为返回值返回。
在 Solidity 中,每个函数都有一个唯一的函数选择器(function selector)。函数选择器是函数名称和参数类型的哈希值。在智能合约内部,可以使用函数选择器来调用其他合约的函数。
在示例中,我们使用 this.foo.selector
返回 foo()
函数的函数选择器。
abi.decode()
是 Solidity 中的一个特殊函数,用于将字节数组解码为给定的类型。在示例中,我们使用 abi.decode()
将 _data
参数解码为 bytes4
类型。
return abi.decode(_data[:4], (bytes4));
在上面的示例中,我们使用 _data[:4]
将字节数组的前四个字节作为输入,并将其解码为 bytes4
类型。该函数的第二个参数指定解码结果的类型。
使用 Remix 或其他 Solidity 集成开发环境编译和部署合约。在部署后,您可以调用 foo()
函数并获取当前函数选择器:
0x8f4a7106
将此值传递给 bar(bytes memory _data)
函数,它将返回相同的函数选择器。
Solidity 的函数选择器是函数名称和参数类型的哈希值。使用函数选择器,智能合约可以方便地调用其他合约的函数。abi.decode()
函数可将字节数组解码为给定的类型。