Using CosmJS
with WASM Contracts
The tutorial provides example on how to use the CosmJS
package to instantiate, execute and query CosmWASM Smart Contracts deployed on the Coreum blockchain.
Installing dependencies
Install the CosmJS dependency.
npm install @cosmjs/stargate @cosmjs/cosmwasm-stargate
Preparing test account
Before you may broadcast transactions, you need to have access to a funded account. Normally you would create a private key stored securely in a wallet. Here, for simplicity, we will use mnemonic generated by our faucet. Don't use the mnemonic directly in code and never ever use the key generated by the faucet in mainnet. It might cause complete funds loss!
To get a funded account, go to our faucet website, that you can find here
and click on "Generate Funded Wallet" button in "Testnet" section.
Assign mnemonic to the constant senderMnemonic
in the code snippet below.
Generate TS based on Smart Contract messages
For convenience, there are CosmWasm macros that There are CosmWasm macros that allow you to generate the JSON schemas of your Smart Contract, which can be used by code generation tools to generate TypeScript SDKs for your Smart Contracts. Follow these steps:
- Install ts-codegen:
npm install -g @cosmwasm/ts-codegen
- Inside the src folder of your Smart Contract, create a bin folder with the following code:
use cosmwasm_schema::write_api;
use your_contract_name::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
fn main() {
write_api! {
instantiate: InstantiateMsg,
execute: ExecuteMsg,
query: QueryMsg,
}
}
- Generate the schema running the following command inside your contract's folder:
cargo run --bin schema
This will generate another folder called schema with your JSON files inside.
- Run the cosmwasm-ts-codegen command to generate your code:
cosmwasm-ts-codegen generate \
--plugin client \
--schema ./schema \
--out ./ts \
--name MyProject \
--no-bundle
This will generate two TypeScript files. One with all the message types that your contract supports and one client file that you will initialize with your SigningClient and your contract address and that contains all the functions that will interact with your Smart Contract, for convenience.
Preparing coreum settings
Before we are able to broadcast transaction, we must set up chain specific configuration:
const coreumAccountPrefix = "testcore"; // the address prefix (different for different chains/environments)
const coreumHDPath = "m/44'/990'/0'/0/0"; // coreum HD path (same for all chains/environments)
const coreumDenom = "utestcore"; // core denom (different for different chains/environments)
const coreumRpcEndpoint = "https://full-node.testnet-1.coreum.dev:26657"; // rpc endpoint (different for different chains/environments)
const senderMnemonic = "putYourMnemonicHere"; // put mnemonic here
This configuration is for testnet. Parameters of other networks are available at network variables.
Prepare RPC/tendermint clients.
The clients will be reused by multiple samples later.
const httpClient = new HttpBatchClient(coreumRpcEndpoint);
const tendermintClient = await Tendermint34Client.create(httpClient);
const queryClient = QueryClient.withExtensions(tendermintClient, setupWasmExtension);
const rpcClient = createProtobufRpcClient(queryClient);
const feemodelQueryClient = new FeemodelQueryClient(rpcClient)
GasPrice
To obtain the correct GasPrice we can use this function.
export async function getGasPriceWithMultiplier(feemodelQueryClient: FeemodelQueryClient) {
const gasPriceMultiplier = 1.1
const recommendedGasPriceRes = await feemodelQueryClient.RecommendedGasPrice({ AfterBlocks: 10 })
const recommendedGasPrice = decodeCosmosSdkDecFromProto(recommendedGasPriceRes.low?.amount || "")
let gasPrice = recommendedGasPrice.toFloatApproximation() * gasPriceMultiplier
return GasPrice.fromString(`${gasPrice}${recommendedGasPriceRes.low?.denom || ""}`);
}
Prepare sender client
To sign transactions, you need to set up the new account-specific CosmWsam client.
console.log("preparing sender wallet");
const senderWallet = await DirectSecp256k1HdWallet.fromMnemonic(senderMnemonic, {
prefix: coreumAccountPrefix,
hdPaths: [stringToPath(coreumHDPath)],
});
const [sender] = await senderWallet.getAccounts();
console.log(`sender address: ${sender.address}`);
const senderClient = await SigningCosmWasmClient.connectWithSigner(
coreumRpcEndpoint,
senderWallet,
{ gasPrice: getGasPriceWithMultiplier(feemodelQueryClient) }
);
This senderClient can be provided to the client that we generated before using ts-codegen along with the contract address to provide another layer of abstraction to ease or Smart Contract interactions. If we didn't generate the code with ts-codegen then we can create the JSON messages manually.
Instantiating a Smart Contract
- Using generated types:
import { InstantiateMsg } from "./MyProject.types";
const code_id = 547; // Here we put the code id of the contract that we uploaded on the Coreum blockchain.
const instantiateMsg: InstantiateMsg = { field1: "value", field2: "value" };
const instantiateResult = await senderClient.instantiate(sender.address, code_id, instantiateMsg, "yourlabel", "auto", {});
- Without generated types:
const code_id = 547; // Here we put the code id of the contract that we uploaded on the Coreum blockchain.
const instantiateMsg = { field1: "value", field2: "value" }; // Here we input a correct JSON corresponding to our contract's instantiate message.
const instantiateResult = await senderClient.instantiate(sender.address, code_id, instantiateMsg, "yourlabel", "auto", {});
Execute a Smart Contract
- Using generated types:
import { MyProjectClient } from "./MyProject.client";
const client = new MyProjectClient(senderClient, sender.address, instantiateResult.contractAddress);
const executeResult = client.messageName({ field1: "value", field2: "value"})
- Without generated types:
const executeMsg = { message_name : { field1: "value", field2: "value" }} //Example of a possible execute msg
const executeResult = senderClient.execute(sender.address, instantiateResult.contractAddress, executeMsg, "auto", "memo");
Query a Smart Contract
- Using generated types:
import { MyProjectQueryClient } from "./MyProject.client";
const cwClient = await CosmWasmClient.connect(coreumRpcEndpoint);
const qClient = new MyProjectQueryClient(cwClient, instantiateResult.contractAddress);
const queryResponse = qClient.queryName({ field1: "value", field2: "value"})
- Without generated types:
const queryMsg = { query_name: { field1: "value", field2: "value"} } //Example of a possible query msg
const queryResponse = queryClient.queryContractSmart(instantiateResult.contractAddress, queryMsg);
Using browser wallet extension APIs
If we want to create a sender client but instead of providing the wallet ourselves we want to use the wallet we are already using in our browser, we can use the APIs of each wallet to create the offline signer that we will provide to the client:
const senderClient = await SigningCosmWasmClient.connectWithSigner(
coreumRpcEndpoint,
offlineSigner,
{ gasPrice: getGasPriceWithMultiplier(feemodelQueryClient) }
);
For convenience there are already tools to integrate wallets into our applications to sign the messages we send to our contracts. Cosmos-kit is a wallet connector that offers packages for many wallets (both mobile and Web extensions) that we can use to build applications to interact with our Smart Contracts deployed on the Coreum blockchain.