Skip to content

Evmos IBC precompile

Published: at 03:22 PM

Example on how to use the IBC precompile.

Table of contents

Open Table of contents

Context

After the release of Evmos v16, the intended way to interact with the cosmos transactions is using the precompiles.

In this example we are going to send an IBC transfer using a solidity contract instead of sending a Cosmos transaction with a Cosmos wallet.

Requirements

Example

This example will be limited to the creation of the Ethereum transaction, to sign it and broadcast it with wallet extensions in the browser, any tutorial on how to send an Ethereum transaction with Metamask will work.

Set up

mkdir /tmp/example
cd /tmp/example
nvm use v20.12.0
npm init -y
npm install ethers

The IBC precompile

You can get the code from the extensions’ repo.

curl https://raw.githubusercontent.com/evmos/extensions/main/precompiles/abi/ics20.json >> ics20.json

Configure the package.json file

It should look like this.

{
  "name": "example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "sendtx": "tsc && node main.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ethers": "^6.13.2",
    "typescript": "^5.5.4"
  }
}

I added the sendtx script to simple build and execute the code.

Configure the tsconfig.json file

This is a very simple configuration file, just to enable us to build the typescript file.

{
  "compilerOptions": {
    "target": "es2020",
    "lib": ["es2020"],
    "module": "CommonJS",
    "baseUrl": "./",
    "moduleResolution": "node"
  },
  "include": ["./**/*.ts"],
  "exclude": ["node_modules/**/*"]
}

Interact with the Precompile

import { ethers } from "ethers";
import * as fs from "fs";

const abi = JSON.parse(fs.readFileSync("./ics20.json", "utf8"));

const provider = new ethers.JsonRpcProvider("http://localhost:8545");

const privateKey =
  "53ED37ED51B785D6E197033AA1325541A802F2994EF801680AD0EBEACAB25122";
const wallet = new ethers.Wallet(privateKey, provider);

// IBC Precompile Address
const contractAddress = "0x0000000000000000000000000000000000000802";
const contract = new ethers.Contract(contractAddress, abi, wallet);

const ONE_DAY_IN_MILLISECONDS = 60 * 60 * 24 * 1000;
function getTimeoutTimestamp() {
  return BigInt(Date.now() + ONE_DAY_IN_MILLISECONDS) * 1000000n;
}

async function sendTransaction() {
  try {
    const tx = await contract.transfer(
      "transfer", // port
      "channel-0", // channel
      "aevmos", //denom
      1, // amount
      "0x914369752aC051F66581Fdeba6Ea5DC9602c7EA6", // sender
      "cosmos1nqy85a3uu75vz8gpejxedm5dfxn8zs0l4q8a8l", // receiver
      {
        revisionNumber: 0n,
        revisionHeight: 0n,
      },
      getTimeoutTimestamp(),
      "memo"
    );

    // Wait for the transaction to be mined
    const receipt = await tx.wait();
    console.log("Transaction successful with hash:", receipt.transactionHash);
  } catch (error) {
    console.error("Error sending transaction:", error);
  }
}

sendTransaction();

Production

When running in production, you want to connect to a provider using a WalletConnect or Metamask wallet extension.

The only thing that you need to change from the given example is the ethers.Wallets object and directly use the wallet extension.