Ethers.js – How to Decode a Bytes Value from Ethereum Event with Ethers.js

bytesdecodingethers.jseventsjavascript

I've got the following event declaration in my Solidity contract:

    event RemoteOrderCreated(bytes32 indexed id, uint32 indexed nonce, address indexed sender, bytes input);

And I capture the event like so:

contract.on('eventName', async (...args) => {
  // here I have all the event args
})

For this sample transaction (sepolia block explorer link) the args looks like this:

[
  '0x1ae0b4b1df473f1aea6c8970adf5347aa0eaf656ba1c6888e4876c889488ef18',
  4502989,
  '0xCbeeBE266ab8d734a8ccd90097bF87809893977B',
  '0x030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e8d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a'
]

Value at index 0 is the id from the event, at index 1 is the nonce, at index 2 is the sender and at index 3 is the input prop.

This is the part of the ABI relating to the event declaration:

{
  "_format": "hh-sol-artifact-1",
  "contractName": "MyContract",
  "sourceName": "contracts/MyContract.sol",
  "abi": [
    {
      "inputs": [
        // inputs
      ],
      "stateMutability": "nonpayable",
      "type": "constructor"
    },
    // HERE is the event declaraion:
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "bytes32",
          "name": "id",
          "type": "bytes32"
        },
        {
          "indexed": true,
          "internalType": "uint32",
          "name": "nonce",
          "type": "uint32"
        },
        {
          "indexed": true,
          "internalType": "address",
          "name": "sender",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "bytes",
          "name": "input",
          "type": "bytes"
        }
      ],
      "name": "RemoteOrderCreated",
      "type": "event"
    },
    // the rest of the ABI
 ]
}

I need to decode the input, but I'm not being successful.
I tried with ethers.utils.defaultAbiCoder.decode() in different combinations of: providing the contract ABI, or just specific parts of it (relating to the event itself), and the input value that I want to decode.

I also tried with simple bytes decoding, but to no avail.

Is there a standard way of decoding bytes values from events/transactions?

Best Answer

Okay, thru a bunch of research I found out that when it's not a specific type of bytes type, eg. bytes4 or bytes32 etc..., but just bytes, then you need to figure out what is the structure of the encoded bytes string.

In my case, it's the following:

(bytes4, uint32, bytes32, uint256, address, uint256, uint256)

I got that from the Solidity contract where the input is being encoded before emitted with the event.

So, the JS solution is as follows:

decodedInput = ethers.utils.defaultAbiCoder.decode(
  ['bytes4', 'uint32', 'bytes32', 'uint256', 'address', 'uint256', 'uint256'],
  input
)
Related Topic