Detailed explanation of contract calls in Solidity

Recently, I am doing my graduation project and building a detector based on Slither. I need to model the contract calling paradigm in solidity. I have made many attempts on contract calling on remix qwq. It makes me cry if I talk too much. So the system summary one time.
solidity环境:0.8.13

1. Use the call function to make calls within the contract.

Here we do not consider using the call function to transfer funds, but only consider using the call function to make function calls between contracts.
When the call() function calls the functions of other contracts or this contract, it needs the corresponding function identifier for the call.
Function identifiers are generated by abi encoding functions.
The environment here should, if nothing else, only support encodeWithSignature,encodeWithSelector

  • Basic calling method:
  1. encodeWithSignature
    After keccak256 operation is performed on the function signature, the result of the first four bytes is taken. The remaining bytes are the result of encode(), and each data is filled into 32 bytes.
bytes memory method = abi.encodeWithSignature("函数名(参数列表)", 对应的参数列表);
(bool , bytes memory returnData)= address(this) .call(method);

Here we take calling a function in its own contract as an example to illustrate. An example is given and tested on remix.

pragma solidity ^0.8.13;
//基合约实现
contract TestFallback {
    string message;
    address add;
    uint inter;
    //构造函数,初始化状态变量message
    constructor() {
        message = "hello";
    }
    fallback() external {
        message = "fallback";
    }
    function testFallbackWithParam(string memory _message) external returns (bytes memory) {
        bytes memory method = abi.encodeWithSignature("setMsg(uint)",_message);
        (bool success, bytes memory returnData) = address(this).call(method);

        require(success, "set fail");
        return returnData;
    }
    function setAddr(address a1) external {
        add = a1;
    }

    function getMsg() external view returns (string memory) {
        return message;
    }
    function setMsg(string memory _message) external {
        message = _message;
    }
  1. encodeWithSelector
    It is very similar to the above. The former can be considered as the abbreviation of the latter, because it will automatically perform keccak256 operation on the function signature and then take the first four bytes. But encodeWithSelector will not.
    The calling method is as follows:
bytes memory method = abi.encodeWithSignature(bytes4(keccak256("函数名(参数列表)")), 对应的参数列表);
(bool , bytes memory returnData)= address(this) .call(method);

Because the usage of this function is exactly the same as above, the example on remix is ​​not posted here.

Here I share a pitfall that I stepped on and wasted my whole day qwq

contract B{
    function toeat(address _add , uint a) public{
        bytes memory method =abi.encodeWithSignature("eat(uint)",a);
        (bool answer, bytes memory value) = address(cat(_add)).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
    }
}

Here I want to call the eat() function in the cat contract. When encoding, the function name (parameter list) is written eat(uint). The problem lies here. Solidity sometimes defaults to uint which is uint256, but here it doesn’t know whether to default to uint256! ! ! ! ! ! ! ! ! , when calling the toeat() function, it will always report an error, or call the fallback function, which is very embarrassing,
so it should be changed to:

contract B{
    function toeat(address _add , uint a) public{
        bytes memory method =abi.encodeWithSignature("eat(uint256)",a);
        (bool answer, bytes memory value) = address(cat(_add)).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
    }
}

2. Use the call function to make function calls between contracts

Without further ado, let’s start with the code, which has been tested on remix:

pragma solidity ^0.8.13;

import "./testFallbackImport3.sol" ;

contract Animal{
    cat c;
    constructor(address _add){
        c = cat(_add);
    }
    function test()public view returns(uint) { //普通实例化合约调用
       return c.eat(1);
   }
    function test2()external returns(bool) {  //通过call方法调用
        bytes memory method =abi.encodeWithSignature("use(uint256)",2);
        (bool answer, bytes memory value) = address(c).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
} }

contract test{
    function toeat (address _add , uint a) public{
        bytes memory method =abi.encodeWithSignature("eat(uint256)",a);
        (bool answer, bytes memory value) = address(cat(_add)).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
    }

    function toeat1(address _add , uint a) public{
        cat chu = cat(_add);
        bytes memory method =abi.encodeWithSignature("eat(uint256)",a);
        (bool answer, bytes memory value) = address(chu).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
    }

    function toeat2(cat _add , uint a) public{
        bytes memory method =abi.encodeWithSignature("eat(uint256)",a);
        (bool answer, bytes memory value) = address(_add).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
    }

}

test contract, the three functions represent the three ways to use the call function to call the contract:
toeat()
pass in the address of the target contract, generate a reference to the target contract, and then call the target function

    function toeat (address _add , uint a) public{
        bytes memory method =abi.encodeWithSignature("eat(uint256)",a);
        (bool answer, bytes memory value) = address(cat(_add)).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
    }

toeat1()
Create a contract variable and then use it to call the target function

    function toeat1(address _add , uint a) public{
        cat chu = cat(_add);
        bytes memory method =abi.encodeWithSignature("eat(uint256)",a);
        (bool answer, bytes memory value) = address(chu).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
    }

toeat2()
Pass in the contract variable and use the address type to make the call

    function toeat2(cat _add , uint a) public{
        bytes memory method =abi.encodeWithSignature("eat(uint256)",a);
        (bool answer, bytes memory value) = address(_add).call(method);  //通过bytes32(keccak256("eat()"指定方法,后面的是参数
        require(answer, "call failed");
    }

Guess you like

Origin blog.csdn.net/m0_53689197/article/details/129721360