Monday, September 16, 2019

Ethereum contract transactions decoding


Ethereum is a platform for decentralized applications.
It is based on a database containing blocks. The first block is the genesis block, and it is followed by newer blocks. Each block contains, among other, set of transactions that were committed.

The transactions could be ether transfer from one account to another, but the transactions could also represent a contract method activation.

Now, let's assume you have a contract, and an access to the web3 API for the ethereum, and you want to view the transactions related to activation of a specific contract method.

How can we get the method activation history?

We can scan the ethereum blocks, and get transaction from each block:
const Web3 = require('web3');
const web3 = new Web3('ws://127.0.0.1:7475');
// assumming we have a blockNumber
const block = await web3.eth.getBlock(blockNumber, true);
block.transactions.forEach(async (transaction) => {
 // assuming we want a specific contract instance
 if (transaction.to === myContractAddress) {
  // decode the transaction input
 }
});

But wait...
The transaction.input is a hex string of the related method data. How can we know which method is being called?

The answer is that the first 4 bytes of the transaction.input are the hash code of the method. I've tried several ways to get this hash, and found the easiest is to use ethereum web3 API to create hash using dummy variables. Notice that the web3 API creates hash including the actual arguments data sent, but we are only interested in the method hash, so we use the 10 first chars.
(10 chars is 4 bytes, for example: 0x12345678)

const contract = new web3.eth.Contract(abi, contractAddress);
const methods = contract.methods;
const hashes = {};

// example for activation of function with 2 ints arguments
let hash = methods['MyMethod1'](1, 2).encodeABI();
hashes[hash.substring(0, 10)] = 'MyMethod1';

// example for activation of function with address argument
const dummyAddress = '0x7d6BeA6003c1c8944cAECe5b4dde4831180A1eC2';
hash = methods['MyMethod2'](dummyAddress).encodeABI()
hashes[hash.substring(0, 10)] = 'MyMethod2';


Once we have all our methods of interest in the "hashes" variable, we can locate the transactions related to these method hashes:

// decode the transaction input
if (transaction.input.length > 10) {
 const hash = transaction.input.substring(0, 10);
 if (hashes[hash]) {
  console.log(`method ${hashes[hash]} located`);
 }
}

Next we use this information for any purpose we'll choose.

1 comment:

  1. Great article with excellent idea!Thank you for such a valuable article. I really appreciate for this great information.. crypto mining tools

    ReplyDelete