Skip to main content

Typescript code generation

The ignite generate ts-client command generates a Typescript client for your blockchain project.

Typescript client code can be automatically regenerated on reset or source code changes when the blockchain is started with the --generate-clients options.

Client code generation

To configure TypeScript (TS) client code generation add the client settings to config.yml:

client:
typescript:
path: ts-client

A TS client is generated in the ts-client directory when using this configuration.

Client code regeneration

To regenerate all clients for custom and standard Cosmos SDK modules, run this command:

ignite generate ts-client

Setup

The best way to get started building with the TypeScript client is by using a Vite boilerplate. Vite provides boilerplate code for vanilla TS projects as well as react, vue, lit, svelte and preact frameworks. You can find additional information at the Vite Getting Started guide.

You will also need to polyfill the client's dependencies. The following is an example of setting up a vanilla TS project with the necessary polyfills.

npm create [email protected] my-frontend-app -- --template vanilla-ts
npm install --save-dev @esbuild-plugins/node-globals-polyfill @rollup/plugin-node-resolve

You must then create the necessary vite.config.ts file.

import { nodeResolve } from '@rollup/plugin-node-resolve'
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
import { defineConfig } from 'vite'

export default defineConfig({

plugins: [nodeResolve()],

optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis',
},
plugins: [
NodeGlobalsPolyfillPlugin({
buffer:true
}),
],
},
}
})

You are then ready to use the generated client code inside this project directly or by publishing the client and installing it as any other npm package.

Usage

The code generated in ts-client comes with a package.json file ready to publish which you can modify to suit your needs.

The client is based on a modular architecture where you can configure a client class to support the modules you need and instantiate it.

By default, the generated client exports a client class that includes all the Cosmos SDK, custom and 3rd party modules in use in your project.

To instantiate the client you need to provide environment information (endpoints and chain prefix) and an optional wallet (implementing the CosmJS OfflineSigner interface).

For example, to connect to a local chain instance running under the Ignite CLI defaults, using a CosmJS wallet:

import { Client } from '<path-to-ts-client>';
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";

const mnemonic = "surround miss nominee dream gap cross assault thank captain prosper drop duty group candy wealth weather scale put";
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic);

const client = new Client({
apiURL: "http://localhost:1317",
rpcURL: "http://localhost:26657",
prefix: "cosmos"
},
wallet
);

The resulting client instance contains namespaces for each module, each with a query and tx namespace containing the module's relevant querying and transacting methods with full type and auto-completion support.

e.g.

const balances = await client.CosmosBankV1Beta1.query.queryAllBalances('cosmos1qqqsyqcyq5rqwzqfys8f67');

And for transactions:

const tx_result = await client.CosmosBankV1Beta1.tx.sendMsgSend(
{
value: {
amount: [
{
amount: '200',
denom: 'token',
},
],
fromAddress: 'cosmos1qqqsyqcyq5rqwzqfys8f67',
toAddress: 'cosmos1qqqsyqcyq5rqwzqfys8f67'
},
fee,
memo
}
);

If you prefer, you can construct a lighter client using only the modules you are interested in by importing the generic client class and expanding it with the modules you need:

import { IgniteClient } from '<path-to-ts-client>/client';
import { Module as CosmosBankV1Beta1 } from '<path-to-ts-client>/cosmos.bank.v1beta1'
import { Module as CosmosStakingV1Beta1 } from '<path-to-ts-client>/cosmos.staking.v1beta1'
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";

const mnemonic = "surround miss nominee dream gap cross assault thank captain prosper drop duty group candy wealth weather scale put";
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic);
const CustomClient = IgniteClient.plugin([CosmosBankV1Beta1, CosmosStakingV1Beta1]);

const client = new CustomClient({
apiURL: "http://localhost:1317",
rpcURL: "http://localhost:26657",
prefix: "cosmos"
},
wallet
);

You can also construct TX messages separately and send them in a single TX using a global signing client like so:

const msg1 = await client.CosmosBankV1Beta1.tx.msgSend(
{
value: {
amount: [
{
amount: '200',
denom: 'token',
},
],
fromAddress: 'cosmos1qqqsyqcyq5rqwzqfys8f67',
toAddress: 'cosmos1qqqsyqcyq5rqwzqfys8f67'
}
}
);
const msg2 = await client.CosmosBankV1Beta1.tx.msgSend(
{
value: {
amount: [
{
amount: '200',
denom: 'token',
},
],
fromAddress: 'cosmos1qqqsyqcyq5rqwzqfys8f67',
toAddress: 'cosmos1qqqsyqcyq5rqwzqfys8f67'
},
}
);
const tx_result = await client.signAndBroadcast([msg1, msg2], fee, memo);

Finally, for additional ease-of-use, apart from the modular client mentioned above, each generated module is usable on its own in a stripped-down way by exposing a separate txClient and queryClient.

e.g.

import {queryClient} from '<path-to-ts-client>/cosmos.bank.v1beta1';

const client = queryClient({addr: 'http://localhost:1317'});
const balances = await client.queryAllBalances('cosmos1qqqsyqcyq5rqwzqfys8f67');

and

import { txClient } from '<path-to-ts-client>/cosmos.bank.v1beta1';
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";

const mnemonic = "surround miss nominee dream gap cross assault thank captain prosper drop duty group candy wealth weather scale put";
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic);

const client = txClient({
signer: wallet,
prefix: 'cosmos',
addr: 'http://localhost:26657'
});

const tx_result = await client.sendMsgSend(
{
value: {
amount: [
{
amount: '200',
denom: 'token',
},
],
fromAddress: 'cosmos1qqqsyqcyq5rqwzqfys8f67',
toAddress: 'cosmos1qqqsyqcyq5rqwzqfys8f67'
},
fee,
memo
}
);

Usage with Keplr

Normally, Keplr provides a wallet object implementing the OfflineSigner interface so you can simply replace the wallet argument in client instantiation with it like so:

import { Client } from '<path-to-ts-client>';

const chainId = 'mychain-1'
const client = new Client({
apiURL: "http://localhost:1317",
rpcURL: "http://localhost:26657",
prefix: "cosmos"
},
window.keplr.getOfflineSigner(chainId)
);

The problem is that for a new Ignite CLI scaffolded chain, Keplr has no knowledge of it thus requiring an initial call to experimentalSuggestChain() method to add the chain information to the user's Keplr instance.

The generated client makes this easier by offering a useKeplr() method that autodiscovers the chain information and sets it up for you. Thus you can instantiate the client without a wallet and then call useKeplr() to enable transacting via Keplr like so:

import { Client } from '<path-to-ts-client>';

const client = new Client({
apiURL: "http://localhost:1317",
rpcURL: "http://localhost:26657",
prefix: "cosmos"
}
);
await client.useKeplr();

useKeplr() optionally accepts an object argument that contains one or more of the same keys as the ChainInfo type argument of experimentalSuggestChain() allowing you to override the auto-discovered values.

For example, the default chain name and token precision (which are not recorded on-chain) are set to <chainId> Network and 0 while the ticker for the denom is set to the denom name in uppercase. If you wanted to override these, you could do something like:

import { Client } from '<path-to-ts-client>';

const client = new Client({
apiURL: "http://localhost:1317",
rpcURL: "http://localhost:26657",
prefix: "cosmos"
}
);
await client.useKeplr({ chainName: 'My Great Chain', stakeCurrency : { coinDenom: 'TOKEN', coinMinimalDenom: 'utoken', coinDecimals: '6' } });

Wallet switching

The client also allows you to switch out the wallet for a different one on an already instantiated client like so:

import { Client } from '<path-to-ts-client>';
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";

const mnemonic = "surround miss nominee dream gap cross assault thank captain prosper drop duty group candy wealth weather scale put";
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic);


const client = new Client({
apiURL: "http://localhost:1317",
rpcURL: "http://localhost:26657",
prefix: "cosmos"
}
);
await client.useKeplr();

// transact using Keplr Wallet

client.useSigner(wallet);

//transact using CosmJS wallet