@binance/wallet-provider
v0.0.1
Published
EIP-1193 provider for third-party mini programs to access Binance wallet signing via the host global bn.call over the Native bridge
Readme
@binance/wallet-provider
English | 简体中文
第三方小程序调用 Binance 钱包签名能力的 EIP-1193 Provider。
运行环境
只能跑在 Binance App 的小程序容器里。 本包直接调用宿主注入的全局 bn.call,
在浏览器 / Node 里 import 不会报错,但调用时会抛 ReferenceError: bn is not defined。
这个约束没法通过 dependencies 表达,所以写在这里。
用法
下面示例里的地址都是占位的假地址,换成你自己的。
取地址
import { BinanceMPWalletProvider } from '@binance/wallet-provider'
const provider = new BinanceMPWalletProvider()
const accounts = (await provider.request('eth_accounts')) as string[]
const from = accounts[0] // '0x1111111111111111111111111111111111111111'personal_sign
参数是 [message, address],message 在前。message 传普通 UTF-8 字符串即可,不必 hex 编码。
const signature = (await provider.request('personal_sign', [
'Sign in to Example App\nNonce: 8f4a1c',
from,
])) as stringeth_signTypedData_v4
参数是 [address, typedDataJSONString],address 在前,且 typedData 要传 JSON 字符串。
const typedData = {
domain: {
name: 'Example App',
version: '1',
chainId: 56,
verifyingContract: '0x3333333333333333333333333333333333333333',
},
primaryType: 'Order',
types: {
// ⚠️ EIP712Domain 必须显式声明,不是可省的样板
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Order: [
{ name: 'maker', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
},
message: {
maker: from,
amount: '1000000000000000000',
},
}
const signature = (await provider.request('eth_signTypedData_v4', [
from,
JSON.stringify(typedData), // 注意是字符串,不是对象
])) as stringeth_sendTransaction — 转原生币
to 是收款人,金额走 value,data 留空。
import { parseEther } from '@ethersproject/units'
const txHash = (await provider.request('eth_sendTransaction', [
{
from,
to: '0x2222222222222222222222222222222222222222',
value: parseEther('0.001').toHexString(), // '0x038d7ea4c68000'
data: '0x',
},
])) as stringeth_sendTransaction — 调合约(ERC-20 转账)
和上面完全不同:to 是 token 合约地址、value 恒为 '0x0',
收款人和金额都编码进 data。这两处填反是最常见的错误。
import { Interface } from '@ethersproject/abi'
import { parseUnits } from '@ethersproject/units'
const erc20 = new Interface(['function transfer(address to, uint256 amount)'])
const USDT = '0x55d398326f99059fF775485246999027B3197955' // BSC-USDT,18 decimals
const recipient = '0x2222222222222222222222222222222222222222'
const txHash = (await provider.request('eth_sendTransaction', [
{
from,
to: USDT, // 不是收款人
value: '0x0', // 不发原生币
data: erc20.encodeFunctionData('transfer', [recipient, parseUnits('12.5', 18)]),
},
])) as string⚠️
decimals按 token 实际值填。BSC 上的 USDT 是 18 位,不是以太坊那个 6 位,填错就差几个数量级。
错误处理
失败时 reject 一个带 number 型 code 的 Error,和对接 MetaMask 的写法一致:
try {
const txHash = await provider.request('eth_sendTransaction', [tx])
} catch (e) {
if (e.code === 4001) {
// 用户主动取消,静默处理
} else {
// 其他失败,e.message 为可展示文案
}
}支持的方法
| 方法 | 参数 | 返回 | 是否唤起钱包 |
| --- | --- | --- | --- |
| eth_chainId | — | string(当前写死 '0x38',BSC) | 否 |
| eth_accounts | — | string[] | 否 |
| eth_requestAccounts | — | string[] | 否 |
| personal_sign | [message, address] | string 签名 | 是 |
| eth_signTypedData_v4 | [address, typedDataJSONString] | string 签名 | 是 |
| eth_sendTransaction | [tx],数值字段为 0x hex | string txHash | 是 |
参数形状按 EIP-1193 / MetaMask 事实标准,注意 personal_sign 是 message 在前、
eth_signTypedData_v4 是 address 在前且 typedData 传 JSON 字符串。
eth_signTypedData_v4 的 typedData 里 types.EIP712Domain 和 domain.verifyingContract
都是必需的,不是可省的样板——钱包后端要靠它才知道 domain separator 编码哪几个字段。
只读 RPC(eth_getBalance / eth_call / eth_estimateGas / eth_gasPrice /
eth_getTransactionByHash / eth_getTransactionReceipt)钱包不代理,调用方自己打节点。
传进来会直接抛 Method xxx not supported。
错误码
取 EIP-1193 标准值,钱包内部更细的失败原因不透传:
| code | 含义 | 谁的问题 | 建议处理 |
| --- | --- | --- | --- |
| 4001 | 用户拒绝(点取消、下滑关闭、pending 交易超限) | 用户主动行为 | 静默处理,不报错不重试 |
| 4200 | 方法不在 SDK 支持范围内(如只读 RPC) | 集成方代码 | 改代码,别调这个方法 |
| 4201 | 方法支持,但当前宿主 App 版本没有对应能力 | 用户 App 版本旧 | 提示升级 App,重试无意义 |
| -32603 | 其他失败(钱包内部异常、bridge 不通、Native 超时) | — | 展示 message,可重试 |
4200 和 4201 要分开处理,虽然都是「不支持」,但成因完全不同:
4200是编程错误,联调期就该发现。给终端用户弹「请升级 App」是误导 —— 升到最新也一样不支持。4201是运行时状态,线上随时可能遇到。发请求前会用bn.canIUseCustom探一次宿主有没有对应 action,没有就直接拦下 —— 确定性判断,不是靠嗅错误文案。结果按实例缓存,一次会话只探一次。
try {
await provider.request('personal_sign', [message, address])
} catch (e) {
if (e.code === 4001) return // 用户取消,静默
if (e.code === 4201) return showUpgradePrompt() // 引导升级 App
showError(e.message) // 4200 会走到这里,但它应该在联调期就被修掉
}调试
new BinanceMPWalletProvider({ debug: true })每次调用前把 payload 打三份到 console:原样对象(devtool 可展开)、params 解码后
(它是 JSON 字符串,对象树里展不开)、序列化版(可整段复制)。默认关闭。
当前不支持
- 事件推送(
on/accountsChanged/chainChanged) - 非 EVM 链
- 连接态管理 —— 每次调用独立、无状态
- 切链(
wallet_switchEthereumChain)—— 链 ID 当前写死 BSC
