ABI is used to decode Ethereum transaction input data to reduce gas free

Time:2020-11-12

When writing a smart contract, I used to record the data to be queried in the contract event. Some of the event fields are input parameters of methods. Although Ethereum log data gas free is relatively small, it can be accumulated significantly. After thinking about it, I set some rules for myself

  1. The fields to be indexed and the intermediate results of calculation can be kept in the event
  2. DAPP without background can keep more fields in event
  3. For DAPP with complex functions (such as Sub Ledger details), if you need to query a large amount of data multiple times, you can consider using the background to query the blockchain. At this time, the input parameters of the function contained in the event are not necessary to be retained, and the data can be recovered through the input field of transaction.

Take a transaction I sent as an example

{
  hash: '0xcc1c866186ff39555936ea007a63ead761aef80d4301eb4e0081e8fc8f6fe18d',
  nonce: 892,
  blockHash: '0xbfff2fc0dd268dfce90417a3ea3b5da3a9e59703d8d4ec6a5be3ba2dce59b924',
  blockNumber: 987,
  transactionIndex: 0,
  from: '0x40FB66078a2e688f83002930B7EbA981323d4bef',
  to: '0x2C71AC97716A17E66D7E524Cfeb28B97A3728250',
  value: '0',
  gas: 5000000,
  gasPrice: '10000000000',
  input: '0x70a1495c00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000003782dace9d900000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000056f70656e31000000000000000000000000000000000000000000000000000000',
  v: '0x1b',
  r: '0xbfbd3aef6c6777598847de0aa1ffcaf50470f785054771a54e2e274b89d1a633',
  s: '0x27aea35dd4d462598ac909b55444ec0131c7977fe2ad244eaac0cc28b70e07f1'
}

You can see a very long input field with0x70a1495cAt the beginning, this is actually the function signature, and the data behind is just splicing the data by type. Let’s look at the ABI of this function

{
  constant: false,
  inputs: [
    { internalType: 'bytes', name: 'name', type: 'bytes' },
    { internalType: 'bool', name: 'isOpen', type: 'bool' },
    { internalType: 'bool', name: 'isCustom', type: 'bool' },
    { internalType: 'uint256', name: 'cusPrice', type: 'uint256' },
    { internalType: 'uint8', name: 'durationInYear', type: 'uint8' }
  ],
  name: 'registerRoot',
  outputs: [],
  payable: false,
  stateMutability: 'nonpayable',
  type: 'function',
  signature: '0x70a1495c'
}

As you can see, so is signature0x70a1495c
How to generate this signature is very simple
web3.eth.abi.encodeFunctionSignature("registerRoot(bytes,bool,bool,uint256,uint8)")
It’s a short hash. Take it out0x70a1495cAfter that, we decode the rest of the data:

web3.eth.abi.decodeParameters(["bytes","bool","bool","uint256","uint8"],"0x00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000003782dace9d900000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000056f70656e31000000000000000000000000000000000000000000000000000000")
Result {
  '0': '0x6f70656e31',
  '1': true,
  '2': true,
  '3': '4000000000000000000',
  '4': '1',

Finally, spell the field name.
I wrote an analytic function, as follows:

async function decodeParamsOfTransaction(txHash, func_abi){
    var txData = await web3.eth.getTransaction(txHash);
    var input = txData.input;
    var types = func_abi.inputs.map(x=>x.internalType);
    var _d = "0x"+input.replace(func_abi.signature,"");
    var names = func_abi.inputs.map(x=>x.name);
    var r = web3.eth.abi.decodeParameters(types, _d);
    var dic = {}
    for(var i=0; i<names.length; i++){
        dic[names[i]] = r[i];
    }
    return dic
}

For example, the following results can be obtained:

{
  name: '0x6f70656e31',
  isOpen: true,
  isCustom: true,
  cusPrice: '4000000000000000000',
  durationInYear: '1'
}