【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最原始的方式来调用,也是最麻烦的方法
- 必须准备bytecode, 格式就是function + params
所以第一步就是调用getFunctionBytes4, 然后加上getEncodedParams返回的字符,getEncodedParams前面的0x必须去除。 - 准备好了bytecode接着就是调用callFunction,输入ContractA合约地址和bytecode, 最后就完成了
- 当然也可以直接在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)); } }
结果就如是以下
Facebook评论