Mirror provides two custom functions which can be in used within transforms to decode raw contract events during processing: _gs_fetch_abi and _gs_evm_decode

_gs_evm_decode

This function can decode data given a json string representing the ABI string. Since it is stateless, and will scale very well across different pipeline sizes.

It will automatically use the matching event in the ABI, but partial ABIs that only contain the target event will also work.

Function definition
_gs_evm_decode(string abi, string topics, string data)

Params

abi
string
required

A string representing a json array of events and functions

topics
string
required

Name of the column the dataset which contains a string containing the topics, comma separated.

data
string
required

Name of the column the dataset which contains a string containing the payload of the event.

Return

ROW ( event_param TEXT[], event_signature TEXT )
ROW
required

The function will output a nested ROW type with event_param::TEXT[] and event_signature::TEXT. If you’re planning on using a sink that doesn’t support nested ROWs, you may want to do a further transformation to unnest the result.

Examples

Function Call

If you were using the Raw Logs source dataset, you would call this function passing your ABI and using the ‘topics’ and ‘data’ columns. For example:

SELECT
 _gs_evm_decode('[
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "name": "src",
        "type": "address"
      },
      {
        "indexed": true,
        "name": "dst",
        "type": "address"
      },
      {
        "indexed": false,
        "name": "wad",
        "type": "uint256"
      }
    ],
    "name": "Transfer",
    "type": "event"
  }
]', `topics`, `data`) as decoded
from base.raw_logs

You would then able to access both topics and data from the decoded column as decoded.event_params and decoded.event_signature in a second transform. See below for a complete example pipeline.

_gs_fetch_abi

We provide a convenient function for fetching ABIs, as often they are too big to copy and paste into the yaml definition.

Function definition
_gs_fetch_abi(string url, string type)

Params

url
string
required

The URL from where the ABI will be fetched

type
string
required

The type of url. Two types of value are accepted:

  • etherscan for etherscan or etherscan-compatible APIs
  • raw for json array ABIs.

If you use etherscan-compatible APIs it’s highly recommended to include your own API key if you are using this function with a large pipeline. The amount of workers may result in the API limits being surpassed.

Examples

Function Call

Following up on the previous example, we can replace the raw ABI definition by a call to basescan using the _gs_evm_decode function:

select
    # Call the EVM decoding function
    _gs_evm_decode(
        # This fetches the ABI from basescan, a `etherscan` compatible site.
        _gs_fetch_abi('https://api.basescan.org/api?module=contract&action=getabi&address=0xcf205808ed36593aa40a44f10c7f7c2f67d4a4d4', 'etherscan'),
        `topics`,
        `data`
    ) as `decoded`,
from base.raw_logs

In some cases, you may prefer to host the ABI yourself and retrieve it from your a separate server such as Github Gist:

select
    # Call the EVM decoding function
    _gs_evm_decode(
        # This fetches the ABI from Github Gist
        _gs_fetch_abi('https://gist.github.com/JavierTrujilloG/bde43d5079ea5d03edcc68b4516fd297', 'raw'),
        `topics`,
        `data`
    ) as `decoded`,
from base.raw_logs

Pipeline Example

Below is an example pipeline definition for decoding events for Friendtech contract in Base, make sure to visit this guide for a more in-depth explanation of this pipeline:

name: friendtech-decoded-events
apiVersion: 3
sources:
  my_base_raw_logs:
    type: dataset
    dataset_name: base.raw_logs
    version: 1.0.0
transforms:
  friendtech_decoded:
    primary_key: id
    # Fetch the ABI from basescan, then use it to decode from the friendtech address.
    sql: >
      select 
        `id`,
        _gs_evm_decode(
            _gs_fetch_abi('https://api.basescan.org/api?module=contract&action=getabi&address=0xcf205808ed36593aa40a44f10c7f7c2f67d4a4d4', 'etherscan'), 
            `topics`, 
            `data`
        ) as `decoded`, 
        block_number, 
        transaction_hash 
      from my_base_raw_logs
      where address='0xcf205808ed36593aa40a44f10c7f7c2f67d4a4d4'
  friendtech_clean:
    primary_key: id
    # Clean up the previous transform, unnest the values from the `decoded` object.
    sql: >
      select 
        `id`, 
        decoded.event_params as `event_params`, 
        decoded.event_signature as `event_signature`,
        block_number,
        transaction_hash
      from friendtech_decoded 
      where decoded is not null
sinks:
  friendtech_events:
    secret_name: EXAMPLE_SECRET
    type: postgres
    from: friendtech_clean
    schema: decoded_events
    table: friendtech