[Ethereum] How to determine the ABI to use for web3.eth.abi.decodeLog

logsweb3js

This is a follow up from How to identify token transfers looking at a transaction?.

I have transaction that results in a transfer of tokens.

From PancakeSwap: Router v2
To 0xd423fde466315d45fb0da457df28d5eeb92be580
For 0.005 ($2.97) Wrapped BNB (WBNB)

From 0xd423fde466315d45fb0da457df28d5eeb92be580
To 0xb54eb0cc6457e9d74e20672d8961fad3cde4511c
For 0.154823285118196657 ($2.94) Bogged Finan... (BOG)

I can access transaction logs using getTransactionReceipt, e.g.

{
  blockHash: '0x7a3e03a2eaaf39ba85e5513205545d2509c24e8a58f03bb77d82f136f7028697',
  blockNumber: 7460735,
  contractAddress: null,
  cumulativeGasUsed: '39606975',
  from: '0xb54eb0cc6457e9d74e20672d8961fad3cde4511c',
  gasUsed: '292724',
  logs: [
    {
      address: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
      topics: [
        '0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c',
        '0x00000000000000000000000010ed43c718714eb63d5aa57b78b54704e256024e'
      ],
      data: '0x0000000000000000000000000000000000000000000000000011c37937e08000',
      blockNumber: 7460735,
      transactionHash: '0xcf95271d63836f17c0c3a7beda7df2ea7029ce9161dc6808da86f5f9ce740830',
      transactionIndex: 265,
      blockHash: '0x7a3e03a2eaaf39ba85e5513205545d2509c24e8a58f03bb77d82f136f7028697',
      logIndex: 1007,
      removed: false,
      id: 'log_9d934f9a'
    },
    {
      address: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
      topics: [
        '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
        '0x00000000000000000000000010ed43c718714eb63d5aa57b78b54704e256024e',
        '0x000000000000000000000000d423fde466315d45fb0da457df28d5eeb92be580'
      ],
      data: '0x0000000000000000000000000000000000000000000000000011c37937e08000',
      blockNumber: 7460735,
      transactionHash: '0xcf95271d63836f17c0c3a7beda7df2ea7029ce9161dc6808da86f5f9ce740830',
      transactionIndex: 265,
      blockHash: '0x7a3e03a2eaaf39ba85e5513205545d2509c24e8a58f03bb77d82f136f7028697',
      logIndex: 1008,
      removed: false,
      id: 'log_f759d019'
    },
    {
      address: '0xD7B729ef857Aa773f47D37088A1181bB3fbF0099',
      topics: [
        '0x63be20cc905d5ba0cf65db405c4999f99f2575e521875f13a8009d6eed7a09d9'
      ],
      data: '0x000000000000000000000000d423fde466315d45fb0da457df28d5eeb92be580000000000000000000000000000000000000000000000000000aa739b8dd5525',
      blockNumber: 7460735,
      transactionHash: '0xcf95271d63836f17c0c3a7beda7df2ea7029ce9161dc6808da86f5f9ce740830',
      transactionIndex: 265,
      blockHash: '0x7a3e03a2eaaf39ba85e5513205545d2509c24e8a58f03bb77d82f136f7028697',
      logIndex: 1009,
      removed: false,
      id: 'log_e07c5a40'
    },
    {
      address: '0xD7B729ef857Aa773f47D37088A1181bB3fbF0099',
      topics: [
        '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
        '0x000000000000000000000000d423fde466315d45fb0da457df28d5eeb92be580',
        '0x000000000000000000000000b54eb0cc6457e9d74e20672d8961fad3cde4511c'
      ],
      data: '0x00000000000000000000000000000000000000000000000002260af5206ddbb1',
      blockNumber: 7460735,
      transactionHash: '0xcf95271d63836f17c0c3a7beda7df2ea7029ce9161dc6808da86f5f9ce740830',
      transactionIndex: 265,
      blockHash: '0x7a3e03a2eaaf39ba85e5513205545d2509c24e8a58f03bb77d82f136f7028697',
      logIndex: 1010,
      removed: false,
      id: 'log_4051d23e'
    },
    {
      address: '0xD423fDe466315D45fB0da457dF28D5EEB92be580',
      topics: [
        '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1'
      ],
      data: '0x0000000000000000000000000000000000000000000000257558ffc7c1bb7c9d0000000000000000000000000000000000000000000004a14e4339975756b2ea',
      blockNumber: 7460735,
      transactionHash: '0xcf95271d63836f17c0c3a7beda7df2ea7029ce9161dc6808da86f5f9ce740830',
      transactionIndex: 265,
      blockHash: '0x7a3e03a2eaaf39ba85e5513205545d2509c24e8a58f03bb77d82f136f7028697',
      logIndex: 1011,
      removed: false,
      id: 'log_382fd81b'
    },
    {
      address: '0xD423fDe466315D45fB0da457dF28D5EEB92be580',
      topics: [
        '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822',
        '0x00000000000000000000000010ed43c718714eb63d5aa57b78b54704e256024e',
        '0x000000000000000000000000b54eb0cc6457e9d74e20672d8961fad3cde4511c'
      ],
      data: '0x0000000000000000000000000000000000000000000000000011c37937e08000000000000000000000000000000000000000000000000000000aa739b8dd552600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000230b22ed94b30d7',
      blockNumber: 7460735,
      transactionHash: '0xcf95271d63836f17c0c3a7beda7df2ea7029ce9161dc6808da86f5f9ce740830',
      transactionIndex: 265,
      blockHash: '0x7a3e03a2eaaf39ba85e5513205545d2509c24e8a58f03bb77d82f136f7028697',
      logIndex: 1012,
      removed: false,
      id: 'log_abf4a762'
    }
  ],
  logsBloom: '0x002002000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000401000000000008000080000002000000000000000000004000080000000100000000000000002000000000000000000000000000000000000100000000000000000000000000040000000000000002400010000000800000040000000000000000c0000000000000000020000000000000000000400000000000000000000000002000000000000000000020080000000000000001000000000000080000080000000000000020000000000000000000000000000400000000000000000',
  status: true,
  to: '0x10ed43c718714eb63d5aa57b78b54704e256024e',
  transactionHash: '0xcf95271d63836f17c0c3a7beda7df2ea7029ce9161dc6808da86f5f9ce740830',
  transactionIndex: 265
}

As is, events are not readable; I need to decode them, and as far as I understand, I should be using web3.eth.abi.decodeLog(inputs, hexString, topics). However, I am not clear on how do I get the inputs value.

As far as I understand, it needs to be ABI of the contract that produced the event, e.g. In case of event:

{
  address: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
  topics: [
    '0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c',
    '0x00000000000000000000000010ed43c718714eb63d5aa57b78b54704e256024e'
  ],
  data: '0x0000000000000000000000000000000000000000000000000011c37937e08000',
  blockNumber: 7460735,
  transactionHash: '0xcf95271d63836f17c0c3a7beda7df2ea7029ce9161dc6808da86f5f9ce740830',
  transactionIndex: 265,
  blockHash: '0x7a3e03a2eaaf39ba85e5513205545d2509c24e8a58f03bb77d82f136f7028697',
  logIndex: 1007,
  removed: false,
  id: 'log_9d934f9a'
}

I need to get ABI of contract 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c.

I have tried:

web3.eth.abi.decodeLog(
  abi,
  '0x0000000000000000000000000000000000000000000000000011c37937e08000',
  [
    '0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c',
    '0x00000000000000000000000010ed43c718714eb63d5aa57b78b54704e256024e',
  ],
);

However, this produced error:

Error: invalid type (argument="type", value="_factory", code=INVALID_ARGUMENT, version=abi/5.0.7)

It seems like I am providing wrong value.

How do I know what ABI do I use to decode this log?

Best Answer

This isn't a complete answer, but here is what I have so far.

If you look at the contract 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c code, it looks like it implements BEP20 interface. I don't know how to determine this programmatically, but that seems to be the case.

I looked up BEP20 ABI and used it to construct web3.eth.Contract.

const contract = new web3.eth.Contract(bep20, '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c');

I then extracted ABI tokens that describe events:

const knownEventTokens = contract.options.jsonInterface.filter((token) => {
  return token.type === 'event';
});

If you explore the value of knownEventTokens, tokens now have a signature property:

[
  {
    anonymous: false,
    inputs: [
      { indexed: true, name: 'owner', type: 'address' },
      { indexed: true, name: 'spender', type: 'address' },
      { indexed: false, name: 'value', type: 'uint256' }
    ],
    name: 'Approval',
    type: 'event',
    constant: undefined,
    payable: undefined,
    signature: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925'
  },
  {
    anonymous: false,
    inputs: [
      { indexed: true, name: 'from', type: 'address' },
      { indexed: true, name: 'to', type: 'address' },
      { indexed: false, name: 'value', type: 'uint256' }
    ],
    name: 'Transfer',
    type: 'event',
    constant: undefined,
    payable: undefined,
    signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
  }
]

As far as I can tell, as long as the signature matches the first topic in a log event, then the inputs can be used to decode log event values, e.g.

const events = [
  {
    address: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
    blockHash: '0xbeecdeddd358fca1df274a2545ce5b55cf1938faa377b11c0f78c638d4734868',
    blockNumber: 7_463_306,
    data: '0x000000000000000000000000000000000000000000000000002386f26fc10000',
    id: 'log_041e02ed',
    logIndex: 143,
    removed: false,
    topics: [
      '0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c',
      '0x00000000000000000000000010ed43c718714eb63d5aa57b78b54704e256024e',
    ],
    transactionHash: '0xea00edb3e568701901b56c37c4b185be66dd5bc6398cf3f9298e8542091421f1',
    transactionIndex: 53,
  },
  {
    address: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
    blockHash: '0xbeecdeddd358fca1df274a2545ce5b55cf1938faa377b11c0f78c638d4734868',
    blockNumber: 7_463_306,
    data: '0x000000000000000000000000000000000000000000000000002386f26fc10000',
    id: 'log_cdebb74e',
    logIndex: 144,
    removed: false,
    topics: [
      '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
      '0x00000000000000000000000010ed43c718714eb63d5aa57b78b54704e256024e',
      '0x000000000000000000000000ed726e312afd8f856ab96170fd7225c8cb731f05',
    ],
    transactionHash: '0xea00edb3e568701901b56c37c4b185be66dd5bc6398cf3f9298e8542091421f1',
    transactionIndex: 53,
  },
  {
    address: '0xA528Ed9059e3D418324371C011b7bff8a1c838EB',
    blockHash: '0xbeecdeddd358fca1df274a2545ce5b55cf1938faa377b11c0f78c638d4734868',
    blockNumber: 7_463_306,
    data: '0x0000000000000000000000000000000000000000000000de45e400f02a502361',
    id: 'log_b6e3762a',
    logIndex: 145,
    removed: false,
    topics: [
      '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
      '0x000000000000000000000000ed726e312afd8f856ab96170fd7225c8cb731f05',
      '0x0000000000000000000000008e622e3c0c46b20e96ce61e3ce0180fd2fb5baa2',
    ],
    transactionHash: '0xea00edb3e568701901b56c37c4b185be66dd5bc6398cf3f9298e8542091421f1',
    transactionIndex: 53,
  },
  {
    address: '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    blockHash: '0xbeecdeddd358fca1df274a2545ce5b55cf1938faa377b11c0f78c638d4734868',
    blockNumber: 7_463_306,
    data: '0x00000000000000000000000000000000000000000000ca2de3365dab3b230ce40000000000000000000000000000000000000000000000001d24b2dfac520000',
    id: 'log_5e101161',
    logIndex: 146,
    removed: false,
    topics: [
      '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1',
    ],
    transactionHash: '0xea00edb3e568701901b56c37c4b185be66dd5bc6398cf3f9298e8542091421f1',
    transactionIndex: 53,
  },
  {
    address: '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    blockHash: '0xbeecdeddd358fca1df274a2545ce5b55cf1938faa377b11c0f78c638d4734868',
    blockNumber: 7_463_306,
    data: '0x00000000000000000000000000000000000000000000000bca378b643e734cc5000000000000000000000000000000000000000000000000002386f26fc100000000000000000000000000000000000000000000000000f6f852abb584590add0000000000000000000000000000000000000000000000000000000000000000',
    id: 'log_1b3a7700',
    logIndex: 147,
    removed: false,
    topics: [
      '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822',
      '0x00000000000000000000000010ed43c718714eb63d5aa57b78b54704e256024e',
      '0x0000000000000000000000008e622e3c0c46b20e96ce61e3ce0180fd2fb5baa2',
    ],
    transactionHash: '0xea00edb3e568701901b56c37c4b185be66dd5bc6398cf3f9298e8542091421f1',
    transactionIndex: 53,
  },
];

for (const event of events) {
  const eventToken = knownEventTokens.find((knownEventToken) => {
    return knownEventToken.signature === event.topics[0];
  });

  if (!eventToken) {
    console.log('cannot process log %d', event.logIndex);

    continue;
  }

  console.log({
    eventToken,
    values: web3.eth.abi.decodeLog(
      eventToken.inputs,
      event.data,
      event.topics.slice(1),
    ),
  });
}

Which gives:

cannot process log 143
{
  eventToken: {
    anonymous: false,
    inputs: [
      { indexed: true, name: 'from', type: 'address' },
      { indexed: true, name: 'to', type: 'address' },
      { indexed: false, name: 'value', type: 'uint256' }
    ],
    name: 'Transfer',
    type: 'event',
    constant: undefined,
    payable: undefined,
    signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
  },
  values: Result {
    '0': '0x10ED43C718714eb63d5aA57B78B54704E256024E',
    '1': '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    '2': '10000000000000000',
    __length__: 3,
    from: '0x10ED43C718714eb63d5aA57B78B54704E256024E',
    to: '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    value: '10000000000000000'
  }
}
{
  eventToken: {
    anonymous: false,
    inputs: [
      { indexed: true, name: 'from', type: 'address' },
      { indexed: true, name: 'to', type: 'address' },
      { indexed: false, name: 'value', type: 'uint256' }
    ],
    name: 'Transfer',
    type: 'event',
    constant: undefined,
    payable: undefined,
    signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
  },
  values: Result {
    '0': '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    '1': '0x8e622e3c0c46B20e96cE61E3Ce0180Fd2fb5bAA2',
    '2': '4100213335678329561953',
    __length__: 3,
    from: '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    to: '0x8e622e3c0c46B20e96cE61E3Ce0180Fd2fb5bAA2',
    value: '4100213335678329561953'
  }
}
cannot process log 146
cannot process log 147
https://quiet-black-snowflake.bsc.quiknode.pro/3650b4c6695bbc00b5aaf2bbc6bf794e447538a2/
(node:75758) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
^[[A^C
gke_applaudience_europe-west4-c_juice-boost (dxcoin) in dxcoin-api on master [$✘!+?] took 6m31s
12:18:41 ➜ babel-node src/bin/test.js | roarr
[
  {
    anonymous: false,
    inputs: [
      { indexed: true, name: 'owner', type: 'address' },
      { indexed: true, name: 'spender', type: 'address' },
      { indexed: false, name: 'value', type: 'uint256' }
    ],
    name: 'Approval',
    type: 'event',
    constant: undefined,
    payable: undefined,
    signature: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925'
  },
  {
    anonymous: false,
    inputs: [
      { indexed: true, name: 'from', type: 'address' },
      { indexed: true, name: 'to', type: 'address' },
      { indexed: false, name: 'value', type: 'uint256' }
    ],
    name: 'Transfer',
    type: 'event',
    constant: undefined,
    payable: undefined,
    signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
  }
]
cannot process log 143
{
  eventToken: {
    anonymous: false,
    inputs: [
      { indexed: true, name: 'from', type: 'address' },
      { indexed: true, name: 'to', type: 'address' },
      { indexed: false, name: 'value', type: 'uint256' }
    ],
    name: 'Transfer',
    type: 'event',
    constant: undefined,
    payable: undefined,
    signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
  },
  values: Result {
    '0': '0x10ED43C718714eb63d5aA57B78B54704E256024E',
    '1': '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    '2': '10000000000000000',
    __length__: 3,
    from: '0x10ED43C718714eb63d5aA57B78B54704E256024E',
    to: '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    value: '10000000000000000'
  }
}
{
  eventToken: {
    anonymous: false,
    inputs: [
      { indexed: true, name: 'from', type: 'address' },
      { indexed: true, name: 'to', type: 'address' },
      { indexed: false, name: 'value', type: 'uint256' }
    ],
    name: 'Transfer',
    type: 'event',
    constant: undefined,
    payable: undefined,
    signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
  },
  values: Result {
    '0': '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    '1': '0x8e622e3c0c46B20e96cE61E3Ce0180Fd2fb5bAA2',
    '2': '4100213335678329561953',
    __length__: 3,
    from: '0xeD726e312AfD8f856AB96170fd7225c8cb731f05',
    to: '0x8e622e3c0c46B20e96cE61E3Ce0180Fd2fb5bAA2',
    value: '4100213335678329561953'
  }
}

This is good enough for my use case. However, I do not understand how:

  1. How would one know which contract to use when processing an arbitrary event?
  2. In our case, we cannot process some log events. It seems like those events are not covered by BEP20. How do I find how to process them?
Related Topic