Skip to content

Commit

Permalink
Merge pull request #532 from isol4te/main
Browse files Browse the repository at this point in the history
Update 29: Add Calculating Method for Function Signatures with Various Parameter…
  • Loading branch information
AmazingAng authored May 12, 2024
2 parents 3e0ab22 + 5a22a2f commit 1405824
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 11 deletions.
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
Loading
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
Loading
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

0 comments on commit 1405824

Please sign in to comment.