Skip to content

Instantly share code, notes, and snippets.

@JakeHartnell
Created February 1, 2023 00:00
Show Gist options
  • Save JakeHartnell/14c5697c7bb395b96ad1ec7a3440d7ba to your computer and use it in GitHub Desktop.
Save JakeHartnell/14c5697c7bb395b96ad1ec7a3440d7ba to your computer and use it in GitHub Desktop.
Encoding, Signing, and Broadcasting transactions with CosmJS

Encoding, Signing, and Broadcasting transactions with CosmJS

By default, CosmJS builds, signs, and broadcasts a transactions in a single function call. Often, it's useful to separate these steps.

This gist explores a simple way of doing so in TypeScript, and may also be useful for those wanting to dive deeper into how Cosmos messages are composed and signed.

Setup

You will need a few dependencies:

You can install these with npm or yarn:

npm i cosmwasm cosmjs-types
# OR
yarn add cosmwasm cosmjs-types

High level overview of steps

1. Creating and encoding protobuf messages

Cosmos messages are encoded as protobufs so to create a transaction we'll first need to encode a protobuf message.

Telescope is a TypeScript Transpiler for Cosmos Protobufs. It can be used to efficiently generate protobuf message and will save you lots of time. However, we'll be doing it the manual way with cosmjs-types.

NOTE: Cosmos chains also support an older form of encoding known as amino that is used primarily with ledger devices.

Signing with a signing client

Signing a transaction needs to be done with a private key or signer. To do this we'll create a SigningStargateClient or SigningCosmWasmClient (which is mostly the same plus a great interface for working with CosmWasm smart contracts on chains like Juno Network).

Note: the signing client doesn't have to be online, the is a signingClient.offline() method demonstrated in the code example below.

For CosmJS, signers MUST implement the OfflineSigner interface. For an example of this, see the DirectSecp256k1HdWallet implementation in the cosmjs repo.

Some example signers include:

We'll demonstate creating a wallet purley in code below. See cosmos-kit if you're interested in working with third party wallets.

Broadcasting

After creating and signing the transaction, we need to broadcast it to the chain via an RPC node, for this we'll use a CosmwasmClient

TypeScript code example

NOTE: Many more code examples for all sorts of things exist in CosmJS tests for the various packages.

// Import needed dependencies from the cosmwasm package
import { coins, DirectSecp256k1HdWallet, SigningCosmWasmClient } from "cosmwasm";
// Import protobuf helpers from cosmjs-types
// Note: this the manual way of doing this, Telescope provides much
// nicer tooling for working with chain protobufs
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";

// Create a Secp256k1 wallet manually
// Note: there other ways to create a wallet, such as getting an OfflineDirectSigner from Keplr
// or using the @cosmjs/ledger-amino package. Wallets must implement the OfflineSigner interface.
// See: https://cosmos.github.io/cosmjs/latest/proto-signing/interfaces/OfflineDirectSigner.html
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(
  "enlist hip relief stomach skate base shallow young switch frequent cry park",
  { prefix: "juno" }
);

// Create a client connected an RPC endpoint
const client = await SigningCosmWasmClient.connect(
  "https://rpc.junonetwork.io",
);

// Create an offline client for signing the transaction
const offline = await SigningCosmWasmClient.offline(wallet);

// Get the account address
const account = await wallet.getAccounts();

// Encode a send message.
const msg = {
  typeUrl: "/cosmos.bank.v1beta1.MsgSend",
  value: MsgSend.fromPartial({
    fromAddress: account[0].address,
    toAddress: "juno1xyz...",
    amount: coins(1234, "ujuno"),
  }),
};
const fee = {
  amount: coins(2000, "ujuno"),
  gas: "200000",
};
const memo = "Use your tokens wisely";

// Sign a message
const signed = await offline?.sign(account[0].address, [msg], fee, memo);

// Broadcast the transaction the transaction, note it must be encode as raw protobuf transaction
client
  .broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()))
  .then((res) => {
    console.log(res);
  });

Other tools and resources

  • telescope: awesome tooling for working with Cosmos chain protobufs
  • cosmos-kit: a library for connecting to multiple wallets and a bunch of other stuff
  • create-cosmos-app: Easily create a cosmos frontend application
  • @cosmjs packages: the complete list of cosmjs packages
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment