Skip to content

A TypeScript library for decoding Cosmos SDK transactions, providing human-readable message decoding and balance change tracking.

Notifications You must be signed in to change notification settings

initia-labs/tx-decoder

Repository files navigation

Transaction Decoder

npm version Bundle Size npm downloads TypeScript Unit Tests License: MIT

A TypeScript library for decoding Cosmos SDK transactions, providing human-readable message decoding and balance change tracking.

Table of Contents

✨ Features

  • Human-Readable Output: Decodes Cosmos SDK transaction messages into a clear, human-readable JSON format.
  • Multi-VM Support: Full support for Move VM, EVM, and WASM virtual machines with VM-specific balance tracking.
  • Balance Tracking: Tracks fungible token deltas and correlates Move objects or EVM NFTs with specific tokenId tracking based on the originating VM.
  • Type-Safe: Built with TypeScript and validated with Zod for robust, type-safe operations.
  • Extensible: Features a flexible handler system that can be easily extended to support new message types.
  • Immutable State: Uses Immer for safe and predictable state management.
  • ABI-driven EVM Support: Uses viem to decode EVM event logs (ERC-20 and ERC-721 Transfer events) without relying on Cosmos coin_spent events.

πŸ“¦ Installation

# npm
npm install @initia/tx-decoder

# yarn
yarn add @initia/tx-decoder

# pnpm
pnpm add @initia/tx-decoder

# bun
bun add @initia/tx-decoder

πŸš€ Quick Start

import { TxDecoder } from "@initia/tx-decoder";

const decoder = new TxDecoder({
  registryUrl: "https://registry.initia.xyz/",
  restUrl: "https://rest.initia.xyz"
});

// Decode a Cosmos transaction for L1 and Move L2
const decodedTx = await decoder.decodeCosmosTransaction(txResponse);
console.log(decodedTx);

// Decode a Cosmos transaction for EVM L2
const decodedEvmTx = await decoder.decodeCosmosEvmTransaction(txResponse);
console.log(decodedEvmTx);

// Decode a Cosmos transaction for WASM L2
const decodedWasmTx = await decoder.decodeCosmosWasmTransaction(txResponse);
console.log(decodedWasmTx);

// Decode a native Ethereum RPC transaction
const ethereumTx = await decoder.decodeEthereumTransaction({
  tx: ethereumTransaction,
  txReceipt: ethereumTransactionReceipt
});
console.log(ethereumTx);

Each decoded message includes a balanceChanges object tagged with vm: "move", vm: "evm", or vm: "wasm". EVM balance deltas are sourced from decoded log events via viem rather than Cosmos bank events.

πŸ“– API Reference

TxDecoder

The main class for decoding transactions.

Constructor

new TxDecoder(config: DecoderConfig)

Parameters:

  • config: DecoderConfig - Configuration object with the following properties:
    • registryUrl: string - Registry URL to retrieve chain registries
    • restUrl: string - REST endpoint of the Initia chain to query on-chain data
    • jsonRpcUrl?: string - JSON-RPC endpoint for EVM L2 chains
    • timeoutMs?: number - HTTP request timeout in milliseconds (default: 10000)

Methods

decodeCosmosTransaction(txResponse: TxResponse): Promise<DecodedTx>

Decodes a Cosmos transaction response for L1 and Move L2 chains into a human-readable format.

Parameters:

  • txResponse: TxResponse - The raw transaction response from the blockchain

Returns: Promise<DecodedTx> - A promise that resolves to a decoded transaction object

decodeCosmosEvmTransaction(txResponse: TxResponse): Promise<DecodedTx>

Decodes a Cosmos transaction response for EVM L2 chains, processing only general message types (excludes /initia.move and /opinit prefixes).

Note: Requires providing jsonRpcUrl in TxDecoder config to resolve EVM denominations.

Parameters:

  • txResponse: TxResponse - The raw transaction response from the blockchain

Returns: Promise<DecodedTx> - A promise that resolves to a decoded transaction object

decodeCosmosWasmTransaction(txResponse: TxResponse): Promise<DecodedTx>

Decodes a Cosmos transaction response for WASM L2 chains. Balance changes are tracked from transfer events in transaction logs.

Parameters:

  • txResponse: TxResponse - The raw transaction response from the blockchain

Returns: Promise<DecodedTx> - A promise that resolves to a decoded transaction object

decodeEthereumTransaction(payload: EthereumRpcPayload): Promise<DecodedEthereumTx>

Decodes a native Ethereum RPC transaction with balance change tracking from transaction receipt logs.

Parameters:

  • payload: EthereumRpcPayload - Object containing:
    • tx: EthereumTransaction - The Ethereum transaction object from eth_getTransactionByHash
    • txReceipt: EthereumTransactionReceipt - The transaction receipt from eth_getTransactionReceipt

Returns: Promise<DecodedEthereumTx> - A promise that resolves to a decoded Ethereum transaction object

Type Definitions

DecodedTx (Cosmos Transactions)

interface DecodedTx {
  messages: ProcessedMessage[];
  metadata: Metadata;
  totalBalanceChanges: BalanceChanges;
}

DecodedEthereumTx (Ethereum RPC Transactions)

interface DecodedEthereumTx {
  decodedTransaction: DecodedEthereumCall;
  metadata: Metadata;
  totalBalanceChanges: EvmBalanceChanges;
}

DecodedEthereumCall

type DecodedEthereumCall =
  | DecodedErc20ApproveCall
  | DecodedErc20TransferCall
  | DecodedErc20TransferFromCall
  | DecodedErc721ApproveCall
  | DecodedErc721SafeTransferFromCall
  | DecodedNotSupportedCall;

ProcessedMessage

interface ProcessedMessage {
  balanceChanges: BalanceChanges;
  decodedMessage: DecodedMessage;
}

BalanceChanges

interface BaseBalanceChanges {
  ft: { [address: string]: FtChange };
}

interface MoveBalanceChanges extends BaseBalanceChanges {
  vm: "move";
  object: { [address: string]: ObjectChange };
}

interface EvmBalanceChanges extends BaseBalanceChanges {
  vm: "evm";
  nft: { [address: string]: NftChange };
}

interface WasmBalanceChanges extends BaseBalanceChanges {
  vm: "wasm";
}

type BalanceChanges =
  | MoveBalanceChanges
  | EvmBalanceChanges
  | WasmBalanceChanges;

// Type aliases
type FtChange = { [denom: string]: string };
type NftChange = { [contract: string]: { [tokenId: string]: string } };
type ObjectChange = { [address: string]: string };

Note on NFT Balance Changes:

  • NFT balance changes are tracked with 3-level nesting: address β†’ contract β†’ tokenId β†’ value
  • Each NFT transfer shows "1" for receiving and "-1" for sending
  • This structure allows tracking specific NFT tokens rather than just contract-level changes
  • Example: nft["0x123..."]["0xNFTContract"]["42"] = "1" means address 0x123... received NFT token #42 from contract 0xNFTContract

DecodedMessage

Please see here

Response Structure

The decoder returns a structured object with the following format:

Move VM Example (Bank Send)

{
  messages: [
    {
      balanceChanges: {
        vm: "move",
        ft: {
          "init1...": { "uinit": "-1000000" },
          "init1...": { "uinit": "1000000" }
        },
        object: {}
      },
      decodedMessage: {
        action: "send",
        data: {
          from: "init1...",
          to: "init1...",
          coins: [
            {
              amount: "1000000",
              denom: "uinit"
            },
          ]
        },
        isIbc: false,
        isOp: false
      }
    }
  ],
  metadata: {},
  totalBalanceChanges: {
    vm: "move",
    ft: {
      "init1...": { "uinit": "-1000000" },
      "init1...": { "uinit": "1000000" }
    },
    object: {}
  }
}

EVM Example (NFT Transfer)

{
  messages: [
    {
      balanceChanges: {
        vm: "evm",
        ft: {
          "0x19f8a98c...": { "evm/E1Ff7038eAAAF027031688E1535a055B2Bac2546": "-24400000000001" },
          "0xf1829676...": { "evm/E1Ff7038eAAAF027031688E1535a055B2Bac2546": "737822500000" }
        },
        nft: {
          "0x19f8a98c...": {
            "0x5d4376b62fa8AC16dFabe6a9861E11c33A48C677": {
              "4561": "-1"
            }
          },
          "0x8f433715...": {
            "0x5d4376b62fa8AC16dFabe6a9861E11c33A48C677": {
              "4561": "1"
            }
          }
        }
      },
      decodedMessage: {
        action: "not_supported",
        data: {
          msgType: "/minievm.evm.v1.MsgCall"
        },
        isIbc: false,
        isOp: false
      }
    }
  ],
  metadata: {},
  totalBalanceChanges: {
    vm: "evm",
    ft: {
      "0x19f8a98c...": { "evm/E1Ff7038eAAAF027031688E1535a055B2Bac2546": "-24400000000001" },
      "0xf1829676...": { "evm/E1Ff7038eAAAF027031688E1535a055B2Bac2546": "737822500000" }
    },
    nft: {
      "0x19f8a98c...": {
        "0x5d4376b62fa8AC16dFabe6a9861E11c33A48C677": {
          "4561": "-1"
        }
      },
      "0x8f433715...": {
        "0x5d4376b62fa8AC16dFabe6a9861E11c33A48C677": {
          "4561": "1"
        }
      }
    }
  }
}

Cosmos Messages

Bank Messages

  • /cosmos.bank.v1beta1.MsgSend (supported on Move, EVM, and WASM VMs)

Distribution Messages

  • /cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward

Staking Messages (Multi-Staking)

  • /initia.mstaking.v1.MsgDelegate
  • /initia.mstaking.v1.MsgUndelegate
  • /initia.mstaking.v1.MsgBeginRedelegate

Authz Messages

  • /cosmos.authz.v1beta1.MsgExec - Execute authorized messages on behalf of another account
  • /cosmos.authz.v1beta1.MsgGrant - Grant authorization to another account
  • /cosmos.authz.v1beta1.MsgRevoke - Revoke a previously granted authorization

Feegrant Messages

  • /cosmos.feegrant.v1beta1.MsgGrantAllowance - Grant a fee allowance to another account
  • /cosmos.feegrant.v1beta1.MsgRevokeAllowance - Revoke an existing fee allowance

Note: MsgExec recursively decodes the inner messages it contains. All currently supported message types can be wrapped within MsgExec and will be properly decoded.

Move VM Messages

  • /initia.move.v1.MsgExecute
  • /initia.move.v1.MsgExecuteJSON

Supported Move Scripts

DEX & Liquidity:

  • 0x1::dex::swap_script - Token swap
  • 0x1::dex::provide_liquidity_script - Provide liquidity
  • 0x1::dex::provide_liquidity_and_stake - Provide liquidity and stake LP tokens
  • 0x1::dex::provide_liquidity_and_stake_lock - Provide liquidity and stake-lock LP tokens
  • 0x1::dex::withdraw_liquidity_script - Withdraw liquidity
  • 0x1::dex::extend_lock - Extend LP lock period
  • 0x1::dex::merge_lock - Merge locked LP positions

Stableswap:

  • 0x1::stableswap::swap_script - Stableswap token swap
  • 0x1::stableswap::provide - Provide liquidity to stableswap
  • 0x1::stableswap::withdraw - Withdraw liquidity from stableswap

Minitswap:

  • 0x1::minitswap::provide - Provide liquidity to minitswap
  • 0x1::minitswap::unbond - Unbond from minitswap
  • 0x1::minitswap::withdraw_unbond - Withdraw unbonded tokens

NFT:

  • 0x1::simple_nft::mint - Mint NFT
  • 0x1::simple_nft::burn - Burn NFT

Object Transfer:

  • 0x1::object::transfer_call - Transfer Move objects

Lock Staking:

  • <module_address>::lock_staking::delegate - Delegate with lock
  • <module_address>::lock_staking::undelegate - Undelegate locked tokens
  • <module_address>::lock_staking::redelegate - Redelegate locked tokens
  • <module_address>::lock_staking::withdraw_delegator_reward - Withdraw rewards from locked stake
  • <module_address>::lock_staking::extend - Extend lock period
  • <module_address>::lock_staking::batch_extend - Batch extend lock periods

VIP (Voting Incentive Program):

  • <module_address>::vip::batch_claim_user_reward_script - Claim VIP rewards
  • <module_address>::vip::batch_lock_stake_script - Lock stake for VIP
  • <module_address>::weight_vote::vote - Vote on gauge weights

OpInit Messages (L1 ↔ L2 Bridge)

  • /opinit.ophost.v1.MsgInitiateTokenDeposit - Initiate deposit from L1 to L2
  • /opinit.ophost.v1.MsgFinalizeTokenWithdrawal - Finalize withdrawal on L1
  • /opinit.opchild.v1.MsgFinalizeTokenDeposit - Finalize deposit on L2
  • /opinit.opchild.v1.MsgInitiateTokenWithdrawal - Initiate withdrawal from L2 to L1

IBC Messages

IBC Fungible Token Transfer

  • /ibc.applications.transfer.v1.MsgTransfer - Send fungible tokens via IBC
  • /ibc.core.channel.v1.MsgRecvPacket (with transfer application) - Receive fungible tokens via IBC

IBC Non-Fungible Token Transfer

  • /ibc.applications.nft_transfer.v1.MsgTransfer - Send NFTs via IBC (Move & EVM)
  • /ibc.core.channel.v1.MsgRecvPacket (with nft-transfer application) - Receive NFTs via IBC (Move & EVM)

Ethereum RPC Transactions

The library supports decoding native Ethereum RPC transactions (via decodeEthereumTransaction):

ERC-20 Functions

  • approve(address spender, uint256 amount) - Approve ERC-20 spending
  • transfer(address to, uint256 amount) - Transfer ERC-20 tokens
  • transferFrom(address from, address to, uint256 amount) - Transfer ERC-20 tokens from approved address

ERC-721 Functions

  • approve(address to, uint256 tokenId) - Approve NFT transfer
  • safeTransferFrom(address from, address to, uint256 tokenId) - Safe transfer NFT

Native ETH Transfer

  • Native ETH transfer (value transfer without function call)

Contract Deployment

  • Contract creation transactions

Kami721 NFT

  • publicMint() - Public mint function for Kami721 NFTs

WASM Event Processing

The library automatically processes WASM event logs for balance tracking:

  • transfer event type with recipient, sender, and amount attributes

EVM Event Processing

The library automatically processes EVM event logs for balance tracking:

  • Transfer(address indexed from, address indexed to, uint256 value) (ERC-20)
  • Transfer(address indexed from, address indexed to, uint256 indexed tokenId) (ERC-721)

πŸ’» Development

Prerequisites

  • Node.js >= 20
  • pnpm
# Clone the repository
git clone https://github.com/initia-labs/tx-decoder.git
cd tx-decoder

# Install dependencies
pnpm install

# Run tests
pnpm test

# Build
pnpm build

πŸ“ Folder Structure

tx-decoder/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ api/                     # API client architecture
β”‚   β”‚   β”œβ”€β”€ api.ts               # Main API client with service composition
β”‚   β”‚   └── services/            # Modular API services
β”‚   β”‚       β”œβ”€β”€ base.ts          # Base service with caching
β”‚   β”‚       β”œβ”€β”€ cosmos.ts        # Cosmos chain API interactions
β”‚   β”‚       β”œβ”€β”€ evm.ts           # EVM-specific API calls (contract detection, etc.)
β”‚   β”‚       └── move.ts          # Move VM API interactions
β”‚   β”œβ”€β”€ balance-changes.ts       # Balance aggregation helpers per VM
β”‚   β”œβ”€β”€ constants/               # Application constants
β”‚   β”‚   β”œβ”€β”€ index.ts             # Main constants export
β”‚   β”‚   β”œβ”€β”€ balance-changes.ts   # Default balance change structures
β”‚   β”‚   β”œβ”€β”€ evm-abis.ts          # EVM contract ABIs (ERC20, ERC721, etc.)
β”‚   β”‚   └── evm-selectors.ts     # EVM function selectors and event signatures
β”‚   β”œβ”€β”€ decoder.ts               # Main transaction decoding logic
β”‚   β”œβ”€β”€ decoder-registry.ts      # Decoder registry arrays (Cosmos EVM, Cosmos Move, Ethereum)
β”‚   β”œβ”€β”€ validation.ts            # Transaction validation functions
β”‚   β”œβ”€β”€ index.ts                 # Entry point for exports
β”‚   β”œβ”€β”€ message-types.ts         # Supported message types
β”‚   β”œβ”€β”€ metadata-resolver.ts     # Resolves and fetches NFT metadata for token addresses
β”‚   β”œβ”€β”€ decoders/                # Message decoders
β”‚   β”‚   β”œβ”€β”€ cosmos/              # Cosmos SDK message decoders
β”‚   β”‚   β”œβ”€β”€ ethereum/            # Ethereum RPC transaction decoders
β”‚   β”‚   β”‚   └── erc20/           # ERC-20 transfer and transferFrom decoders
β”‚   β”‚   β”œβ”€β”€ ibc/                 # IBC message decoders
β”‚   β”‚   β”œβ”€β”€ move/                # Move message decoders
β”‚   β”‚   └── op-init/             # OpInit message decoders
β”‚   β”œβ”€β”€ interfaces/              # TypeScript interfaces and discriminated unions
β”‚   β”‚   β”œβ”€β”€ balance-changes.ts   # Balance change type definitions
β”‚   β”‚   β”œβ”€β”€ decoder.ts           # Decoder interface definitions
β”‚   β”‚   β”œβ”€β”€ ethereum.ts          # Ethereum transaction type definitions
β”‚   β”‚   └── processor.ts         # Event processor interfaces
β”‚   β”œβ”€β”€ processors/              # Event processors
β”‚   β”‚   β”œβ”€β”€ evm/                 # ABI-driven EVM event processors
β”‚   β”‚   β”‚   β”œβ”€β”€ index.ts         # EVM processor registry
β”‚   β”‚   β”‚   └── transfer.ts      # ERC-20/ERC-721 Transfer event processor
β”‚   β”‚   └── move/                # Move event processors
β”‚   β”œβ”€β”€ schema/                  # Zod schemas for validation
β”‚   β”‚   β”œβ”€β”€ common.ts            # Common validation schemas
β”‚   β”‚   β”œβ”€β”€ cosmos/              # Cosmos transaction schemas
β”‚   β”‚   β”œβ”€β”€ ethereum/            # Ethereum RPC transaction schemas
β”‚   β”‚   └── evm.ts               # EVM log schemas
β”‚   β”œβ”€β”€ utils/                   # Utility helpers
β”‚   β”‚   β”œβ”€β”€ index.ts             # Utility exports
β”‚   β”‚   β”œβ”€β”€ denom.ts             # Denomination handling utilities
β”‚   β”‚   └── merge-balances.ts    # Balance merging logic (supports 3-level NFT nesting)
β”‚   └── tests/                   # Jest unit tests grouped by domain
β”‚       β”œβ”€β”€ _shared/             # Shared test utilities and helpers
β”‚       β”œβ”€β”€ common/              # Common functionality tests
β”‚       β”œβ”€β”€ cosmos/              # Cosmos message decoder tests
β”‚       β”œβ”€β”€ ethereum/            # Ethereum RPC transaction tests
β”‚       β”‚   └── erc20/           # ERC-20 transfer and transferFrom tests
β”‚       β”œβ”€β”€ protocols/           # Protocol-specific tests (IBC, OpInit)
β”‚       └── utils/               # Utility function tests
β”œβ”€β”€ package.json                 # Project metadata and dependencies
β”œβ”€β”€ README.md                    # Project documentation
└── ...                          # Config and other files

See CONTRIBUTING.md for development setup and guidelines.

πŸ“„ License

MIT

About

A TypeScript library for decoding Cosmos SDK transactions, providing human-readable message decoding and balance change tracking.

Resources

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 7