SaiコントラクトのWASM・EVM連携
Cosmos、WASMとEVMのウォレットから、Saiの無期限先物とSLPボールトに連携するためのリファレンスです。提示されたコードの全呼び出しを、パラメーター、前提条件、例とともに説明します。
環境: メインネットとTestnet-2に対応します。ネットワークごとの資産経路とアドレスは「担保・支払いトークン」を参照してください。
- BANK: Cosmos側のコイン。
tf/...などのデノム。 - ERC-20: EVM側のトークン。
0x...など。 - Interface、EVM: JSONのWASMメッセージを受け取り、Saiのモジュールに転送する単一コントラクト。
- Perp: Cosmos側の無期限先物ルーター。
saiContracts.perpで参照。 - Vault: 入金・出金・償還に対応するSLPボールト。CosmosのBech32アドレス。
担保・支払いトークン
Section titled “担保・支払いトークン”コードは usd と stnibi に対応します。WASMでは添付資金、EVMではERC-20パラメーターに使います。
メインネット
Section titled “メインネット”| ルート | シンボル | 表示名 | bankDenom | evmAddr | 既定ERC-20小数桁数 |
|---|---|---|---|---|---|
usd | USDC | USDC | erc20/0x0829F361A05D993d5CEb035cA6DF3446b060970b | 0x0829F361A05D993d5CEb035cA6DF3446b060970b | 6 |
stnibi | stNIBI | リキッドステーキングNibiru、ラップ版 | tf/nibi1udqqx30cw8nwjxtl4l28ym9hhrp933zlq8dqxfjzcdhvl8y24zcqpzmh8m/ampNIBI | 0xcA0a9Fb5FBF692fa12fD13c0A900EC56Bb3f0a7b | 6 |
Testnet-2
Section titled “Testnet-2”| ルート | シンボル | 表示名 | bankDenom | evmAddr | 既定ERC-20小数桁数 |
|---|---|---|---|---|---|
usd | USDC | USDC | tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/usdc | 0xAb68f1D1d91854383fd4Df9016E3040D03e8191a | 6 |
stnibi | stNIBI | リキッドステーキングNibiru、ラップ版 | tf/nibi1pc2mmwcqhvzn9vsm0umpu40yzl6gfy6nucwn7g/stnibi | 0xCae3d404AFB50016154a4B18091351065154E9bD | 6 |
WASMはBANKの最小単位、既定で小数6桁を使います。EVMはBANK部分とERC-20部分を指定し、合計します。
共通概念と型
Section titled “共通概念と型”-
署名者
- WASM:
{ fromBech32Addr, runner: NibiruTxClient }。 - EVM:
{ fromHexAddr, runner?: ContractRunner, signer?: Signer }。送信にはsignerが必要です。
- WASM:
-
TradeTypeの判定
-
成行:
Market → "trade"。 -
指値・逆指値は
limitPriceUsdとopen_priceを比較します。- ロング:
limit > open_priceならstop、それ以外はlimit。 - ショート:
limit < open_priceならstop、それ以外はlimit。
- ロング:
-
-
EVMのガス設定
contract.method.estimateGas(...)に1.1倍の余裕を加えます。- 推定失敗時は
gasLimit = 5_000_000、gasPrice = 1 gwei。
-
ExecuteResultの正規化
- EVMレシートを、
height、transactionHash、gasUsed/Wanted、ログごとのtopics・dataを含むeventsを持つCosmJS形式に変換します。
- EVMレシートを、
-
エクスプローラーのリンク
- UI例は
heightとhashからblockUrlとtxUrlを作ります。
- UI例は
EVM連携、インターフェース経由
Section titled “EVM連携、インターフェース経由”EVMは saiEvmInterfaceFromChainType(chainType) で得る単一のInterfaceを呼びます。JSONの wasmMsgBytes と資産額・アドレスを送ります。EVMの signer があることを確認してください。
1. 無期限先物の新規注文
Section titled “1. 無期限先物の新規注文”メソッド: openTrade(wasmMsgBytes, collateralIndex, totalAmountBankUnits, useErc20Amount)
wasmMsg、JSON:
{ "open_trade": { "market_index": "MarketIndex(N)", "leverage": "string", "long": true, "collateral_index": "TokenIndex(N)", "trade_type": "trade|limit|stop", "open_price": "<market price or limit price>", "tp": "optional string", "sl": "optional string", "slippage_p": "0.01", "is_evm_origin": true }}slippage_p はCosmWasm Decimalの割合で、"0.01" は1%です。
数量の計算:
- BANK部分は小数6桁の単位で計算。
- ERC-20部分はトークンの小数桁で計算。既定は6で、あれば
collateral.evmDefaultDecimalsを使用。 totalAmount=bankAmount + evmAmount。オンチェーンのInterfaceが変換・結合します。
前提条件:
orderType === "Market"にはopen_priceが必要。collateralが存在し、amtTrade> 0。- 自動配分では残高と設定に応じてBANK・ERC-20に分割。
2. 無期限先物の決済
Section titled “2. 無期限先物の決済”メソッド: executeSimpleFunctions(wasmMsgBytes)
wasmMsg:
{ "close_trade": { "trade_index": "UserTradeIndex(<N>)" } }3. 紹介コードの作成
Section titled “3. 紹介コードの作成”メソッド: executeSimpleFunctions(wasmMsgBytes)
wasmMsg:
{ "create_referrer_code": { "code": "<your_code>" } }4. 紹介コードの適用
Section titled “4. 紹介コードの適用”メソッド: executeSimpleFunctions(wasmMsgBytes)
wasmMsg:
{ "redeem_referrer_code": { "code": "<partner_code>" } }5. ボールトへの入金
Section titled “5. ボールトへの入金”メソッド:
contract.deposit( wasmMsgBytes, // { "deposit": {} } depositAmountTotalBankUnits, // BANK units (includes ERC‑20 converted -> BANK) useErc20Amount, // ERC‑20 units (token decimals) vaultAddress, // bech32 vault contract collateralErc20, // ERC‑20 address true, // sendToEvm on triggers overrides)注意:
- 必要ならERC-20部分をBANK単位に直して
depositAmountTotalに加えます。小数桁が異なる場合があります。
6. ボールトの出金申請
Section titled “6. ボールトの出金申請”メソッド:
contract.makeWithdrawRequest( vaultAddress, wasmMsgBytes, // { "make_withdraw_request": {} } totalShares, // BANK units to lock (sharesBANK + sharesFromERC20) useErc20Amount, // shares to bridge from ERC‑20 (BANK-scaled already) overrides)7. ボールトの償還
Section titled “7. ボールトの償還”メソッド:
contract.redeem( wasmMsgBytes, // { "redeem": { "shares": "..." } } vaultAddress, shares, // BANK units sendToEvm, // boolean overrides)8. 出金申請の取り消し
Section titled “8. 出金申請の取り消し”メソッド: executeVaultSimpleFunctions(wasmMsgBytes, vaultAddress)
wasmMsg:
{ "cancel_withdraw_request": { "unlock_epoch": <number> } }WASM、Cosmos連携
Section titled “WASM、Cosmos連携”NibiruTxClient の signer.runner.wasmClient を使います。資金は Coin { denom, amount } で、amount はBANK単位、既定で1e6倍です。
1. 無期限先物の新規注文
Section titled “1. 無期限先物の新規注文”クライアント: wasmClient.execute(sender, saiContracts.perp, msg, fee, memo, funds)
メッセージ:
{ "open_trade": { "market_index": "MarketIndex(N)", "leverage": "string", "long": true, "collateral_index": "TokenIndex(N)", "trade_type": "trade|limit|stop", "open_price": "<market price or limit price>", "slippage_p": "0.01", "tp": "optional string", "sl": "optional string", "is_evm_origin": false }}資金:
[{ "denom": "<collateral.bankDenom>", "amount": "<BANK units>" }]前提条件:
Marketはopen_priceが必要。Limit/StopはlimitPriceUsdが必要。
任意のERC-20→BANK変換
- BANK残高不足なら、先頭に
MsgConvertEvmToCoinを追加し、不足分を変換します。
2. 無期限先物の決済
Section titled “2. 無期限先物の決済”クライアント: execute(sender, saiContracts.perp, { close_trade: { trade_index: "UserTradeIndex(N)" } }, fee)
3. 紹介コードの作成
Section titled “3. 紹介コードの作成”クライアント: execute(sender, saiContracts.perp, { create_referrer_code: { code } }, fee)
4. 紹介コードの適用
Section titled “4. 紹介コードの適用”クライアント: execute(sender, saiContracts.perp, { redeem_referrer_code: { code } }, fee)
5. ボールトへの入金
Section titled “5. ボールトへの入金”クライアント: execute(sender, vaultSelection.address, { deposit: {} }, fee, memo, [funds])
資金:
[{ "denom": "<collateral.bankDenom>", "amount": "<BANK units>" }]6. ボールトの出金申請
Section titled “6. ボールトの出金申請”持分デノムの取得: 実行前に一度取得します。
{ "get_vault_share_denom": {} }クライアント:
- 持分デノムの資金を添付します。
[{ "denom": "<shareDenom>", "amount": "<shares BANK units>" }]メッセージ: { "make_withdraw_request": {} }
7. ボールトの償還
Section titled “7. ボールトの償還”クライアント: execute(sender, vaultAddress, { redeem: { shares: "..." } }, fee)。資金添付は不要です。
8. 出金申請の取り消し
Section titled “8. 出金申請の取り消し”クライアント: execute(sender, vaultAddress, { cancel_withdraw_request: { unlock_epoch } }, fee)
数量と小数桁の注意点
Section titled “数量と小数桁の注意点”- BANKの既定は6桁。表示額を
display × 10^decimalsに変換します。 - EVM入出金でBANK・ERC-20を混ぜる場合、桁を合わせてから加算します。
- 変換後の値が有限・非負・ゼロ以外であることを検証します。
エラー処理とUI
Section titled “エラー処理とUI”- 署名者: EVMはrunnerだけでなく
signer、WASMはrunnerが必要。 - 通知: 待機・成功・失敗を表示し、成功時は
height・hashとエクスプローラーを示します。 - EVMデバッグ: リバート時はarchive RPCの
debug_traceTransactionから最深部のcalls[-1].errorを取得します。
最小構成の擬似コード
Section titled “最小構成の擬似コード”以下は
chainType、saiContracts、interfaceAddress、collateral等を選択済みとする簡略例です。
新規注文、EVM
Section titled “新規注文、EVM”const msg = { open_trade: { /* ...see schema above... */ is_evm_origin: true } }const wasmMsgBytes = ethers.toUtf8Bytes(JSON.stringify(msg))const gas = await estimateGasWithFallback( () => contract.openTrade.estimateGas(wasmMsgBytes, collateralIndex, totalAmount, useErc20), { msg })const tx = await contract.openTrade(wasmMsgBytes, collateralIndex, totalAmount, useErc20, toTxOverrides(gas))const res = await tx.wait() // -> map to ExecuteResult if desired新規注文、WASM
Section titled “新規注文、WASM”await wasm.execute( bech32, saiContracts.perp, { open_trade: { /* ... */, is_evm_origin: false } }, "auto", undefined, [ { denom: collateral.bankDenom, amount: scaledAmount } ])ボールト入金、EVM
Section titled “ボールト入金、EVM”const msg = { deposit: {} }const wasmMsgBytes = ethers.toUtf8Bytes(JSON.stringify(msg))const bankTotal = bankAmount + toBankUnits(erc20Amount, erc20Dec, 6)const tx = await contract.deposit(wasmMsgBytes, bankTotal, erc20Amount, vaultAddr, collateral.evmAddr, true, overrides)await tx.wait()出金申請、WASM
Section titled “出金申請、WASM”const shareDenom = await wasm.queryContractSmart(vaultAddr, { get_vault_share_denom: {} })await wasm.execute( bech32, vaultAddr, { make_withdraw_request: {} }, "auto", undefined, [ { denom: shareDenom, amount: shares } ])呼び出しごとの前提条件
Section titled “呼び出しごとの前提条件”- 新規: 数量>0。成行は
open_price、指値・逆指値はlimitPriceUsd。collateral.indexが解析可能で、署名者が準備済み。 - 決済:
userTradeIndexと署名者。 - 紹介: 空でない
codeと署名者。 - 入金: 桁変換済み。EVMではERC-20をBANKに直して合計。署名者。
- 出金申請: 持分はBANK単位。WASMは持分デノム取得済み。署名者。
- 償還: 持分はBANK単位。EVMは
sendToEvm指定。署名者。 - 取り消し:
unlock_epochと署名者。
付録:補助関数
Section titled “付録:補助関数”estimateGasWithFallbackは推定を2倍にし、最低2_500_000、失敗時5_000_000 / 1 gweiにします。Wasmプリコンパイルの推定は下限として信頼できず、料率ゼロではガス代も無料なので、大きめの余裕を設けます。receiptToExecuteResultはCosmJSに似た形式へ正規化します。MsgConvertEvmToCoinはWASMでBANK不足時に任意で追加できます。- 自動配分: 残高と希望に応じてBANK・ERC-20に分け、各側で桁変換して合計します。