Sai 合约的 WASM 与 EVM 集成
本参考介绍从 Cosmos、WASM 和 EVM 钱包集成 Sai 永续合约及 SLP 金库,涵盖所示代码的全部调用、参数、前提条件和示例。
环境: 支持主网和 Testnet-2。代币路径和地址随网络变化,见“抵押资产与支付代币”。
- BANK: Cosmos 侧代币,如
tf/...denom。 - 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 参数。
| 路径 | 符号 | 显示名称 | 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 Gas 策略
- 用
contract.method.estimateGas(...)估算并乘 1.1。 - 失败时回退为
gasLimit = 5_000_000、gasPrice = 1 gwei。
- 用
-
ExecuteResult 归一化
- EVM 收据转换为类似 CosmJS 的结果,包含
height、transactionHash、gasUsed/Wanted及按日志保留 topics、data 的events。
- EVM 收据转换为类似 CosmJS 的结果,包含
-
浏览器链接
- 界面示例根据
height和hash生成blockUrl、txUrl。
- 界面示例根据
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 处理跨 VM 转换和合并。
前提条件:
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 集成”WASM 使用 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将不足部分由 ERC-20 转为 BANK。
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. 金库提现申请”查询份额 denom: 执行前查询一次。
{ "get_vault_share_denom": {} }客户端:
- 使用份额 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 时,先统一小数位再求和。
- 验证缩放后金额有限、非负且非零。
错误处理与交互
Section titled “错误处理与交互”- 签名者: EVM 需要
signer,仅 runner 不够;WASM 需要runner。 - 通知: 显示待处理、成功、失败,成功时附
height、hash和浏览器链接。 - EVM 调试: 回退时,用归档 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 已获取份额 denom;签名者就绪。
- 赎回: 份额用 BANK 单位;EVM 设置
sendToEvm;签名者就绪。 - 取消: 设置
unlock_epoch,签名者就绪。
附录:工具说明
Section titled “附录:工具说明”estimateGasWithFallback将估算翻倍,最低2_500_000,失败时用5_000_000 / 1 gwei。Wasm 预编译的估算不是可靠下限,零费率下 Gas 免费,因此有意保留较大余量。receiptToExecuteResult归一化为类似 CosmJS 的格式。MsgConvertEvmToCoin可在 WASM 的 BANK 不足时追加。- 智能分配: 按余额和偏好拆分 BANK、ERC-20,分别缩放再合并。