The @sei-js/cosmjs
package contains helper functions for wallet connection, transaction signing, and RPC querying using cosmjs.
When first developing an application on Sei, you must first choose which RPC interface you want to use. This package is designed to work with cosmjs and the Cosmos ecosystem. If you would like to use EVM RPC, you should use the @sei-js/evm
package.
When building a dApp on Sei, it is important to understand the Cosmos SDK. Specifically, it is important to know how transactions are structured with different messages, gas fees, and signers. For an in-depth overview of the Cosmos SDK, see the Cosmos SDK documentation.
CosmJS is a JavaScript/TypeScript library for building applications on the Cosmos network. It provides a set of tools for interacting with the Cosmos network, including wallet management, transaction signing, and RPC querying. For more information, see the CosmJS documentation.
yarn add @sei-js/cosmjs
In order to sign and broadcast transactions, you must first connect to a wallet. This package contains helper functions to connect to the Sei wallet extension, or to create a wallet from a seed phrase.
Cosmos Kit is a helpful library for connecting to various Sei wallet extensions. It is recommended to use this library and to take advantage of all the useful hooks, utilities, and UI components it provides. For more information, see the Cosmos Kit documentation
If you prefer to manage wallet connections manually, you can use the native interface of the wallet extension to connect to a wallet. See the Compass documentation for more information on how to connect to a wallet extension.
In some circumstances it is necessary to generate a new wallet or to restore a wallet from a given seed phrase.
import { generateWallet, restoreWallet } from "@sei-js/cosmjs";
// 12 word mnemonic by default
const generatedWallet = await generateWallet();
console.log('generated mnemonic', generatedWallet.mnemonic);
// or restore a wallet given a seed phrase
const restoredWallet = await restoreWallet(SEED_PHRASE);
console.log('restored mnemonic', restoredWallet.mnemonic);
//Both the above functions have optional parameters for selecting a certain account index in a given wallet
When querying a Sei chain it is helpful to use a query client from @sei-js in order to have the correct registry and amino types. It is recommended that you do not use common web2 request libraries (fetch, axios, request) as they will not have typed query and response types.
import { getQueryClient } from '@sei-js/cosmjs';
const queryClient = await getQueryClient(REST_URL);
// Getting the market summary from the Sei dex module
const dexMarketSummary = await queryClient.seiprotocol.seichain.dex.getMarketSummary(params);
// Query the bank balance of a given address
const balances = await queryClient.cosmos.bank.v1beta1.allBalances({ address });
// Query a specific transaction hash
const txInfo = await queryClient.cosmos.tx.v1beta1.getTx({ hash });
@sei-js contains helper functions to get a SigningStargateClient with default Sei protocol registry and amino types.
import { getSigningStargateClient } from '@sei-js/cosmjs';
// Don't forget to connect if using a wallet extension
// or create a wallet from a mnemonic (See above)
await window.compass.connect(chainId);
const offlineSigner = await window.compass.getOfflineSigner(chainId);
const signingClient = await getSigningStargateClient(RPC_URL, offlineSigner);
Using Custom Registry or Amino Types:
import { Registry } from "@cosmjs/proto-signing";
import { defaultRegistryTypes } from "@cosmjs/stargate";
import { getSigningStargateClient } from '@sei-js/cosmjs';
import { seiprotocol, seiprotocolProtoRegistry } from "@sei-js/proto";
// Set up Sei proto registry
const registry = new Registry([
...defaultRegistryTypes,
...seiprotocolProtoRegistry,
]);
// Create a client with registry
const signingClient = await getSigningStargateClient(RPC_URL, offlineSigner, { registry });
import { calculateFee } from '@cosmjs/stargate';
const fee = calculateFee(100000, "0.1usei");
const amount = { amount: SEND_AMOUNT, denom: TOKEN_DENOM };
const sendResponse = await signingClient.sendTokens(SENDER_ADDRESS, DESTINATION_ADDRESS, [amount], fee);
import { calculateFee } from '@cosmjs/stargate';
const amount = { amount: SEND_AMOUNT, denom: TOKEN_DENOM };
const ibcResponse = await signingClient.sendIbcTokens(SENDER_ADDRESS, DESTINATION_ADDRESSS, amount, 'transfer', CHANNEL_ID, undefined, undefined, fee)
// Create message to place an order
const { placeOrders } = seiprotocol.seichain.dex.MessageComposer.withTypeUrl;
const msg = placeOrders({ contractAddr, creator, funds, orders });
const fee = calculateFee(150000, "0.1usei");
// Sign and broadcast the message
const response = signingClient.signAndBroadcast(firstAccount.address, [msg], fee);
@sei-js contains helper functions to get a CosmWasmClient
. Thi should be used for querying smart contracts only. If you need to call a contracts execute msg, see the SigningCosmWasmClient
below.
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { getCosmWasmClient } from "@sei-js/cosmjs";
// Create a CosmWasmClient
const cosmWasmClient = await getCosmWasmClient(RPC_URL);
Build the queryMsg
according to the contracts specific query specifications. Each contract will define its own queryable state, so check the contract documentation for the correct query message format by examining the contract source code or documentation.
// Create the query msg json based on contract specific query specifications
const queryMsg = {
tokens: {
owner: address,
},
};
// Query a smart contract state
const queryResponse = cosmWasmClient.queryContractSmart(CONTRACT_ADDR, queryMsg);
This package contains helper functions to get a SigningCosmWasmClient
with Sei registry and amino types from @sei-js/proto used for smart contract execute messages.
import { getSigningCosmWasmClient } from "@sei-js/cosmjs";
// Create a CosmWasmClient
const signingCosmWasmClient = await getSigningCosmWasmClient(RPC_URL, offlineSigner);
Build the executeMsg
according to the contracts specific specifications. Each contract will define its own execute functions, so check the contract documentation for the correct execute message format by examining the contract source code or documentation.
import { getSigningCosmWasmClient } from "@sei-js/cosmjs";
// Create a CosmWasmClient
const signingCosmWasmClient = await getSigningCosmWasmClient(RPC_URL, offlineSigner);
// Execute a message on a smart contract
const fee = calculateFee(150000, "0.1usei");
const msg = { mint: {} };
const result = await signingCosmWasmClient.execute(SENDER_ADDRESS, CONTRACT_ADDRESS, msg, fee);
You can validate that a given string is a valid Sei address:
import { isValidSeiAddress } from '@sei-js/cosmjs';
const isValidSeiAddress = isValidSeiAddress(ADDRESS_TO_TEST);
Sometimes it is necessary to prove account ownership without executing anything on chain. To achieve this verification you can use the signArbitrary function in all official Sei wallets to create a StdSignature which can be verified using the verifyArbitrary
function from @sei-js/cosmjs
import { verifyArbitrary } from "@sei-js/cosmjs";
const stdSignature = await window.compass.signArbitrary('atlantic-2', SEI_ADDRESS, "Message to sign"); // or FIN_WALLET, KEPLR_WALLET, LEAP_WALLET
In order to sign a transaction for a multi-sig transaction you must know the sequence and account number of the multi-sig account on chain. Start by querying that on chain.
import { getStargateClient } from '@sei-js/cosmjs';
const broadcaster = await getStargateClient(rpcUrl);
const multiSigAccount = await broadcaster.getAccount(multiSigAccountAddress);
const multiSigPubkey = account.pubkey as unknown as MultisigThresholdPubkey;
// If the account has not broadcasted any transactions, the pubkey will not be available.
if (!isMultisigThresholdPubkey(multiSigPubkey)) {
console.log('The account address you entered is not a multi-sig account that exists on chain.');
}
Note that if the multi sig account has neither broadcasted a transaction nor received funds, the call to `broadcaster.getAccount(multiSigAccountAddress) will fail since the account information will not exist on chain.
Repeat this step by signing the exact same msg, with the same fee across all the required accounts in the multi-sig.
import { getSigningStargateClient } from '@sei-js/cosmjs';
const offlineAminoSigner = await window.compass.getOfflineSignerAmino(chainId); // Or use another way to get the offline amino signer
const signingClient = await getSigningStargateClient(rpcUrl, offlineAminoSigner); // Or use another way to get a StargateSigningClient
const accounts = await offlineAminoSigner.getAccounts();
const response = await signingClient.sign(accounts[0].address, [YOUR_TX_MSG], YOUR_TX_FEE, '', {
accountNumber: multiSigAccount.accountNumber, // from step 1
sequence: multiSigAccount.sequence, // from step 1
chainId
});
Once all the signatures have been collected, we can broadcast the transaction from the multi-sig account.
const broadcaster = await StargateClient.connect(rpcUrl);
const multiSigAccount = await broadcaster.getAccount(multiSigAccountAddress);
const multiSigPubkey = multiSigAccount.pubkey as unknown as MultisigThresholdPubkey;
const signatures = new Map<string, Uint8Array>([
[firstAddress, firstSignResponse.signatures[0]],
[secondAddress, secondSignResponse.signatures[0]],
// Repeat for however many signatures are required
]);
const multiSignedTxBytes = makeMultisignedTxBytes(
multiSigPubkey,
multiSigAccount.sequence,
TX_FEE,
firstSignResponse.bodyBytes, // or any signature since all signatures sign the same body
signatures
);
const result = await broadcaster.broadcastTx(multiSignedTxBytes);