WASM Integration Guide
End‑to‑end instructions for interacting with Sai from the Cosmos (WASM) side using NibiruTxClient.
0. Getting Started
Prereqs
Node.js ≥ 18
pnpm (or npm/yarn)
New Project
mkdir sai-wasm && cd sai-wasm
pnpm init -y # or: npm init -y
pnpm add @nibiruchain/nibijs @cosmjs/cosmwasm-stargate @cosmjs/stargate cosmjs-types bignumber.js
pnpm add -D typescript ts-node @types/node
npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNextMinimal Env
Create .env with your RPC + mnemonic or direct offline signer strategy.
NIBI_RPC=https://rpc.testnet-2.nibiru.fi # example
MNEMONIC="word1 word2 ..."Bootstrap Client
// src/wasmClient.ts
import "dotenv/config"
import { NibiruTxClient } from "@nibiruchain/nibijs"
export async function makeNibiruTxClient() {
const rpc = process.env.NIBI_RPC!
const mnemonic = process.env.MNEMONIC!
const client = await NibiruTxClient.connectWithSignerFromMnemonic(rpc, mnemonic)
const account = (await client.signer.getAccounts())[0]
return { client, bech32: account.address }
}1. Collateral / Payment Tokens
Default decimals for BANK amounts are 6.
Testnet‑2
USDC:
bankDenom = tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdcstNIBI:
bankDenom = tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/stnibi
Mainnet
USDC:
bankDenom = erc20/0x0829F361A05D993d5CEb035cA6DF3446b060970bstNIBI:
bankDenom = tf/nibi1udqqx30cw8nwjxtl4l28ym9hhrp933zlq8dqxfjzcdhvl8y24zcqpzmh8m/ampNIBI
Scale display amounts → BANK units via
amount * 10^6, floor to integer.
2. Perps – Open Trade
Msg
{
"open_trade": {
"market_index": "MarketIndex(N)",
"leverage": "string",
"long": true,
"collateral_index": "TokenIndex(N)",
"trade_type": "trade|limit|stop",
"open_price": "<market or limit>",
"slippage_p": "1",
"tp": "optional",
"sl": "optional",
"is_evm_origin": false
}
}Funds
[{ "denom": "<collateral.bankDenom>", "amount": "<BANK units>" }]Rules
Marketrequiresopen_price.Limit/StoprequirelimitPriceUsd(becomesopen_price).
Code
import BigNumber from "bignumber.js"
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin"
import { makeNibiruTxClient } from "./wasmClient"
async function openTrade() {
const { client, bech32 } = await makeNibiruTxClient()
const saiPerp = "<saiContracts.perp>" // bech32
const bankDenom = "<bankDenom>"
const display = new BigNumber("100") // 100 USDC
const amount = display.times(1e6).integerValue().toString()
const msg = {
open_trade: {
market_index: "MarketIndex(0)",
leverage: "5",
long: true,
collateral_index: "TokenIndex(0)",
trade_type: "trade",
open_price: "70000",
slippage_p: "1",
is_evm_origin: false,
},
}
const res = await client.wasmClient.execute(
bech32,
saiPerp,
msg,
"auto",
undefined,
[Coin.fromPartial({ denom: bankDenom, amount })]
)
console.log("tx hash:", res.transactionHash)
}3. Perps – Close Trade
await client.wasmClient.execute(
bech32,
saiPerp,
{ close_trade: { trade_index: "UserTradeIndex(0)" } },
"auto"
)4. Referral – Create & Redeem
await client.wasmClient.execute(
bech32,
saiPerp,
{ create_referrer_code: { code: "MYCODE" } },
"auto"
)
await client.wasmClient.execute(
bech32,
saiPerp,
{ redeem_referrer_code: { code: "PARTNER" } },
"auto"
)5. Vault – Deposit
const vaultAddr = "<vault bech32>"
const bankDenom = "<collateral.bankDenom>"
const amount = new BigNumber("250").times(1e6).integerValue().toString()
await client.wasmClient.execute(
bech32,
vaultAddr,
{ deposit: {} },
"auto",
undefined,
[Coin.fromPartial({ denom: bankDenom, amount })]
)6. Vault – Make Withdraw Request
Query share denom:
const shareDenom = await client.wasmClient.queryContractSmart(vaultAddr, { get_vault_share_denom: {} })Execute with share funds:
await client.wasmClient.execute(
bech32,
vaultAddr,
{ make_withdraw_request: {} },
"auto",
undefined,
[Coin.fromPartial({ denom: shareDenom, amount: "1000000" })] // 1.0 share in BANK units
)7. Vault – Redeem
await client.wasmClient.execute(
bech32,
vaultAddr,
{ redeem: { shares: "500000" } }, // 0.5 shares
"auto"
)8. Vault – Cancel Withdraw Request
await client.wasmClient.execute(
bech32,
vaultAddr,
{ cancel_withdraw_request: { unlock_epoch: 123 } },
"auto"
)Troubleshooting
Ensure
runnerexists: you must createNibiruTxClientand have an account.Amounts must be non‑negative integers in BANK units (6 decimals default).
For insufficient BANK funds, consider a prior EVM→BANK conversion on the UI layer (see EVM guide).
Last updated