Skip to main content

10.Function Selector

在EVM中,函数选择器是用来告诉EVM你要调用哪一个函数的。

函数选择器是一个 4byte的hash值,Solidity使用它来识别函数。

可以使用.selector来查看:

pragma solidity 0.8.25;

contract SelectorTest{

function foo() public {}

function getSelectorOfFoo() external pure returns (bytes4) {
return this.foo.selector; // 0xc2985578
}
}

如果函数没有 arguments,可以使用call来作为data发送给合约来调用函数:

pragma solidity 0.8.25;

contract CallFoo {

function callFooLowLevel(address _contract) external {
bytes4 fooSelector = 0xc2985578;

(bool ok, ) = _contract.call(abi.encodePacked(fooSelector));
require(ok, "call failed");
}

}

contract FooContract {

uint256 public x;

function foo() public {
x = 1;
}
}

Solidity function signature

Solidity 中的函数签名是一个字符串,其中包含合约名称,后跟它接受的参数类型。变量名称已从参数中删除。比如:

function setPoint(uint256 x, uint256 y) --> "setPoint(uint256,uint256)"
function setName(string memory name) --> "setName(string)"
function addValue(uint v) --> "addValue(uint256)"

函数选择器中没有空格。所有 uint 类型必须显式包含其大小(uint256、uint40、uint8 等)。不包括 calldata 和内存类型。例如,getBalanceById(uint) 是无效签名。

如何根据signature来计算selector

函数选择器是函数签名的 keccak256 哈希值的前四个字节。


//SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

contract FunctionSignatureTest {

function foo() external {}

function point(uint256 x, uint256 y) external {}

function setName(string memory name) external {}

function testSignatures() external pure returns (bool) {

// NOTE: Casting to bytes4 takes the first 4 bytes
// and removes the rest

assert(bytes4(keccak256("foo()")) == this.foo.selector);
assert(bytes4(keccak256("point(uint256,uint256)")) == this.point.selector);
assert(bytes4(keccak256("setName(string)")) == this.setName.selector);

return true;

}
}

Tips

  • internal function 没有函数选择器
  • 使用selector的原因是,Solidity 函数名称可以任意长,如果函数名称很长,则会增加交易的大小和成本。如果提供函数选择器而不是名称,则tx的大小通常会更小。
  • fallback函数也没有selector

Tools

可以使用这两个网站来进行计算和搜索: