Skip to main content

Cosmos Ethermint (EVM) Support

SubQuery TeamAbout 3 min

Cosmos Ethermint (EVM) Support

We provide a custom data source processor for Cosmos's Ethermint EVMopen in new window. This offers a simple way to filter and index both EVM and Cosmos activity on many Cosmos networks within a single SubQuery project.

Note

Ethermint chains (e.g. Cronos) are usually fully EVM compatible, which means that you can use two options for indexing Ethermint data. You can index Ethermint contract data via the standard Cosmos RPC interface, or via Ethereum APIs. For Cronos, we provide a starter project for eachopen in new window and you can compare the two different options in the Cronos quick start guide.

This document goes into detail about how to use the Ethermint Cosmos RPCs (rather than the Ethereum API)

You can also refer to the basic Cronos EVM Example Projectopen in new window with an event and call handler.

Getting started

  1. Add the custom datasource as a dependency:
  • Create a new project from an EVM template though subql init OR
  • For existing projects, yarn add @subql/ethermint-evm-processor or npm i @subql/ethermint-evm-processor.
  1. Add a custom data source as described below.
  2. Add handlers for the custom data source to your code.

Data Source Spec

FieldTypeRequiredDescription
kindcosmos/EthermintEvmYesType of the datasource
processor.file"./node_modules/@subql/ethermint-evm-processor/dist/bundle.js"YesFile reference to the data processor code
processor.optionsProcessorOptionsNoOptions specific to the Frontier Processor
assets{ [key: String]: { file: String }}NoAn object of external asset files

Processor Options

FieldTypeRequiredDescription
abiStringNoThe ABI that is used by the processor to parse arguments. MUST be a key of assets
addressString or nullNoA contract address where the event is from or call is made to. null will capture contract creation calls

Call Handlers

FieldTypeRequiredDescription
kindcosmos/EthermintEvmCallYesSpecifies that this is an Call type handler
filterCall FilterNoFilter the data source to execute

Call Filters

FieldTypeExample(s)Description
functionString0x095ea7b3, approve(address to,uint256 value)Either Function Signatureopen in new window strings or the function sighash to filter the function called on the contract
fromString0x6bd193ee6d2104f14f94e2ca6efefae561a4334bAn Ethereum address that sent the transaction

Handler Functions

Unlike a normal handler you will not get a CosmosMessage as the parameter, instead you will get a EthermintEvmCall which is based on Ethers TransactionResponseopen in new window type.

Changes from the TransactionResponse type:

  • It doesn't have wait and confirmations properties.
  • A success property is added to know if the transaction was a success.
  • args is added if the abi field is provided and the arguments can be successfully parsed. You can add a generic parameter like so to type args: EthermintEvmCall<{ from: string, to: string, value: BigNumber }>.

Event Handlers

FieldTypeRequiredDescription
kindsubstrate/EthermintEvmEventYesSpecifies that this is an Event type handler
filterEvent FilterNoFilter the data source to execute

Event Filters

FieldTypeExample(s)Description
topicsString arrayTransfer(address indexed from,address indexed to,uint256 value)The topics filter follows the Ethereum JSON-PRC log filters, more documentation can be found hereopen in new window.

Note on topics:

There are a couple of improvements from basic log filters:

Handler Functions

Unlike a normal handler you will not get a CosmosEvent as the parameter, instead you will get a EthermintEvmEvent or EthermintEvmCall which is based on Ethers Logopen in new window type.

Changes from the Log type:

  • args is added if the abi field is provided and the arguments can be successfully parsed. You can add a generic parameter like so to type args: FrontierEvmEvent<{ from: string, to: string, value: BigNumber }>.
import { Approval, Transaction } from "../types";
import {
  EthermintEvmEvent,
  EthermintEvmCall,
} from "@subql/ethermint-evm-processor";
import { BigNumber } from "ethers";

// Setup types from ABI
type TransferEventArgs = [string, string, BigNumber] & {
  from: string;
  to: string;
  value: BigNumber;
};

type ApproveCallArgs = [string, BigNumber] & {
  _spender: string;
  _value: BigNumber;
};

export async function handleEthermintEvmEvent(
  event: EthermintEvmEvent<TransferEventArgs>,
): Promise<void> {
  const transaction = new Transaction(event.transactionHash);

  transaction.value = event.args[2].toBigInt();
  transaction.from = event.args[0];
  transaction.to = event.args[1];
  transaction.contractAddress = event.address;

  await transaction.save();
}

export async function handleEthermintEvmCall(
  event: EthermintEvmCall<ApproveCallArgs>,
): Promise<void> {
  const approval = new Approval(event.hash);
  approval.owner = event.from;
  approval.value = event.args[1].toBigInt();
  approval.spender = event.args[0];
  approval.contractAddress = event.to;

  await approval.save();
}

Data Source Example

This is an extract from the project.ts manifest file.

{
  dataSources: [
    {
      kind: "cosmos/EthermintEvm",
      startBlock: 446,
      processor: {
        file: "./node_modules/@subql/ethermint-evm-processor/dist/bundle.js",
        options: {
          abi: "erc20",
          address: "0x5c7f8a570d578ed84e63fdfa7b1ee72deae1ae23", // Wrapped CRO
        },
      },
      assets: new Map([["erc20", { file: "./erc20.abi.json" }]]),
      mapping: {
        file: "./dist/index.js",
        handlers: [
          {
            handler: "handleEthermintEvmCall",
            kind: "cosmos/EthermintEvmCall",
            filter: {
              // Either Function Signature strings or the function `sighash` to filter the function called on the contract
              // https://docs.ethers.io/v5/api/utils/abi/fragments/#FunctionFragment
              method: "approve(address guy, uint256 wad)",
            },
          },
          {
            handler: "handleEthermintEvmEvent",
            kind: "cosmos/EthermintEvmEvent",
            filter: {
              // The topics filter follows the Ethereum JSON-PRC log filters
              // https://docs.ethers.io/v5/concepts/events
              // Example valid values:
              // - '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
              // - Transfer(address,address,u256)
              topics: ["Transfer(address src, address dst, uint256 wad)"],
            },
          },
        ],
      },
    },
  ],
}

Known Limitations

  • There is no way to get the transaction receipts with call handlers.
  • blockHash properties are currently left undefined, the blockNumber property can be used instead.

Extra info