【Solidity】EVM、ABI、Call、Encode与Decode的关系

EVM就是类似JAVA虚拟机,开发过JAVA的同学应该都知道,无论任何的操作系统都必须装上JAVA虚拟机,否则将无法运行Java程式,那EVM也是一样的都必须装上那才能够运行Solidity的只能合约。

ABI就是可以让外部调用部署在网络中的智能合约function。ByteCode就是把我们的参数给编码(encode)然后和区块链网络交互。所有的ByteCode无论是提交或返回都需要进行encode和decode。外部访问ABI都可以使用不同的语言,比如前段的web3,ether库,php语言,或是golang语言。

以下4个范例是如何在Solidity当中从一个合约去调用另一个合约

准备先必须部署ContractA

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract ContractA {
    string public name;
    uint256 public number;

    function exec(string memory _name, uint256 _number)public returns(string memory,uint){
        name = _name;
        number = _number;
        return(name,number);
    }

    fallback() external {}
}

方法1:encodeWithSignature , 这是比较方便的方法,需要传入contractA的合约地址

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract ContractB {
    function callContractA(address _contractAddress, string memory _name, uint256 _number)public returns(bool){
        (bool success, ) = _contractAddress.call(
            abi.encodeWithSignature("exec(string,uint256)", _name,_number)
        );
        return success;
    }
}

方法2:encodePacked

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract ContractB {
    function callContractA(address _contractAddress, string memory _name, uint256 _number)public returns(bool){
        bytes4 sig = bytes4(keccak256("exec(string,uint256)"));
        bytes memory _bMessage = abi.encode(_name);
        bytes memory _bNum = abi.encode(_number);

        (bool success, ) = _contractAddress.call(
            abi.encodePacked(sig, _bMessage,_bNum)
        );
        return success;
    }
}

方法3:encodeWithSelector,只需要把function进行keccak编码

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract ContractB {
    function callContractA(address _contractAddress, string memory _name, uint256 _number)public returns(bool,bytes memory){
        bytes4 sig = bytes4(keccak256("exec(string,uint256)"));
        (bool success, bytes memory data) = _contractAddress.call(
            abi.encodeWithSelector(sig, _name, _number)
        );

        return (success,data);
    }
}

方法4:实例化contact, 直接调用contract当中的function

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;
import "./ContractA.sol";

contract ContractB {
    function callContractA(address _contractAddress, string memory _name, uint256 _number)public returns(string memory,uint256){
       ContractA _ContractA = ContractA(_contractAddress);
       (string memory _rName, uint256 _rNumber ) = _ContractA.exec(_name,_number);
        return (_rName,_rNumber);
    }
}


使用CallData最原始的方式来调用,也是最麻烦的方法

  1. 必须准备bytecode, 格式就是function + params
    所以第一步就是调用getFunctionBytes4, 然后加上getEncodedParams返回的字符,getEncodedParams前面的0x必须去除。
  2. 准备好了bytecode接着就是调用callFunction,输入ContractA合约地址和bytecode, 最后就完成了
  3. 当然也可以直接在ContractA调用CALLDATA,但是ContractA 必须写上fallback function 否则就无法调用了,以下是报错信息。
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract ContractB {

    function getFunctionBytes4()public pure returns(bytes4){
        return bytes4(keccak256("exec(string,uint256)"));
    }

    function getEncodedParams(string memory _name,uint256 _number)public pure returns(bytes memory){
        return abi.encode(_name,_number);
    }

    function callFunction(address contractAddress, bytes memory byteCode)public returns(bool){
        (bool isSuccess,) = contractAddress.call(byteCode);
        return isSuccess;
    }
}


合约ABI的encode和decode

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract EncodeDecode {

     struct Employee {
        uint[2] nums;//array必须放在前面,否则decode的时候name将无法decode, 不知道是什么原因
        string name;
    }


    function encode(uint age, uint[] calldata arr, Employee calldata emp)public pure returns(bytes memory){
        return abi.encode(age,arr,emp);
    }

    function decode(bytes calldata data)public pure returns(uint age ,uint[] memory ints ,Employee memory employeeData){
        (age,ints,employeeData) = abi.decode(data,(uint, uint[],Employee));
    }
}

结果就如是以下

Loading

Facebook评论