Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update 29: Add Calculating Method for Function Signatures with Various Parameter… #532

Merged
merged 2 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 79 additions & 5 deletions 29_Selector/Selector.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

contract DemoContract {
// empty contract
}

contract Selector{
// event 返回msg.data
event Log(bytes data);
event SelectorEvent(bytes4);

// Struct User
struct User {
uint256 uid;
bytes name;
}

// Enum School
enum School { SCHOOL1, SCHOOL2, SCHOOL3 }

// 输入参数 to: 0x2c44b726ADF1963cA47Af88B284C06f30380fC78
function mint(address /*to*/) external{
Expand All @@ -16,10 +30,70 @@ contract Selector{
return bytes4(keccak256("mint(address)"));
}

// 无参数selector
// 输入: 无
// nonParamSelector() : 0x03817936
function nonParamSelector() external returns(bytes4 selectorWithNonParam){
emit SelectorEvent(this.nonParamSelector.selector);
return bytes4(keccak256("nonParamSelector()"));
}

// elementary(基础)类型参数selector
// 输入:param1: 1,param2: 0
// elementaryParamSelector(uint256,bool) : 0x3ec37834
function elementaryParamSelector(uint256 param1, bool param2) external returns(bytes4 selectorWithElementaryParam){
emit SelectorEvent(this.elementaryParamSelector.selector);
return bytes4(keccak256("elementaryParamSelector(uint256,bool)"));
}

// fixed size(固定长度)类型参数selector
// 输入: param1: [1,2,3]
// fixedSizeParamSelector(uint256[3]) : 0xead6b8bd
function fixedSizeParamSelector(uint256[3] memory param1) external returns(bytes4 selectorWithFixedSizeParam){
emit SelectorEvent(this.fixedSizeParamSelector.selector);
return bytes4(keccak256("fixedSizeParamSelector(uint256[3])"));
}

// non-fixed size(可变长度)类型参数selector
// 输入: param1: [1,2,3], param2: "abc"
// nonFixedSizeParamSelector(uint256[],string) : 0xf0ca01de
function nonFixedSizeParamSelector(uint256[] memory param1,string memory param2) external returns(bytes4 selectorWithNonFixedSizeParam){
emit SelectorEvent(this.nonFixedSizeParamSelector.selector);
return bytes4(keccak256("nonFixedSizeParamSelector(uint256[],string)"));
}

// mapping(映射)类型参数selector
// 输入:demo: 0x9D7f74d0C41E726EC95884E0e97Fa6129e3b5E99, user: [1, "0xa0b1"], count: [1,2,3], mySchool: 1
// mappingParamSelector(address,(uint256,bytes),uint256[],uint8) : 0xe355b0ce
function mappingParamSelector(DemoContract demo, User memory user, uint256[] memory count, School mySchool) external returns(bytes4 selectorWithMappingParam){
emit SelectorEvent(this.mappingParamSelector.selector);
return bytes4(keccak256("mappingParamSelector(address,(uint256,bytes),uint256[],uint8)"));
}

// 使用selector来调用函数
function callWithSignature() external returns(bool, bytes memory){
// 只需要利用`abi.encodeWithSelector`将`mint`函数的`selector`和参数打包编码
(bool success, bytes memory data) = address(this).call(abi.encodeWithSelector(0x6a627842, 0x2c44b726ADF1963cA47Af88B284C06f30380fC78));
return(success, data);
function callWithSignature() external{
// 初始化uint256数组
uint256[] memory param1 = new uint256[](3);
param1[0] = 1;
param1[1] = 2;
param1[2] = 3;

// 初始化struct
User memory user;
user.uid = 1;
user.name = "0xa0b1";

// 利用abi.encodeWithSelector将函数的selector和参数打包编码
// 调用nonParamSelector函数
(bool success0, bytes memory data0) = address(this).call(abi.encodeWithSelector(0x03817936));
// 调用elementaryParamSelector函数
(bool success1, bytes memory data1) = address(this).call(abi.encodeWithSelector(0x3ec37834, 1, 0));
// 调用fixedSizeParamSelector函数
(bool success2, bytes memory data2) = address(this).call(abi.encodeWithSelector(0xead6b8bd, [1,2,3]));
// 调用nonFixedSizeParamSelector函数
(bool success3, bytes memory data3) = address(this).call(abi.encodeWithSelector(0xf0ca01de, param1, "abc"));
// 调用mappingParamSelector函数
(bool success4, bytes memory data4) = address(this).call(abi.encodeWithSelector(0xe355b0ce, 0x9D7f74d0C41E726EC95884E0e97Fa6129e3b5E99, user, param1, 1));
require(success0 && success1 && success2 && success3 && success4);
}
}
}
Binary file modified 29_Selector/img/29-2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 29_Selector/img/29-3.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 79 additions & 6 deletions 29_Selector/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,91 @@ function mintSelector() external pure returns(bytes4 mSelector){

![method id in remix](./img/29-2.png)

### 使用selector
由于计算`method id`时,需要通过函数名和函数的参数类型来计算。在`Solidity`中,函数的参数类型主要分为:基础类型参数,固定长度类型参数,可变长度类型参数和映射类型参数。

##### 基础类型参数
`solidity`中,基础类型的参数有:`uint256`(`uint8`, ... , `uint256`)、`bool`, `address`等。在计算`method id`时,只需要计算`bytes4(keccak256("函数名(参数类型1,参数类型2,...)"))`。例如,如下函数,函数名为`elementaryParamSelector`,参数类型分别为`uint256`和`bool`。所以,只需要计算`bytes4(keccak256("elementaryParamSelector(uint256,bool)"))`便可得到此函数的`method id`。
```solidity
    // elementary(基础)类型参数selector
    // 输入:param1: 1,param2: 0
    // elementaryParamSelector(uint256,bool) : 0x3ec37834
    function elementaryParamSelector(uint256 param1, bool param2) external returns(bytes4 selectorWithElementaryParam){
        emit SelectorEvent(this.elementaryParamSelector.selector);
        return bytes4(keccak256("elementaryParamSelector(uint256,bool)"));
    }
```

我们可以利用`selector`来调用目标函数。例如我想调用`mint`函数,我只需要利用`abi.encodeWithSelector`将`mint`函数的`method id`作为`selector`和参数打包编码,传给`call`函数:
##### 固定长度类型参数
固定长度的参数类型通常为固定长度的数组,例如:`uint256[5]`等。例如,如下函数`fixedSizeParamSelector`的参数为`uint256[3]`。因此,在计算该函数的`method id`时,只需要通过`bytes4(keccak256("fixedSizeParamSelector(uint256[3])"))`即可。

```solidity
function callWithSignature() external returns(bool, bytes memory){
(bool success, bytes memory data) = address(this).call(abi.encodeWithSelector(0x6a627842, 0x2c44b726ADF1963cA47Af88B284C06f30380fC78));
return(success, data);
    // fixed size(固定长度)类型参数selector
    // 输入: param1: [1,2,3]
    // fixedSizeParamSelector(uint256[3]) : 0xead6b8bd
    function fixedSizeParamSelector(uint256[3] memory param1) external returns(bytes4 selectorWithFixedSizeParam){
        emit SelectorEvent(this.fixedSizeParamSelector.selector);
        return bytes4(keccak256("fixedSizeParamSelector(uint256[3])"));
    }
```

##### 可变长度类型参数
可变长度参数类型通常为可变长的数组,例如:`address[]`、`uint8[]`、`string`等。例如,如下函数`nonFixedSizeParamSelector`的参数为`uint256[]`和`string`。因此,在计算该函数的`method id`时,只需要通过`bytes4(keccak256("nonFixedSizeParamSelector(uint256[],string)"))`即可。

```solidity
    // non-fixed size(可变长度)类型参数selector
    // 输入: param1: [1,2,3], param2: "abc"
    // nonFixedSizeParamSelector(uint256[],string) : 0xf0ca01de
    function nonFixedSizeParamSelector(uint256[] memory param1,string memory param2) external returns(bytes4 selectorWithNonFixedSizeParam){
        emit SelectorEvent(this.nonFixedSizeParamSelector.selector);
        return bytes4(keccak256("nonFixedSizeParamSelector(uint256[],string)"));
    }
```

##### 映射类型参数
映射类型参数通常有:`contract`、`enum`、`struct`等。在计算`method id`时,需要将该类型转化成为`ABI`类型。

例如,如下函数`mappingParamSelector`中`DemoContract`需要转化为`address`,结构体`User`需要转化为`tuple`类型`(uint256,bytes)`,枚举类型`School`需要转化为`uint8`。因此,计算该函数的`method id`的代码为`bytes4(keccak256("mappingParamSelector(address,(uint256,bytes),uint256[],uint8)"))`。

```solidity
contract DemoContract {
    // empty contract
}

contract Selector{
    // Struct User
    struct User {
        uint256 uid;
        bytes name;
    }
    // Enum School
    enum School { SCHOOL1, SCHOOL2, SCHOOL3 }
    ...
    // mapping(映射)类型参数selector
    // 输入:demo: 0x9D7f74d0C41E726EC95884E0e97Fa6129e3b5E99, user: [1, "0xa0b1"], count: [1,2,3], mySchool: 1
    // mappingParamSelector(address,(uint256,bytes),uint256[],uint8) : 0xe355b0ce
    function mappingParamSelector(DemoContract demo, User memory user, uint256[] memory count, School mySchool) external returns(bytes4 selectorWithMappingParam){
        emit SelectorEvent(this.mappingParamSelector.selector);
        return bytes4(keccak256("mappingParamSelector(address,(uint256,bytes),uint256[],uint8)"));
    }
    ...
}
```

### 使用selector

我们可以利用`selector`来调用目标函数。例如我想调用`elementaryParamSelector`函数,我只需要利用`abi.encodeWithSelector`将`elementaryParamSelector`函数的`method id`作为`selector`和参数打包编码,传给`call`函数:

```solidity
// 使用selector来调用函数
function callWithSignature() external{
...
// 调用elementaryParamSelector函数
(bool success1, bytes memory data1) = address(this).call(abi.encodeWithSelector(0x3ec37834, 1, 0));
...
}
```

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

![logs in remix](./img/29-3.png)

Expand Down