or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

decoding.mdindex.mdpubkey.mdregistry.mdsigning.mdwallets.md
tile.json

decoding.mddocs/

Transaction Decoding

Utilities for decoding raw transaction data into structured format. These functions help parse serialized transactions from the blockchain into usable TypeScript objects.

Capabilities

Transaction Raw Decoding

Decode serialized TxRaw bytes (as stored in Tendermint/CometBFT) into structured components.

/**
 * Takes a serialized TxRaw (the bytes stored in Tendermint) and decodes it into something usable
 * @param tx - Serialized transaction bytes
 * @returns Decoded transaction with separate auth info, body, and signatures
 */
function decodeTxRaw(tx: Uint8Array): DecodedTxRaw;

interface DecodedTxRaw {
  /** Authentication information including signers and fee details */
  readonly authInfo: AuthInfo;
  /** Transaction body containing messages and metadata */
  readonly body: TxBody;
  /** Array of transaction signatures */
  readonly signatures: readonly Uint8Array[];
}

Usage Examples:

import { decodeTxRaw } from "@cosmjs/proto-signing";

// Decode transaction from blockchain data
const rawTxBytes = new Uint8Array([/* serialized tx bytes from blockchain */]);
const decodedTx = decodeTxRaw(rawTxBytes);

// Access transaction components
console.log("Transaction body:", decodedTx.body);
console.log("Messages count:", decodedTx.body.messages.length);
console.log("Transaction memo:", decodedTx.body.memo);
console.log("Timeout height:", decodedTx.body.timeoutHeight);

console.log("Auth info:", decodedTx.authInfo);
console.log("Fee amount:", decodedTx.authInfo.fee?.amount);
console.log("Gas limit:", decodedTx.authInfo.fee?.gasLimit);
console.log("Signers count:", decodedTx.authInfo.signerInfos.length);

console.log("Signatures count:", decodedTx.signatures.length);
console.log("First signature length:", decodedTx.signatures[0]?.length);

Working with Decoded Transactions

Once decoded, you can inspect and process the transaction components.

import { decodeTxRaw, Registry } from "@cosmjs/proto-signing";
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";

// Decode transaction
const decodedTx = decodeTxRaw(txBytes);

// Create registry to decode messages
const registry = new Registry([
  ["/cosmos.bank.v1beta1.MsgSend", MsgSend],
]);

// Process each message in the transaction
for (const message of decodedTx.body.messages) {
  console.log("Message type:", message.typeUrl);
  
  if (message.typeUrl === "/cosmos.bank.v1beta1.MsgSend") {
    const sendMsg = registry.decode({
      typeUrl: message.typeUrl,
      value: message.value,
    });
    
    console.log("Send from:", sendMsg.fromAddress);
    console.log("Send to:", sendMsg.toAddress);
    console.log("Amount:", sendMsg.amount);
  }
}

// Analyze fee information
const fee = decodedTx.authInfo.fee;
if (fee) {
  console.log("Gas limit:", fee.gasLimit.toString());
  console.log("Fee amount:", fee.amount);
  console.log("Fee granter:", fee.granter);
  console.log("Fee payer:", fee.payer);
}

// Analyze signers
for (const [index, signerInfo] of decodedTx.authInfo.signerInfos.entries()) {
  console.log(`Signer ${index}:`);
  console.log("  Public key:", signerInfo.publicKey);
  console.log("  Sequence:", signerInfo.sequence.toString());
  console.log("  Sign mode:", signerInfo.modeInfo?.single?.mode);
}

Types

interface DecodedTxRaw {
  /** Authentication information including signers and fee details */
  readonly authInfo: AuthInfo;
  /** Transaction body containing messages and metadata */
  readonly body: TxBody;
  /** Array of transaction signatures */
  readonly signatures: readonly Uint8Array[];
}

interface AuthInfo {
  /** Information about each signer */
  signerInfos: SignerInfo[];
  /** Fee information */
  fee?: Fee;
}

interface SignerInfo {
  /** Public key of the signer */
  publicKey?: Any;
  /** Mode info describing how the transaction was signed */
  modeInfo?: ModeInfo;
  /** Account sequence number */
  sequence: bigint;
}

interface ModeInfo {
  /** Single signing mode info */
  single?: {
    /** The signing mode used */
    mode: SignMode;
  };
  /** Multi signing mode info (for multisig) */
  multi?: {
    /** Bitarray of signatures */
    bitarray?: CompactBitArray;
    /** Mode infos for each signature */
    modeInfos: ModeInfo[];
  };
}

interface Fee {
  /** Fee amount in various denominations */
  amount: Coin[];
  /** Gas limit for the transaction */
  gasLimit: bigint;
  /** Optional fee granter address */
  granter?: string;
  /** Optional fee payer address */
  payer?: string;
}

interface TxBody {
  /** Array of transaction messages */
  messages: Any[];
  /** Optional transaction memo */
  memo: string;
  /** Optional timeout height */
  timeoutHeight: bigint;
  /** Optional extension options */
  extensionOptions: Any[];
  /** Optional non-critical extension options */
  nonCriticalExtensionOptions: Any[];
}

interface Coin {
  /** Denomination (e.g., "uatom", "stake") */
  denom: string;
  /** Amount as string */
  amount: string;
}

interface Any {
  /** Type URL identifying the message type */
  typeUrl: string;
  /** Serialized message value */
  value: Uint8Array;
}

enum SignMode {
  /** Direct signing mode using protobuf serialization */
  SIGN_MODE_DIRECT = 1,
  /** Legacy amino signing mode */
  SIGN_MODE_LEGACY_AMINO_JSON = 127,
}

Complete Decoding Example

import { decodeTxRaw, Registry } from "@cosmjs/proto-signing";
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx";

// Create registry for common message types
const registry = new Registry([
  ["/cosmos.bank.v1beta1.MsgSend", MsgSend],
  ["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate],
]);

// Function to analyze a transaction
function analyzeTx(txBytes: Uint8Array) {
  // Decode the raw transaction
  const decoded = decodeTxRaw(txBytes);
  
  console.log("=== Transaction Analysis ===");
  
  // Basic transaction info
  console.log(`Messages: ${decoded.body.messages.length}`);
  console.log(`Memo: ${decoded.body.memo || "(none)"}`);
  console.log(`Timeout Height: ${decoded.body.timeoutHeight || "(none)"}`);
  console.log(`Signatures: ${decoded.signatures.length}`);
  
  // Fee analysis
  const fee = decoded.authInfo.fee;
  if (fee) {
    console.log(`Gas Limit: ${fee.gasLimit.toString()}`);
    console.log(`Fee Amount: ${fee.amount.map(c => `${c.amount}${c.denom}`).join(", ")}`);
    if (fee.granter) console.log(`Fee Granter: ${fee.granter}`);
    if (fee.payer) console.log(`Fee Payer: ${fee.payer}`);
  }
  
  // Message analysis
  console.log("\n=== Messages ===");
  decoded.body.messages.forEach((msg, index) => {
    console.log(`Message ${index + 1}: ${msg.typeUrl}`);
    
    try {
      const decodedMsg = registry.decode({
        typeUrl: msg.typeUrl,
        value: msg.value,
      });
      
      if (msg.typeUrl === "/cosmos.bank.v1beta1.MsgSend") {
        console.log(`  From: ${decodedMsg.fromAddress}`);
        console.log(`  To: ${decodedMsg.toAddress}`);
        console.log(`  Amount: ${decodedMsg.amount.map(c => `${c.amount}${c.denom}`).join(", ")}`);
      } else if (msg.typeUrl === "/cosmos.staking.v1beta1.MsgDelegate") {
        console.log(`  Delegator: ${decodedMsg.delegatorAddress}`);
        console.log(`  Validator: ${decodedMsg.validatorAddress}`);
        console.log(`  Amount: ${decodedMsg.amount.amount}${decodedMsg.amount.denom}`);
      }
    } catch (error) {
      console.log(`  Could not decode message: ${error.message}`);
    }
  });
  
  // Signer analysis
  console.log("\n=== Signers ===");
  decoded.authInfo.signerInfos.forEach((signer, index) => {
    console.log(`Signer ${index + 1}:`);
    console.log(`  Sequence: ${signer.sequence.toString()}`);
    console.log(`  Sign Mode: ${signer.modeInfo?.single?.mode || "Unknown"}`);
    if (signer.publicKey) {
      console.log(`  Public Key Type: ${signer.publicKey.typeUrl}`);
    }
  });
  
  return decoded;
}

// Usage
const txBytes = new Uint8Array([/* transaction bytes from blockchain */]);
const analyzedTx = analyzeTx(txBytes);