Ethers.js – Reading Transactions from Arbitrum Sequencer Feed: A Comprehensive Guide

arbitrumethers.jsjavascriptnodejs

I try to read transactions from arbitrum sequencer feed and decode the transaction data to get information about the transaction, therefore I try to check which function is called for the transaction and decode it with the abis, but I cannot find the functions in the abis…
Thats what I did so far:
I do have a websocket to get messages from the sequencer websocket:

provider_seq.on("message", (data) => {
   let Ret = read_mess(data);
});

In my read_mess(data) function I am reading the transactions and it looks like this:

function read_mess(m) {
  let messages = JSON.parse(m).messages;
  if (!messages) return;
  let L1MessageType_L2Message = 3;
  let l2Msg = messages[0].message.message.l2Msg;
  let base64 = ethers.utils.base64.decode(l2Msg);
  let firstByte = base64[0];
  let L2MessageKind_SignedTx = 4;
  if (firstByte != L2MessageKind_SignedTx) {
    return;
  }

  try {
    let tx = ethers.utils.parseTransaction(base64.slice(1));
    let router = tx.to;
    switch (router) {
      case dex_details.Camelot.router_addr: {
        decodeTransactionDataV2(tx.data, "Camelot");
      }
      case dex_details.UniV3.router_addr: {
        decodeTransactionDataV3(tx.data, "UniV3");
      }
    }
  } catch (e) {
    console.log(e);
  }
  return "";
}

Everything works so far, I get a transaction (For example on Camelot Dex here) looking like this:

{
  type: 2,
  chainId: 42161,
  nonce: 24,
  maxPriorityFeePerGas: BigNumber { _hex: '0x9502f900', _isBigNumber: true },
  maxFeePerGas: BigNumber { _hex: '0xa0eebb00', _isBigNumber: true },
  gasPrice: null,
  gasLimit: BigNumber { _hex: '0x6acfc0', _isBigNumber: true },
  to: '0xc873fEcbd354f5A56E00E710B90EF4201db2448d',
  value: BigNumber { _hex: '0x04d4863d82a59ac8', _isBigNumber: true },
  data: '0xb4822be300000000000000000000000000000000000000012cb3eca3eade0db259bf000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000001b60894c2e7f2c64990d06ce866cadd2f8f0d8400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064f822db000000000000000000000000000000000000000000000000000000000000000200000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000b9af4762c039d63e30039f1712dfab77026408c7',
  accessList: [],
  hash: '0x64962c6a4d858607dbb1d60a50344792ffbfdd430b9c14f551ac4ee76a8a5965',
  v: 1,
  r: '0x5b762cc21a03af9b9288d6d817637cfaaffe4d280256bf23ac30f69c5d0b7c9b',
  s: '0x4e58678acfaf61fdfcf5ab949b7cdc2f3b9aa19f67f91a9b052e92025c1018b3',
  from: '0x01B60894c2e7f2c64990D06Ce866cADd2F8f0d84'
}

But now I try to decode the transaction data to get the traded amount and the tokens (And for uniV3 the pool fee). For Camelot it looks like this:

const selector_function_mapping_v2 = {
  "0x38ed1739": "swapExactTokensForTokens",
  "0x8803dbee": "swapTokensForExactTokens",
  "0x7ff36ab5": "swapExactETHForTokens",
  "0xd06ca61f": "swapTokensForExactETH",
  "0x18cbafe5": "swapExactTokensForETH",
  "0xc02aaa39": "swapETHForExactTokens",
  "0xd1154596": "swapExactTokensForTokensSupportingFeeOnTransferTokens",
  "0x24e412a0": "swapExactTokensForETHSupportingFeeOnTransferTokens",
  "0xb3f15a7d": "swapExactETHForTokensSupportingFeeOnTransferTokens",
};
async function decodeTransactionDataV2(transactionData, dex) {
  const functionSelector = transactionData.slice(0, 10);
  let decodedData = await router_camelot.interface.decodeFunctionData(
    selector_function_mapping_v2.functionSelector,
    transactionData
  );
  const decodedFunctionData =
    dex_details[dex].router.interface.decodeFunctionData(decodedData);
  console.log(decodedFunctionData);
}

But now I am only getting functionSelector like this 0xac3893ba, what is not in my dictionary. But this should be right (If chatGPT haven't done something wrong…).
Can someone help me out with this?

Thank you!

Best Answer

I suppose that you are trying to build a MEV bot :), good luck with that and let me help you at least with this problem.

When you create a simple bot, you should do it in a way that it filters all the transactions and only store the ones with a specific to field. E.g. In your case, the transaction data that you are showing in your post has a to field. This address corresponds to the Camelot Router address. So suppose that you are building your bot to analyze all the transactions sent to the Camelot Router. These are the steps:

  1. Get the data from the sequencer feed

  2. Check if transaction.to == 0xc873fEcbd354f5A56E00E710B90EF4201db2448d (Camelot Router)

  3. Go to arbiscan.io and save the ABI that corresponds to Camelot Router. (here you have it)

  4. Inside your code create an interface for the Camelot Router

const camelotRouterInterface = new ethers.utils.Interface(camelotRouterInterfaceABI)

  1. Use the function parseTransaction(transaction) and pass the transaction with the encoded data. This will return you the decoded data based on the ABI of the Camelot Router, so you don't have to manually search the function selector for each method!!

  2. Print the decoded transaction and find the fields that you need.

This should be enough to get the input data that were used to create that transaction. In the case of swapExactETHForTokensSupportingFeeOnTransferTokens you would get access to information such as uint amountOutMin, address[] calldata path, address to, address referrer ,uint deadline. If you want to get the address of the tokens or the LP address then extract the data from the path variable.

Good luck with your project! I hope that you found anything helpful from this.

Related Topic