4.3 合约编码方法和工具

合约的调用和查询,传入的参数是根据合约ABICode编码后函数和函数参数的data。详细可参考solidity - Application Binary Interface Specification下面介绍几种方式取得合约ABICode方法调用时对应的编码data。

4.3.1 使用remix调用合约获取data

在使用remix执行合约方法时,每次交易都有对应的交易详情,可以在控制台看到交易信息里的函数调用的input,以及对应的decodeInput和decodeOutput。input就是调用函数时向区块链环境发送的JSON里的data。对应开放平台,在使用合约调用时,传入的data可以直接使用remix的data。remix input data

4.3.2 使用ethers.js实现编码和解码

ethers.js是一个以太坊的js依赖包。里面提供了根据ABICode编码函数名的方法和编码参数的静态方法。例:

  1. //HelloWorld.sol
  2. pragma solidity^0.4.23;
  3. contract HelloWorld {
  4. address public owner;
  5. string public info;
  6. constructor(address _owner) public {
  7. owner = _owner;
  8. }
  9. function saySomething(string _str) public returns(string) {
  10. info = _str;
  11. return info;
  12. }
  13. }
  1. //编码function saySomething函数的 abi
  2. var ethers = require('ethers')
  3. var abi = [
  4. {
  5. "constant": false,
  6. "inputs": [
  7. {
  8. "name": "_str",
  9. "type": "string"
  10. }
  11. ],
  12. "name": "saySomething",
  13. "outputs": [
  14. {
  15. "name": "",
  16. "type": "string"
  17. }
  18. ],
  19. "payable": false,
  20. "stateMutability": "nonpayable",
  21. "type": "function"
  22. },
  23. {
  24. "inputs": [
  25. {
  26. "name": "_owner",
  27. "type": "address"
  28. }
  29. ],
  30. "payable": false,
  31. "stateMutability": "nonpayable",
  32. "type": "constructor"
  33. },
  34. {
  35. "constant": true,
  36. "inputs": [],
  37. "name": "info",
  38. "outputs": [
  39. {
  40. "name": "",
  41. "type": "string"
  42. }
  43. ],
  44. "payable": false,
  45. "stateMutability": "view",
  46. "type": "function"
  47. },
  48. {
  49. "constant": true,
  50. "inputs": [],
  51. "name": "owner",
  52. "outputs": [
  53. {
  54. "name": "",
  55. "type": "address"
  56. }
  57. ],
  58. "payable": false,
  59. "stateMutability": "view",
  60. "type": "function"
  61. }
  62. ];
  63. var interface = new ethers.Interface(abi)
  64. var abiInstance = interface.functions[abi[0].name] // abi[0]即saySomething function的abi
  65. return abiInstance.sighash // 0xfe6b3783

获取函数参数编码data的方法:使用ethers.utils.ABICoder的encode方法,如

  1. // 编码 function saySomething 的输入参数 'Hello World'的data
  2. var abiCoder = new ethers.utils.AbiCoder()
  3. return abiCoder.encode(['string'], ['Hello World'])
  4. //0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000

实际调用函数的input即为:函数saySomething encode的前八位 + 函数输入参数的encode。结果为

  1. 0xfe6b37830000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000

可对比上面使用remix调用方法时的input值,两者结果一致。

合约执行结束后,info值被设置为 "Hello World"。下面请求constant方法(同8.3 Demo合约查询),根据结果解析info值。

  1. curl -k -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x7eff122b94897ea5b0e2a9abf47b86337fafebdc","to":"0xd5b0df861803a07f330868104eec92ebdcce4c79","data":"0x370158ea"}, "latest"],"id":1}' https://sandbox-blockchain.xunlei.com/call
  2. {"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000"}
  1. var ethers = require('ethers')
  2. var outputTypes = ['string']
  3. var response = "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000"
  4. var abiCoder = new ethers.utils.AbiCoder()
  5. abiCoder.decode(outputTypes, response)
  6. // [ 'Hello World' ]

decode结果根据传入的outputTypes数组长度,解析对应的参数。