使用ABI解码以太坊Transaction input数据,减少gas free

tech2022-09-25  107

在写智能合约的时候,之前我的习惯是把需要查询的数据记录在合约的event中,这些event的字段有些就是方法的入参。虽然以太坊log数据gas free相对较少,但积累起来就可观了。经过思考后,我给自己定了一些规则:

需要索引的字段,计算的中间结果,可以保留在event中无后台的DApp可以在event中保留更多的字段具有复杂功能(比如分账明细)的DApp,如需要多次查询大量数据,可以考虑使用后台来查询区块链。此时event中含有的函数入参就没有必要保留了,数据可以通过transaction的input字段恢复。

拿我发送的一个transaction为例

{ 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' }

可以看到很长的input字段,这个字段以0x70a1495c开头,这其实就是函数签名,后面的数据则是按类型把数据拼接起来而已。我们再来看这个函数的abi

{ 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' }

可以看到signature也是0x70a1495c 这个签名是怎么生成的呢,其实很简单:web3.eth.abi.encodeFunctionSignature("registerRoot(bytes,bool,bool,uint256,uint8)") 就是一个短Hash,去掉0x70a1495c后,我们把剩下数据解码:

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

最后再拼上字段名称就可以了。 自己写了个解析函数,如下:

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 }

可得到例如下面的结果:

{ name: '0x6f70656e31', isOpen: true, isCustom: true, cusPrice: '4000000000000000000', durationInYear: '1' }
最新回复(0)