Skip to content

Latest commit

 

History

History
203 lines (147 loc) · 8.17 KB

cosmjs.md

File metadata and controls

203 lines (147 loc) · 8.17 KB
title order description tag
CosmJS
20
Understanding the TypeScript library for the Cosmos SDK
deep-dive

CosmJS

CosmJS provides a TypeScript library for the Cosmos SDK. Reading the following sections as a preparation is recommended:

CosmJS is a library created to talk to the Cosmos SDK. It is a powerful tool, which can be used to create wallets, explorers, IBC relayers, and other decentralized applications (dApps). It is written in TypeScript and can therefore be client or server side.

Your chain

In the checkers blockchain exercise you created a chain using Ignite CLI. Ignite CLI generated several components including a UI. Under the hood, this UI uses Vue.js and CosmJS to interact with the services exposed by the chain.

You used Ignite CLI to create the definitions for MsgCreatePost by running the command:

$ ignite scaffold message createPost title body

Your module in the UI

Ignite CLI also created UI-side elements to facilitate integration, for example:

import { StdFee } from "@cosmjs/launchpad";
import { SigningStargateClient } from "@cosmjs/stargate";
import { Registry, OfflineSigner, EncodeObject, DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { Api } from "./rest";
import { MsgCreatePost } from "./types/checkers/tx";


const types = [
  ["/alice.checkers.checkers.MsgCreatePost", MsgCreatePost],

];
export const MissingWalletError = new Error("wallet is required");

const registry = new Registry(<any>types);

const defaultFee = {
  amount: [],
  gas: "200000",
};

interface TxClientOptions {
  addr: string
}

interface SignAndBroadcastOptions {
  fee: StdFee,
  memo?: string
}

const txClient = async (wallet: OfflineSigner, { addr: addr }: TxClientOptions = { addr: "http://localhost:26657" }) => {
  if (!wallet) throw MissingWalletError;

  const client = await SigningStargateClient.connectWithSigner(addr, wallet, { registry });
  const { address } = (await wallet.getAccounts())[0];

  return {
    signAndBroadcast: (msgs: EncodeObject[], { fee, memo }: SignAndBroadcastOptions = {fee: defaultFee, memo: ""}) => client.signAndBroadcast(address, msgs, fee,memo),
    msgCreatePost: (data: MsgCreatePost): EncodeObject => ({ typeUrl: "/alice.checkers.checkers.MsgCreatePost", value: data }),

  };
};

interface QueryClientOptions {
  addr: string
}

const queryClient = async ({ addr: addr }: QueryClientOptions = { addr: "http://localhost:1317" }) => {
  return new Api({ baseUrl: addr });
};

export {
  txClient,
  queryClient,
};
import { SigningStargateClient } from "@cosmjs/stargate";
import { Registry } from "@cosmjs/proto-signing";
import { Api } from "./rest";
import { MsgCreatePost } from "./types/checkers/tx";
const types = [
    ["/alice.checkers.checkers.MsgCreatePost", MsgCreatePost],
];
export const MissingWalletError = new Error("wallet is required");
const registry = new Registry(types);
const defaultFee = {
    amount: [],
    gas: "200000",
};
const txClient = async (wallet, { addr: addr } = { addr: "http://localhost:26657" }) => {
    if (!wallet)
        throw MissingWalletError;
    const client = await SigningStargateClient.connectWithSigner(addr, wallet, { registry });
    const { address } = (await wallet.getAccounts())[0];
    return {
        signAndBroadcast: (msgs, { fee, memo } = { fee: defaultFee, memo: "" }) => client.signAndBroadcast(address, msgs, fee, memo),
        msgCreatePost: (data) => ({ typeUrl: "/alice.checkers.checkers.MsgCreatePost", value: data }),
    };
};
const queryClient = async ({ addr: addr } = { addr: "http://localhost:1317" }) => {
    return new Api({ baseUrl: addr });
};
export { txClient, queryClient, };

In this file your module's client-side services are defined.

What does that code do?

  1. It starts by importing @cosmjs/stargate, which is the client library for Cosmos SDK 0.40, and the following versions named Stargate.
  2. Then it imports @cosmjs/proto-signing which encapsulates knowledge on how to sign Msg objects created with Protobuf. Lower down, it adds your MsgCreatePost type to the registry in this case.
  3. Mirroring your Go code, your message type MsgCreatePost is defined in [...]]alice.checkers.checkers/module/types/checkers/tx.js using Protobuf.js. This is the TypeScript/​JavaScript counterpart of Protobuf in Go - that you saw earlier. With this both ends speak the same serialization language.
  4. http://localhost:26657 is the default Tendermint RPC node endpoint used to send transactions. And is indeed passed here only as a default value if it is missing.
  5. http://localhost:1317 is the default high-level blockchain API endpoint. In the above code it is used for queries.
  6. The created elements txClient and queryClient are returned to be used in ./vue/src/store/generated/alice/checkers/alice.checkers.checkers/index.js as you can see in the previous Ignite CLI section.

Details on the client

In the nested function above you can see that @cosmjs/stargate is used for signing and broadcasting:

const client = await SigningStargateClient.connectWithSigner(addr, wallet, { registry });
const { address } = (await wallet.getAccounts())[0];
return {
  signAndBroadcast: (msgs: EncodeObject[], { fee, memo }: SignAndBroadcastOptions = {fee: defaultFee, memo: ""}) => client.signAndBroadcast(address, msgs, fee,memo),
  msgCreatePost: (data: MsgCreatePost): EncodeObject => ({ typeUrl: "/alice.checkers.checkers.MsgCreatePost", value: data }),
};
const client = await SigningStargateClient.connectWithSigner(addr, wallet, { registry });
const { address } = (await wallet.getAccounts())[0];
return {
    signAndBroadcast: (msgs, { fee, memo } = { fee: defaultFee, memo: "" }) => client.signAndBroadcast(address, msgs, fee, memo),
    msgCreatePost: (data) => ({ typeUrl: "/alice.checkers.checkers.MsgCreatePost", value: data }),
};

The following is taking place:

  1. SigningStargateClient.connectWithSigner is a way to connect with a wallet and obtain a signing client. The addr is passed so that it can be used as part of the information prompted to the user when confirming if an external wallet is used.
  2. An object with two functions is returned: msgCreatePost and signAndBroadcast.
  3. signAndBroadcast is called when the UI finishes creating the message and is about to sign and broadcast the message. If using an external wallet, the wallet prompts the user to confirm the signing.

@cosmjs/proto-signing provides DirectSecp256k1HdWallet as a wallet. You cannot see it in the code above because it is wrapped through Ignite CLI and Vue.

If you want to use a specific mnemonic, for instance on a server, instead of importing an external wallet via the UI, you can do so with:

const mnemonic =
  "ivory uniform actual spot floor vessel monster rose yellow noise smile odor veteran human reason miss stadium phrase assault puzzle sentence approve coral apology";
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic);

There are many other useful methods like assertIsBroadcastTxSuccess that verifies whether the transaction was successful or not, as used in this sample snippet.

Next up

Ready for the final section of this course? Discover CosmWasm and multi-chain smart contracting.