StableOps
SDK

钱包 SDK

让用户用自托管钱包(EVM / TRON / Solana)完成链上付款。

@stableops/wallet-sdk仅浏览器使用的前端库:把后端创建订单时返回的 paymentInstructions 交给用户的钱包,发起一笔链上转账到该订单的指定收款地址。

幂等、地址分配、链上扫描、确认数、Webhook 全部仍由 StableOps 后端负责;钱包 SDK 只做一件事:让用户把钱转到正确的地址

不要在浏览器暴露 STABLEOPS_API_KEY。订单创建必须在后端完成,前端只接收订单 id、amountpaymentInstructions

安装

pnpm add @stableops/wallet-sdk

需要支持 fetch 和 EIP-1193 / TronLink / Solana 钱包适配器的浏览器环境。 WalletConnect 为可选能力,依赖在用到时再装(见下文)。

想看可运行的完整示例?

Playground 在浏览器里串起「建单 → 钱包支付 → 确认 → finalized」全流程,并附带可阅读的源码。

快速开始(注入式钱包)

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-provider
import { 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

sendWalletPaymentsendOrderWalletPayment 都返回:

{
  txHash: string
  chain: ChainId
  asset: 'USDC' | 'USDT'
  fromAddress: string
  toAddress: string
  tokenContract: string
  amount: string // 人类可读金额,如 '49.00'
  amountUnits: string // 最小单位整数字符串
  confirmation: Promise<void>
}

confirmationbest-effort 参考信号不阻塞函数返回(交易广播后在后台查询):

  • resolve:交易已上链且合约执行成功(或超时 ~90s 后 best-effort 放行)。
  • rejectcode: 'wallet_tx_reverted'):链上 revert,没有代币转出(如余额不足)。 设计目的是尽早捕获「立即 revert」,避免用户久等才发现失败。
sent.confirmation.catch((err) => {
  // err.code === 'wallet_tx_reverted'
})
服务端链上扫描才是权威状态。 如果 scanner 已把订单推进到 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

这篇文档怎么样?

最后更新

本页内容