コンテンツにスキップ
このページの翻訳は LLM によって生成されています。誤りに気づいた場合は GitHub で issue を開いてお知らせください。

Sai WASM連携ガイド

NibiruTxClient でCosmos、WASM側からSaiを操作する一連の手順です。


  • Node.js ≥ 18。
  • pnpm、npm、yarnのいずれか。
Terminal window
mkdir sai-wasm && cd sai-wasm
pnpm init -y
pnpm add @nibiruchain/nibijs @cosmjs/cosmwasm-stargate @cosmjs/stargate cosmjs-types bignumber.js
pnpm add -D typescript ts-node @types/node dotenv
npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext

設定を持つ .env を作成します。

Terminal window
# Testnet-2
NIBI_RPC=https://rpc.testnet-2.nibiru.fi/
# Or Mainnet
# NIBI_RPC=https://rpc.nibiru.fi/
MNEMONIC="word1 word2 ... word12" # Your wallet mnemonic (keep this secret!)
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 }
}

BANKは既定で小数6桁です。表示額に10^6を掛けて変換してください。

トークンBankデノム
USDCtf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc
stNIBItf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/stnibi
トークンBankデノム
USDCerc20/0x0829F361A05D993d5CEb035cA6DF3446b060970b
stNIBItf/nibi1udqqx30cw8nwjxtl4l28ym9hhrp933zlq8dqxfjzcdhvl8y24zcqpzmh8m/ampNIBI
import BigNumber from "bignumber.js"
const displayAmount = new BigNumber("100") // 100 USDC
const bankAmount = displayAmount.times(1e6).integerValue().toString()
// Result: "100000000" (100 * 10^6)

{
"open_trade": {
"market_index": "MarketIndex(N)",
"leverage": "string (e.g., '5')",
"long": true,
"collateral_index": "TokenIndex(N)",
"trade_type": "trade|limit|stop",
"open_price": "<market or limit price>",
"slippage_p": "0.01",
"tp": "optional take-profit",
"sl": "optional stop-loss",
"is_evm_origin": false
}
}

slippage_p はCosmWasm Decimalの割合で、"0.01" は1%です。

  • 成行では open_price に現在の市場価格を設定します。
  • 指値・逆指値ではトリガー価格を設定します。
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()
// Contract addresses (from chain config)
const saiPerp = "<saiContracts.perp>" // bech32 address
const bankDenom = "tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc" // Testnet-2 USDC
// Convert 100 USDC to BANK units
const display = new BigNumber("100")
const amount = display.times(1e6).integerValue().toString() // "100000000"
const msg = {
open_trade: {
market_index: "MarketIndex(0)",
leverage: "5",
long: true,
collateral_index: "TokenIndex(0)",
trade_type: "trade",
open_price: "70000",
slippage_p: "0.01",
is_evm_origin: false,
},
}
const res = await client.wasmClient.execute(
bech32,
saiPerp,
msg,
"auto",
undefined,
[Coin.fromPartial({ denom: bankDenom, amount })]
)
console.log("Transaction hash:", res.transactionHash)
console.log("Gas used:", res.gasUsed)
}

未決済ポジションを決済します。

import { makeNibiruTxClient } from "./wasmClient"
async function closeTrade() {
const { client, bech32 } = await makeNibiruTxClient()
const saiPerp = "<saiContracts.perp>"
const res = await client.wasmClient.execute(
bech32,
saiPerp,
{
close_trade: {
trade_index: "UserTradeIndex(0)",
},
},
"auto"
)
console.log("Trade closed in tx:", res.transactionHash)
}

async function createReferralCode() {
const { client, bech32 } = await makeNibiruTxClient()
const saiPerp = "<saiContracts.perp>"
const res = await client.wasmClient.execute(
bech32,
saiPerp,
{
create_referrer_code: {
code: "MYCODE",
},
},
"auto"
)
console.log("Referral code created in tx:", res.transactionHash)
}
async function redeemReferralCode() {
const { client, bech32 } = await makeNibiruTxClient()
const saiPerp = "<saiContracts.perp>"
const res = await client.wasmClient.execute(
bech32,
saiPerp,
{
redeem_referrer_code: {
code: "PARTNER",
},
},
"auto"
)
console.log("Referral code redeemed in tx:", res.transactionHash)
}

担保をボールトに入金します。

import BigNumber from "bignumber.js"
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin"
import { makeNibiruTxClient } from "./wasmClient"
async function depositToVault() {
const { client, bech32 } = await makeNibiruTxClient()
const vaultAddr = "<vault bech32 address>"
const bankDenom = "tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc" // Testnet-2 USDC
// Convert 250 USDC to BANK units
const amount = new BigNumber("250")
.times(1e6)
.integerValue()
.toString()
const res = await client.wasmClient.execute(
bech32,
vaultAddr,
{ deposit: {} },
"auto",
undefined,
[Coin.fromPartial({ denom: bankDenom, amount })]
)
console.log("Deposited to vault in tx:", res.transactionHash)
}

ボールトから出金を申請します。

import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin"
import { makeNibiruTxClient } from "./wasmClient"
async function makeWithdrawRequest() {
const { client, bech32 } = await makeNibiruTxClient()
const vaultAddr = "<vault bech32 address>"
// First, query the vault to get the share denom
const shareDenom = await client.wasmClient.queryContractSmart(vaultAddr, {
get_vault_share_denom: {},
})
console.log("Share denom:", shareDenom)
// Request to withdraw 1.0 share (1,000,000 in BANK units)
const res = await client.wasmClient.execute(
bech32,
vaultAddr,
{ make_withdraw_request: {} },
"auto",
undefined,
[Coin.fromPartial({ denom: shareDenom, amount: "1000000" })]
)
console.log("Withdraw request created in tx:", res.transactionHash)
}

持分を償還します。

import { makeNibiruTxClient } from "./wasmClient"
async function redeemVaultShares() {
const { client, bech32 } = await makeNibiruTxClient()
const vaultAddr = "<vault bech32 address>"
// Redeem 0.5 shares (500,000 in BANK units)
const res = await client.wasmClient.execute(
bech32,
vaultAddr,
{
redeem: {
shares: "500000",
},
},
"auto"
)
console.log("Shares redeemed in tx:", res.transactionHash)
}

待機中の申請を取り消します。

import { makeNibiruTxClient } from "./wasmClient"
async function cancelWithdrawRequest() {
const { client, bech32 } = await makeNibiruTxClient()
const vaultAddr = "<vault bech32 address>"
const res = await client.wasmClient.execute(
bech32,
vaultAddr,
{
cancel_withdraw_request: {
unlock_epoch: 123, // Replace with actual unlock epoch
},
},
"auto"
)
console.log("Withdraw request cancelled in tx:", res.transactionHash)
}

async function getVaultShareDenom() {
const { client } = await makeNibiruTxClient()
const vaultAddr = "<vault bech32 address>"
const shareDenom = await client.wasmClient.queryContractSmart(vaultAddr, {
get_vault_share_denom: {},
})
console.log("Share denom:", shareDenom)
return shareDenom
}
async function getUserTrades() {
const { client, bech32 } = await makeNibiruTxClient()
const saiPerp = "<saiContracts.perp>"
const trades = await client.wasmClient.queryContractSmart(saiPerp, {
get_user_trades: {
user: bech32,
},
})
console.log("User trades:", trades)
return trades
}
async function getVaultInfo() {
const { client } = await makeNibiruTxClient()
const vaultAddr = "<vault bech32 address>"
const info = await client.wasmClient.queryContractSmart(vaultAddr, {
get_info: {},
})
console.log("Vault info:", info)
return info
}

問題解決方法
”No account found”ニーモニックと NibiruTxClient 初期化を確認
”Insufficient funds”BANK単位の残高を確認
”Invalid denom”bankDenom がネットワークに合うか確認
”Contract not found”ネットワークのコントラクトアドレスを確認
小数桁の変換WASMに渡す前に10^6倍
タイムアウト待機時間を増やすかRPC接続を確認
  • 実行前に NibiruTxClient の接続を確認します。
  • 数量がBANK単位の非負整数か検証します。
  • 特別な要件がなければガス推定に "auto" を使います。
  • ニーモニックを安全に保ち、Gitにコミットしてはいけません。
  • パラメーターを推測せず、状態を取得してください。
  • メインネットの前にTestnet-2で試してください。
  • client.getTx(transactionHash) で詳細を確認。
  • 結果の詳しいエラー原因を確認。
  • console.log() で処理を追跡。
  • 応答のガス使用量・手数料を確認。