Utilities for decoding raw transaction data into structured format. These functions help parse serialized transactions from the blockchain into usable TypeScript objects.
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);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);
}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,
}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);