@rebarxyz/rivet
v0.0.27
Published
Minimal Cosmos SDK transaction signing library
Downloads
510
Readme
@rebarxyz/rivet
Cosmos SDK transaction signing in one package. Handles message encoding, gas estimation, signing, and broadcasting with 3 runtime dependencies and full tree-shaking support.
Every encoding, signing, and key derivation output is cross-validated byte-for-byte against cosmjs across 171 tests.
Install
pnpm add @rebarxyz/rivetQuick Start
import { Rivet, defineProto } from '@rebarxyz/rivet';
import { MsgSend, MsgSendResponse, protobufPackage } from 'cosmjs-types/cosmos/bank/v1beta1/tx';
// defineProto wraps protobuf codecs into typed, callable helpers
const bank = defineProto({ MsgSend, MsgSendResponse }, protobufPackage);
const rivet = Rivet.connect('https://rpc.cosmos.network', {
wallet: offlineSigner,
gasConfig: { multiplier: 1.75, gasPrice: '0.025uatom' },
});
const result = await rivet.signAndBroadcast({
messages: [bank.Send({
fromAddress: sender,
toAddress: recipient,
amount: [{ denom: 'uatom', amount: '1000000' }],
})],
});
const response = bank.Send.decodeResponse(result);Why Rivet?
Single package. One import for message encoding, signing, gas estimation, broadcasting, and ABCI queries. No coordinating between proto-signing, stargate, amino, crypto, and encoding.
No protobuf runtime. The 15 Cosmos SDK types needed for transactions are hand-coded in ~220 lines. No protobufjs (800+ KB) or @bufbuild/protobuf in your dependency tree. Your chain's message types come from your existing codegen (ts-proto or cosmjs-types) — Rivet works with both.
Actually tree-shakeable. sideEffects: false throughout, explicit exports (no barrel re-exports), and the HD wallet is a separate entry point so browser apps don't pay for BIP-32 key derivation. The full package with dependencies is ~40 KB gzipped.
Wallet support without wallet dependencies. OfflineDirectSigner is a structural type. Keplr's getOfflineSignerAuto() and Leap's equivalent satisfy it at runtime without importing anything from those wallets.
defineProto replaces manual registry wiring. Pass your codecs in, get typed callable helpers back. Naming convention does the classification — MsgSend becomes a message helper, MsgSendResponse gets paired for response decoding, QueryBalanceRequest/QueryBalanceResponse become a query helper.
Not in scope
Rivet is SIGN_MODE_DIRECT only. No Amino signing, no Ledger integration, no multisig, no EVM/ethermint chains, and no IBC types in the built-in protos (though IBC messages work fine as pre-encoded Any values via your own codegen). Only secp256k1 keys are supported.
Usage
Browser with Keplr/Leap
Keplr and Leap signers satisfy OfflineDirectSigner via structural typing — no wallet import dependencies needed.
import { Rivet } from '@rebarxyz/rivet';
const offlineSigner = await window.keplr.getOfflineSignerAuto('cosmoshub-4');
const rivet = Rivet.connect('https://rpc.cosmos.network', {
wallet: offlineSigner,
gasConfig: { multiplier: 1.75, gasPrice: '0.025uatom' },
});Server with HD Wallet
The HD wallet lives in a separate entry point (@rebarxyz/rivet/wallet) so browser bundles don't pull in BIP-32 key derivation.
import { Rivet } from '@rebarxyz/rivet';
import { HDWallet } from '@rebarxyz/rivet/wallet';
const wallet = HDWallet.fromMnemonic(process.env.MNEMONIC, { prefix: 'cosmos' });
const rivet = Rivet.connect('https://rpc.cosmos.network', {
wallet,
gasConfig: { gasPrice: '0.025uatom' },
});defineProto
defineProto accepts codecs from ts-proto or cosmjs-types. It classifies keys by naming convention — Msg${X} becomes a message helper, Msg${X}Response gets paired for response decoding, and Query${X}Request/Query${X}Response pairs become query helpers.
import { defineProto } from '@rebarxyz/rivet';
import {
MsgSend, MsgSendResponse,
QueryBalanceRequest, QueryBalanceResponse,
protobufPackage, // needed helper for ts-proto; not required for cosmjs-types
} from './generated/cosmos/bank/v1beta1/tx';
const bank = defineProto({
MsgSend, MsgSendResponse,
QueryBalanceRequest, QueryBalanceResponse,
}, protobufPackage);
// Encode a message (ready to pass to signAndBroadcast)
const encodedMsg = bank.Send({ fromAddress, toAddress, amount });
// Decode a message response from a broadcast transaction
const decodedResponse = bank.Send.decodeResponse(broadcastResult);
// Query and return the decoded result
const bal = await bank.Balance(rivet, { address, denom });Read-Only
const rivet = Rivet.connect('https://rpc.cosmos.network');
const balance = await bank.Balance(rivet, { address: 'cosmos1...', denom: 'uatom' });Dependencies
| Dependency | Purpose | Size |
|---|---|---|
| @noble/curves | secp256k1 sign/verify/pubkey | ~33 KB gzipped |
| @noble/hashes | SHA-256, RIPEMD-160, PBKDF2 | (dep of @noble/curves) |
| bech32 | Address encoding | ~3 KB |
| @scure/bip32 (optional) | HD key derivation, @rebarxyz/rivet/wallet only | ~36 KB |
Errors
RivetError
BroadcastError
InsufficientFundsError
SimulationError
AccountNotFoundError
RpcError
SigningRejectedErrorAll errors carry structured context (error codes, tx hashes, RPC endpoints). Private keys and mnemonics are never included in error messages.
Testing
171 tests across 11 files. The cross-validation suite does byte-for-byte differential testing against cosmjs: address derivation, proto encoding (with fuzz), signing, full tx pipeline roundtrips, cross-implementation decode interop, and wallet compatibility (CosmJS/Keplr response format handling).
pnpm testComparison
| | Rivet | cosmjs | interchainjs |
|---|---|---|---|
| npm packages needed | 1 | 4-6 | 5+ |
| Bundle size (w/ deps) | ~40 KB gzipped | ~1 MB+ | ~2 MB+ |
| Protobuf runtime | Hand-coded (15 types) | protobufjs (800+ KB) | @bufbuild/protobuf |
| sideEffects: false | Yes | No | No |
| Barrel re-exports | No | Yes | Yes (entire cosmos-types) |
| Message registration | defineProto auto-classifies | Manual registry.register() | Manual codec setup |
| Amino signing | No | Yes | Yes |
| Ledger support | No | Yes | Yes |
| Multisig | No | Yes | Yes |
| EVM/ethermint chains | No | Limited | Yes |
| Key algorithms | secp256k1 only | secp256k1, ed25519 | secp256k1, ed25519, ethsecp256k1 |
| IBC types built-in | No (use your own codegen) | Yes | Yes |
Rivet is the right choice if you want a small, tree-shakeable signing library for SIGN_MODE_DIRECT on standard Cosmos SDK chains and you're already running your own protobuf codegen. If you need Amino signing, Ledger, multisig, or EVM chain support, use cosmjs or interchainjs.
Architecture
See ARCHITECTURE.md for module diagrams, the signing flow, and entry point tree-shaking details.
