How to Decode Raw Blockchain Event Logs with GoldRush Decoder

Learn how to turn raw event logs into structured data with GoldRush Decoder, a scalable solution for human-readable data.

Introduction

In this tutorial, we'll walk through on how to decode a Compound V2 cETH Mint Transaction using the innovative GoldRush Decoder. This process is useful for anyone looking to understand complex transaction events, whether for blockchain analytics, creating GoldRush UI components, or any other use case. We'll start with outlining the essential tools and dependencies needed for this task, followed by a step-by-step guide through the decoding process, ensuring that you have a clear and concise understanding of each phase.

Prerequisites

Tutorial: Using GoldRush Decoder to Decode a Compound V2 cETH Mint Transaction

1

Protocol Configuration

Looking at the README from the GoldRush Decoder repo, we know the first step in order to decode cETH events is to open a terminal in your IDE and run the following:
yarn add-config
2
In the terminal, you should see a prompt asking for the protocol name which in this case will be compound-v2.
3
The program will recognize the protocol does not currently exist so it will create a new config template. It will ask for the contract address which in this case is 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5.
4
It will then ask if this contract is a Factory Address, which is defined as a contract that is responsible for deploying other contracts for the protocol (e.g. Uniswap Factory Contract, Compound Deployer, etc.). In this case, we will be selecting no.
5
Lastly, it will ask for the chain name the contract is deployed on, which in this case will be eth-mainnet For a reference with all the supported chain names, please check out the supported networks page on the GoldRush docs.
6
Finally, the program will have created a new folder with a decoder, test and folder for contract abis.
7
Check the configs file to understand the structure of the information we just provided.
const configs: Configs = [
    {
        address: "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
        is_factory: false,
        protocol_name: "compound-v2",
        chain_name: "eth-mainnet",
    },
];

export default configs;
8

Acquire and Set Up ABI

Once we have the protocol folder set up we will need the ABI for the contract we want to decode events for (Note: Many contracts are implemented as proxies of other contracts so make sure the ABI you are using has the event of interest).
9
In order to find the ABI, you can go to the block explorer, input the contract address, and click the contract field (usually next to transactions, token transfers etc.)
10
Scroll down to the ABI section, copy the entirety of it, and paste it into the compound-v2.abi.json file in JSON format.
11
Lastly, we are going to rename the ABI file be the same name as the contract which is CEther.
12

Modify the Decoder File

Now that we have the ABI in place, we can begin modifying the compound-v2.decoder.ts file.
13
Open up the file and make sure the ABI is pointing to the right file at the top.
14
We can replace the <EVENT NAME> portion of the code with the name of the event we are looking to decode. It should be spelt in the exact same format as it is in the ABI, except in the return function where it can be formatted in a more UI-friendly manner. In our case, we will be adding in the “Mint” value.
15
In the args parameter we want to list out the topic/data field names and types in the order the appear in the ABI, the finished code thus far will look like this:
import { GoldRushDecoder } from "../../decoder";
import { type EventType } from "../../decoder.types";
import {
    DECODED_ACTION,
    DECODED_EVENT_CATEGORY,
} from "../../decoder.constants";
import { decodeEventLog, type Abi } from "viem";
import CEtherABI from "./abis/compound-v2.CEther.abi.json";

GoldRushDecoder.on(
    "compound-v2:Mint",
    ["eth-mainnet"],
    CEtherABI as Abi,
    async (log_event, tx, chain_name, covalent_client): Promise<EventType> => {
        const { raw_log_data, raw_log_topics } = log_event;

        const { args: decoded } = decodeEventLog({
            abi: CEtherABI,
            topics: raw_log_topics as [],
            data: raw_log_data as `0x${string}`,
            eventName: "Mint",
        }) as {
            eventName: "Mint";
            args: {
                minter: string;
                mintAmount: bigint;
                mintTokens: bigint;
            };
        };

        return {
            action: DECODED_ACTION.SWAPPED,
            category: DECODED_EVENT_CATEGORY.DEX,
            name: "Mint",
            protocol: {
                logo: log_event.sender_logo_url as string,
                name: log_event.sender_name as string,
            },
        };
    }
);
16
Now we will construct the details and Token constant variables.
It is worth checking the README again to see the standardized variables format for ERC-20 token information along with the details constant variable format for any information that does not fit into tokens or nft.
We will only be using the details constant for this example, but within the repo there are many examples of other constants being used.
For the Event details constant we have to specify the heading of each data field, reference the appropriate field in the decoded object and the appropriate data type (will be either address or test).
Once the details constant is defined we want to pass it in the return object after protocol and modify the action and category to the most relevant types (the different constants for actions and category can be found in decoder.constants.ts ).
Finally the finished code will look like this:
import { GoldRushDecoder } from "../../decoder";
import type { EventDetails } from "../../decoder.types";
import { type EventType } from "../../decoder.types";
import {
    DECODED_ACTION,
    DECODED_EVENT_CATEGORY,
} from "../../decoder.constants";
import { decodeEventLog, type Abi } from "viem";
import CEtherABI from "./abis/compound-v2.CEther.abi.json";

GoldRushDecoder.on(
    "compound-v2:Mint",
    ["eth-mainnet"],
    CEtherABI as Abi,
    async (log_event, tx, chain_name, covalent_client): Promise<EventType> => {
        const { raw_log_data, raw_log_topics } = log_event;

        const { args: decoded } = decodeEventLog({
            abi: CEtherABI,
            topics: raw_log_topics as [],
            data: raw_log_data as `0x${string}`,
            eventName: "Mint",
        }) as {
            eventName: "Mint";
            args: {
                minter: string;
                mintAmount: bigint;
                mintTokens: bigint;
            };
        };

        const details: EventDetails = [
            {
                heading: "Minter",
                value: decoded.minter,
                type: "address",
            },
            {
                heading: "Mint Amount",
                value: decoded.mintAmount.toString(),
                type: "text",
            },
            {
                heading: "Mint Tokens",
                value: decoded.mintTokens.toString(),
                type: "text",
            },
        ];

        return {
            action: DECODED_ACTION.TRANSFERRED,
            category: DECODED_EVENT_CATEGORY.LENDING,
            name: "Mint",
            protocol: {
                logo: log_event.sender_logo_url as string,
                name: log_event.sender_name as string,
            },
            details,
        };
    }
);
17

Prepare API Request in Postman

Once the decoding logic is inserted, we can check to see if the response is appropriate. Save the decoder file and open up Postman.
18
We will be making a POST request to the URL: http://localhost:8080/api/v1/tx/decode and we want to add a header called x-covalent-api-key in which the GoldRush API key will be passed.
The body of the request should be:
{
    "chain_name": "<INSERT CHAIN NAME HERE>",
    "tx_hash": "<INSERT TRANSACTION HASH HERE>"
}
19
For example:
{
    "chain_name": "eth-mainnet",
    "tx_hash": "0xf49858398e325a1b2c71795c3615f42242d5a6fe6ccc5f23ea128854b23632a1"
}
20
In the decoder terminal, type in yarn dev and the program will evaluate the decoder. If there are no errors we can make the API request, and the response for the log event in question should be the decoded response.
21
Looking through the entire decoded transaction, we can see the transfers of WETH and CEther already decoded as well due to the ERC-20 standard being decoded in the fallbacks folder. That means we do not have to decode the transfers of assets in the Mint event decoder as they will already be decoded with the appropriate metadata and USD values.
22

Run the Decoder and Test

Finally, we will be adding a test for the event we just decoded in the compound-v2-tests.ts.
23
We can replace <EVENT NAME> with the name of the event we passed in the return function in the decoder file and we can replace <ENTER TX HASH FOR TESTING> with the transaction hash we used in API request above.
24
We want to replace the last two lines of the test from this:
const testAdded: boolean = false;
expect(testAdded).toEqual(true); 
25
To this:
expect(event?.details?.length).toEqual(3);
26
Note: We want to add individual tests for each constant that is returned by the decoding function. For example, if our response, in addition to the 3 values in details also returned 2 token values and 1 NFT value, our tests would look like this:
expect(event?.details?.length).toEqual(3);
expect(event?.tokens?.length).toEqual(2);
expect(event?.nfts?.length).toEqual(1);
27
The finished code for the compound test will look like:
import request from "supertest";
import app from "../../../../api";
import { type EventType } from "../../decoder.types";

describe("compound-v2", () => {
    test("eth-mainnet:Mint", async () => {
        const res = await request(app)
            .post("/api/v1/tx/decode")
            .set({ "x-covalent-api-key": process.env.TEST_COVALENT_API_KEY })
            .send({
                chain_name: "eth-mainnet",
                tx_hash:
                    "0xf49858398e325a1b2c71795c3615f42242d5a6fe6ccc5f23ea128854b23632a1",
            });
        const { events } = res.body as { events: EventType[] };
        const event = events.find(({ name }) => name === "Mint");
        if (!event) {
            throw Error("Event not found");
        }
        expect(event?.details?.length).toEqual(3);
    });
});
28
Once the test has been configured, we want to run the test locally and then make a PR to the main branch.
To run the tests locally run the following command in your terminal:
yarn jest services/decoder/protocols/compound-v2/compound-v2.test.ts
29
If the tests pass, we can make more decoding functions and tests and then push the changes to a PR!

Conclusion

This tutorial has provided a detailed guide on using the GoldRush Decoder to decode a Compound V2 cETH Mint Transaction. The process involves setting up the necessary environment, fetching and configuring the ABI, modifying the decoder file, and finally testing the decoding logic using Postman. By following these steps, you can successfully use GoldRush Decoder to decode blockchain events and gain deeper insights into transaction data!

Read more