npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

viem-cosmos

v0.0.3

Published

viem client extensions for Cosmos EVM precompiles (bank, staking, distribution, gov). Typed actions built on the cosmos-evm-contracts ABIs.

Readme

viem-cosmos

Typed viem client extensions ("decorators") for the Cosmos EVM precompiles.

viem-cosmos is a representation layer on top of the cosmos-evm-contracts ABIs. It bridges the gap between EVM-native types (hex addresses, raw uint/bytes fields) and Cosmos-native representations (bech32 addresses, typed BondStatus/ProposalStatus/VoteOption enums, human-readable LegacyDec/DecCoin numbers). The underlying call plumbing is viem's readContract/writeContract; argument and return types are inferred straight from the ABI via a generic factory, so every action is fully type-safe without hand-written bindings.

v0.x covers the validator-economy precompiles: bank, staking, distribution, gov (core four, combined), and slashing (standalone). Other precompiles (ICS-20, vesting, …) are reachable raw via precompileActions.

Install

pnpm add viem-cosmos viem
# viem is a peer dependency
# cosmos-evm-contracts (precompile ABIs) is a bundled dependency

Quick start

import { http } from "viem"
import { defineCosmosChain, stakingActions, pageRequest, bondStatusToWireString } from "viem-cosmos"
import { createPublicClient } from "viem"

const cosmosEvmLocal = defineCosmosChain({
  id: 262144,
  name: "evmd Local",
  nativeCurrency: { name: "TEST", symbol: "TEST", decimals: 18 },
  rpcUrls: { default: { http: ["http://localhost:8545"] } },
  bech32Prefix: "cosmos",
})

const client = createPublicClient({
  chain: cosmosEvmLocal,
  transport: http(),
}).extend(stakingActions())

const { validators } = await client.validators({
  args: [bondStatusToWireString("BONDED"), pageRequest({ limit: 10n })],
})

// validators[0].operatorAddress === "cosmosvaloper1…"
// validators[0].operatorAddressHex  === "0x…"  (retained, non-lossy)
// validators[0].status              === "BONDED"  (friendly union; statusRaw keeps the wire uint8)

defineCosmosChain wraps viem's defineChain and adds a bech32Prefix (and optional bech32Prefixes overrides for non-standard validator/consensus prefixes). The formatted decorators read the prefix from client.chain so you never thread it through individual calls.

Precompiles covered

| Precompile | Decorator | In cosmosActions() | Notes | | --- | --- | --- | --- | | bank | bankActions() | yes | Raw passthrough — balances are ERC20-paired integer Coin amounts; no bech32 or LegacyDec transform needed | | staking | stakingActions() | yes | Formatted: hex operator → bech32 valoper, uint8BondStatus, LegacyDec → parsed | | distribution | distributionActions() | yes | Formatted: DecCoin amounts → parsed FormattedDecCoin; write guards require bech32 addresses | | gov | govActions() | yes | Formatted: uint32ProposalStatus, uint8VoteOption, tally LegacyDec → parsed | | slashing | slashingActions() | no (standalone) | Formatted: signing-info hex validator address → bech32 valoper, getParams Dec fields parsed. Standalone because getParams collides with gov's |

Decorator model

Each precompile ships two decorator factories:

  • xActions() — formatted, chain-prefix-aware canonical surface. Transforms reads (hex → bech32, uint enums → typed string unions, LegacyDec/DecCoin → parsed non-lossily). Validates bech32 on write args that carry role-bearing addresses (funds path: hex rejected, no silent mis-targeting).
  • xActionsRaw() — thin ABI→action passthrough, the wagmi-cli analogue. Values returned exactly as viem decoded them. Use as an escape hatch when you need the raw wire form.

The combined cosmosActions() merges bank + staking + distribution + gov onto one client (collision-free). getEvents is stripped from the combined surface to avoid name collisions — use a single-module decorator for event reads.

Slashing is standalone and is NOT part of cosmosActions() because its getParams collides by name with gov's getParams. Extend slashingActions() on a separate client or alongside the individual decorators you need (see Composition below).

This follows the viem op-stack pattern: extend only the decorators a chain supports. Calling an action for an absent precompile errors at runtime.

Usage examples

Combined client (core four)

import { createCosmosClient } from "viem-cosmos"

// createCosmosClient wraps createClient + cosmosActions()
const client = createCosmosClient({
  chain: cosmosEvmLocal,   // a defineCosmosChain(...) result
  transport: http(),
  account,                 // optional; required for write actions
})

// bank
const balances = await client.balances({ args: [account] })

// staking write — validator address must be bech32 valoper
const hash = await client.delegate({
  args: [account, "cosmosvaloper1…", amount],
})

// gov
const proposal = await client.getProposal({ args: [1n] })
// proposal.status === "VOTING_PERIOD"  (friendly union; statusRaw keeps the wire uint32)

Per-module decorators (tree-shakeable)

import { createPublicClient, createWalletClient, http } from "viem"
import { stakingActions, distributionActions } from "viem-cosmos"

const reader = createPublicClient({ chain: cosmosEvmLocal, transport: http() })
  .extend(stakingActions())

const { validators, pageResponse } = await reader.validators({
  args: ["BOND_STATUS_BONDED", pageRequest({ limit: 50n })],
})

Slashing (standalone)

import { createPublicClient, http } from "viem"
import { slashingActions } from "viem-cosmos"

const client = createPublicClient({ chain: cosmosEvmLocal, transport: http() })
  .extend(slashingActions())

const info = await client.getSigningInfo({ args: [consAddressHex] })
// info.validatorAddress === "cosmosvaloper1…"  (bridged from hex)

const params = await client.getParams()
// params.slashFractionDoubleSign, params.slashFractionDowntime — parsed `Dec` fields

// unjail takes the hex operator address directly (no bech32 guard — the ABI arg is
// a plain 20-byte hex, not a role-bearing bech32 valoper)
const hash = await walletClient.extend(slashingActions()).unjail({
  args: [validators[0].operatorAddressHex],
})

Composing individual decorators

import { createWalletClient, http } from "viem"
import { stakingActions, slashingActions } from "viem-cosmos"

// Compose staking + slashing on the same client (viem-idiomatic chaining)
const client = createWalletClient({ chain: cosmosEvmLocal, transport: http(), account })
  .extend(stakingActions())
  .extend(slashingActions())

Raw escape hatch

import { createPublicClient, http } from "viem"
import { stakingActionsRaw } from "viem-cosmos"

// Values returned as decoded: hex operator address, uint8 status, LegacyDec strings
const client = createPublicClient({ chain: cosmosEvmLocal, transport: http() })
  .extend(stakingActionsRaw())

const result = await client.validators({ args: ["BOND_STATUS_BONDED", pageRequest()] })
// result[0][0].operatorAddress === "0x…" (hex, not bridged)

Action surface

| Module | Reads | Writes | | --- | --- | --- | | bank | balances, supplyOf, totalSupply | — | | staking | delegation, redelegation, redelegations, unbondingDelegation, validator, validators | delegate, undelegate, redelegate, cancelUnbondingDelegation, createValidator, editValidator | | distribution | communityPool, delegationRewards, delegationTotalRewards, delegatorValidators, delegatorWithdrawAddress, validatorCommission, validatorDistributionInfo, validatorOutstandingRewards, validatorSlashes | claimRewards, depositValidatorRewardsPool, fundCommunityPool, setWithdrawAddress, withdrawDelegatorRewards, withdrawValidatorCommission | | gov | getConstitution, getDeposit, getDeposits, getParams, getProposal, getProposals, getTallyResult, getVote, getVotes | cancelProposal, deposit, submitProposal, vote, voteWeighted | | slashing | getSigningInfo, getSigningInfos, getParams | unjail |

Every action accepts the underlying viem call fields:

  • readsargs plus blockNumber / blockTag / account / stateOverride
  • writesargs plus account / chain / gas / nonce / fee fields

Representation helpers

Chain definition

import { defineCosmosChain, resolvePrefixes } from "viem-cosmos"

const chain = defineCosmosChain({
  id: 262144,
  name: "My Cosmos EVM Chain",
  nativeCurrency: { name: "ATOM", symbol: "ATOM", decimals: 18 },
  rpcUrls: { default: { http: ["https://rpc.example.com"] } },
  bech32Prefix: "cosmos",
  // Optional: override derived prefixes for non-standard chains
  bech32Prefixes: { valoper: "customvaloper", valcons: "customvalcons" },
})

// { account: "cosmos", valoper: "cosmosvaloper", valcons: "cosmosvalcons" }
const prefixes = resolvePrefixes(chain)

Address conversion

import { evmAddressToBech32, bech32ToEvmAddress } from "viem-cosmos"

const bech32 = evmAddressToBech32("0xabc…", "cosmosvaloper")     // "cosmosvaloper1…"
const hex    = bech32ToEvmAddress("cosmos1…", "cosmos")          // "0x…" (asserts the prefix)

Decimal formatting

import { formatDecCoin, formatDec, formatLegacyDec, parseLegacyDec, formatCoin } from "viem-cosmos"

// DecCoin (precompile-decoded struct with denom + amount)
const coin = formatDecCoin({ denom: "uatom", amount: 1_234_567n })
// { denom: "uatom", amount: 1_234_567n, formatted: "1.234567" }

// LegacyDec (the ABI returns an 18-decimal fixed-point value)
const parsed = parseLegacyDec(1_000_000_000_000_000_000n)  // { raw: 1000000000000000000n, precision: 18 }
const str    = formatLegacyDec(1_000_000_000_000_000_000n)  // "1"
const dec    = formatDec({ value: 1_000_000_000_000_000_000n, precision: 18 })  // "1"

// Plain Coin (integer base-unit amount, e.g. from bank.balances)
const display = formatCoin({ denom: "uatom", amount: 1_000_000n }, 6)  // "1"

Enums

import {
  bondStatusFromWire, bondStatusToWireString, bondStatusToWireNumber,
  proposalStatusFromWire, proposalStatusToWireNumber, proposalStatusToWireString,
  voteOptionFromWire, voteOptionToWireNumber, voteOptionToWireString,
} from "viem-cosmos"

// Wire → friendly union (accepts the uint OR the protobuf string form)
bondStatusFromWire(1)            // "UNBONDED"
proposalStatusFromWire(2)        // "VOTING_PERIOD"
voteOptionFromWire(1)            // "YES"

// Friendly → wire number (for uint ABI args like vote())
bondStatusToWireNumber("BONDED") // 3
// Friendly → wire string (for string-keyed ABI args like validators())
bondStatusToWireString("BONDED") // "BOND_STATUS_BONDED"

Pagination

import { pageRequest, paginate, paginated, hasNextPage } from "viem-cosmos"

// Single page
const page = pageRequest({ limit: 50n })
const { validators, pageResponse } = await client.validators({
  args: ["BOND_STATUS_BONDED", page],
})
if (hasNextPage(pageResponse)) { /* fetch next */ }

// Lazy async generator — one page per round-trip, exit early
for await (const page of paginated(
  (req, signal) =>
    client.validators({ args: ["BOND_STATUS_BONDED", req] })
      .then(r => [r.validators, r.pageResponse]),
  { limit: 50n, signal },
)) {
  render(page)
  if (foundWhatINeed) break
}

// Eager collect-all
const all = await paginate(
  (req) =>
    client.validators({ args: ["BOND_STATUS_BONDED", req] })
      .then(r => [r.validators, r.pageResponse]),
  { limit: 100n },
)

Gov proposal encoding

import { encodeGovProposalJson } from "viem-cosmos"

const jsonProposal = encodeGovProposalJson({
  messages: [{
    "@type": "/cosmos.bank.v1beta1.MsgSend",
    from_address: govModuleAddress,
    to_address: recipient,
    amount: [{ denom: "uatom", amount: "1" }],
  }],
  title: "Community pool spend",
  summary: "Send 1 uatom from the gov module account.",
})

await client.submitProposal({ args: [proposer, jsonProposal, deposit] })

Pure transforms

The raw transform functions are exported for use with wagmi's select, multicall, or any other composition pattern that bypasses the decorator layer:

import { formatValidators, formatProposal, formatVote, formatSigningInfo } from "viem-cosmos"

// Use with wagmi useReadContract select:
const { data } = useReadContract({
  abi: stakingIAbi,
  functionName: "validators",
  select: (raw) => formatValidators(raw, { valoper: "cosmosvaloper" }),
})

Available: formatValidator, formatValidators, formatDelegation, formatProposal, formatProposals, formatTallyResult, formatVote, formatVotes, formatSigningInfo, formatSigningInfos, formatSlashingParams, formatDecCoins, formatDecStruct.

Typed errors

All errors extend viem's BaseError and integrate with error.walk(...):

import {
  CosmosBaseError,
  InvalidBech32AddressError,
  Bech32PrefixMismatchError,
  InvalidAddressLengthError,
  ChainMissingBech32PrefixError,
  InvalidDecimalScaleError,
  UnknownEnumValueError,
  InvalidPageRequestError,
} from "viem-cosmos"

try {
  await client.delegate({ args: [account, "not-a-valoper", amount] })
} catch (e) {
  if (e instanceof Bech32PrefixMismatchError) { /* wrong chain or role */ }
  if (e instanceof CosmosBaseError) { /* any viem-cosmos error */ }
}

Custom precompile factory

Build a decorator for any precompile ABI not yet shipped as a named decorator:

import { precompileActions } from "viem-cosmos"
import { ics20IAbi } from "cosmos-evm-contracts/precompiles/ics20/ICS20I"

const ics20Actions = precompileActions(
  ics20IAbi,
  "0x0000000000000000000000000000000000000802",
)

const client = publicClient.extend(ics20Actions())

Addresses

Default precompile addresses are exported and used automatically by every decorator:

import { precompileAddresses } from "viem-cosmos"
// {
//   bank:         "0x0000000000000000000000000000000000000804",
//   staking:      "0x0000000000000000000000000000000000000800",
//   distribution: "0x0000000000000000000000000000000000000801",
//   gov:          "0x0000000000000000000000000000000000000805",
//   slashing:     "0x0000000000000000000000000000000000000806",
// }

A test asserts these never drift from the *_PRECOMPILE_ADDRESS constants in cosmos-evm-contracts. Override per decorator or per call if a chain wires a precompile to a different address:

// per decorator
const client = publicClient.extend(bankActions({ precompileAddress: "0x…" }))

// per combined client
const client = createCosmosClient({
  chain, transport: http(),
  precompiles: { bank: { precompileAddress: "0x…" } },
})

Testing

Unit tests run without a chain; e2e tests exercise the full stack against the evmd reference chain via cosmock, including a gov proposal lifecycle (submit → deposit → vote → tally) and slashing signing-info reads.

pnpm --filter viem-cosmos typecheck   # tsc --noEmit
pnpm --filter viem-cosmos test        # unit tests (no chain required)
pnpm --filter viem-cosmos test:e2e    # on-chain e2e (local Cosmos EVM via cosmock)
pnpm --filter viem-cosmos build       # tsdown → dist (ESM + CJS + d.ts)

Status

v0.x — API is not yet stable. The validator-economy precompiles (bank, staking, distribution, gov, slashing) are covered. Remaining precompiles (ICS-20, vesting, …) are reachable today via precompileActions and will receive named decorators in future releases.

License

MIT © Wonhee Lee