钱包 SDK
让用户用自托管钱包(EVM / TRON / Solana)完成链上付款。
@stableops/wallet-sdk 是仅浏览器使用的前端库:把后端创建订单时返回的
paymentInstructions 交给用户的钱包,发起一笔链上转账到该订单的指定收款地址。
幂等、地址分配、链上扫描、确认数、Webhook 全部仍由 StableOps 后端负责;钱包 SDK 只做一件事:让用户把钱转到正确的地址。
STABLEOPS_API_KEY。订单创建必须在后端完成,前端只接收订单 id、amount 和 paymentInstructions。安装
pnpm add @stableops/wallet-sdk需要支持 fetch 和 EIP-1193 / TronLink / Solana 钱包适配器的浏览器环境。
WalletConnect 为可选能力,依赖在用到时再装(见下文)。
想看可运行的完整示例?
快速开始(注入式钱包)
getInjectedWalletProviders() 收集页面里已注入的钱包(MetaMask、TronLink、Phantom 等),
sendOrderWalletPayment() 自动从订单候选里挑一条「有可用钱包」的链并发起转账。
import { getInjectedWalletProviders, sendOrderWalletPayment } from '@stableops/wallet-sdk'
const sent = await sendOrderWalletPayment({
order, // { amount, paymentInstructions },来自后端
providers: getInjectedWalletProviders(),
})
console.log(sent.txHash)可用 preferredChains 指定优先尝试的链顺序:
await sendOrderWalletPayment({
order,
providers: getInjectedWalletProviders(),
preferredChains: ['base', 'arbitrum'],
})手动选择候选指令
需要自己控制「选哪条链」时,先用 selectWalletPaymentInstruction() 选中,
再调用底层的 sendWalletPayment():
import {
getInjectedWalletProviders,
selectWalletPaymentInstruction,
sendWalletPayment,
} from '@stableops/wallet-sdk'
const { instruction, provider } = selectWalletPaymentInstruction(
order.paymentInstructions,
getInjectedWalletProviders(),
['base'], // 可选:优先链
)
const sent = await sendWalletPayment({
provider,
instruction,
amount: order.amount,
})
console.log(sent.txHash)WalletConnect(移动端 / 自定义 UI)
移动端浏览器或没有注入 EVM provider 的页面,可以接入可选的 WalletConnect 运行时。 SDK 不内置 UI、也不维护钱包列表:钱包选项由你传入,界面通过订阅 controller 的 state 自行渲染(钱包列表、二维码、连接中 / 失败状态)。
pnpm add @walletconnect/universal-providerimport { createWalletConnectController, sendOrderWalletPayment } from '@stableops/wallet-sdk'
const wc = await createWalletConnectController({
projectId: 'YOUR_REOWN_PROJECT_ID',
metadata: {
name: 'Your App',
description: 'StableOps checkout',
url: window.location.origin,
icons: [`${window.location.origin}/icon.png`],
},
// 启用的命名空间(按需取舍)
chains: ['base', 'arbitrum'], // EVM
solanaChains: ['solana'], // Solana
tronChains: ['tron'], // TRON
wallets: [
{
id: 'metamask',
name: 'MetaMask',
links: { native: 'metamask://', universal: 'https://metamask.app.link' },
},
],
})
const unsubscribe = wc.subscribe((state) => {
// 按 state.status 渲染:'idle' | 'connecting' | 'uri_ready' | 'connected' | 'failed' | 'disconnected'
// state.status === 'uri_ready' 时,用 state.uri 生成二维码。
})
await wc.connect({ walletId: 'metamask' })
const sent = await sendOrderWalletPayment({
order,
providers: wc.providers, // 把 controller 的 providers 直接交给发送函数
})
unsubscribe()
console.log(sent.txHash)EVM、Solana、TRON 三类命名空间都可经 WalletConnect 走通:
- EVM:行为与注入式一致。
- Solana:取决于钱包是否支持
solana_signTransaction/solana_signAndSendTransaction; 自定义 RPC / devnet 流程需要solana_signTransaction。 - TRON:钱包只做
tron_signTransaction签名,交易构造与广播由 SDK 用 tronweb 完成 (默认 trongrid 公共节点,可用tronRpcUrl覆盖)。
返回值:SentWalletPayment
sendWalletPayment 与 sendOrderWalletPayment 都返回:
{
txHash: string
chain: ChainId
asset: 'USDC' | 'USDT'
fromAddress: string
toAddress: string
tokenContract: string
amount: string // 人类可读金额,如 '49.00'
amountUnits: string // 最小单位整数字符串
confirmation: Promise<void>
}confirmation 是 best-effort 参考信号,不阻塞函数返回(交易广播后在后台查询):
- resolve:交易已上链且合约执行成功(或超时 ~90s 后 best-effort 放行)。
- reject(
code: 'wallet_tx_reverted'):链上 revert,没有代币转出(如余额不足)。 设计目的是尽早捕获「立即 revert」,避免用户久等才发现失败。
sent.confirmation.catch((err) => {
// err.code === 'wallet_tx_reverted'
})detected / confirmed,一律以服务端为准,忽略 confirmation 的 reject。支持的链
| 链 | 走法 |
|---|---|
ethereum base arbitrum polygon optimism bsc(及各自测试网 *-sepolia / polygon-amoy / bsc-testnet) | 调用 EIP-1193 钱包,必要时切链 / 添加网络,发送 ERC-20 transfer。可用 chainConfigs 覆盖 RPC / 浏览器地址。 |
tron tron-nile | 调用 TronLink / TronWeb(或 WalletConnect TRON),构造、签名并广播 TRC-20 transfer。测试网或自建节点传 tronRpcUrl。 |
solana solana-devnet | 调用 Solana 钱包适配器,幂等创建收款方关联 token account,发送 SPL Token TransferChecked。devnet 传 solanaRpcUrl: 'https://api.devnet.solana.com'(或等价节点)。 |
错误处理
所有失败都会抛 StableOpsWalletError,带 .code、.message、可选 .details。
import { StableOpsWalletError } from '@stableops/wallet-sdk'
try {
await sendOrderWalletPayment({ order, providers: getInjectedWalletProviders() })
} catch (err) {
if (err instanceof StableOpsWalletError && err.code === 'wallet_user_rejected') {
// 用户在钱包里取消了
}
throw err
}常见 code:
| code | 含义 |
|---|---|
payment_instruction_not_found | 订单没有可支付的链上指令 |
wallet_provider_not_found | 所有候选链都没有可用钱包 provider |
wallet_user_rejected | 用户在钱包里拒绝签名 |
wallet_tx_reverted | 链上 revert(多由 confirmation reject 抛出) |
unsupported_chain | 该链不被钱包助手支持 |
chain_config_not_found | 缺少对应 EVM 链配置(传 chainConfigs 补充) |
invalid_amount / invalid_evm_address / invalid_tron_address / invalid_solana_address | 入参非法 |
tron_dependency_missing / solana_dependency_missing | 缺少 TRON / Solana 运行依赖 |
调试
打开模块级调试日志(前缀 [wallet-sdk])便于排查:
import { setWalletSdkDebug } from '@stableops/wallet-sdk'
setWalletSdkDebug(true)也可不改代码临时打开:浏览器控制台设 globalThis.STABLEOPS_WALLET_DEBUG = true,
或构建时注入环境变量 WALLET_SDK_DEBUG=1。
这篇文档怎么样?
最后更新