本文档介绍浏览器插件钱包的技术架构,包括多链账户体系、Keyring 密钥管理、DApp Provider 注入、交易签名全链路等核心实现。
浏览器插件钱包运行在三个独立环境中,各司其职:
| 环境 | 职责 | 关键能力 |
|---|---|---|
| Background | 私钥存储、签名执行、网络请求、钓鱼拦截 | 插件 Storage、后台常驻运行时 |
| UI | 页面渲染、用户交互 | 浏览器 Webview |
| Inpage (DApp) | 向 DApp 页面注入 Provider 对象 | 插件代码注入能力 |
从区块链节点到用户界面的完整链路:
各层职责:
| 层级 | 模块 | 说明 |
|---|---|---|
| 区块链节点 | 全节点 | 自建节点确保数据未被篡改 |
| 签名 SDK | 各链私钥生成、地址生成、交易签名 | |
| 数据解析 | 轻量解析 | 快速解析关键链上数据(交易、事件) |
| 合约解析 | DeFi 智能合约项目解析 | |
| 数仓 | UTXO | BTC NFT 的 UTXO 信息和 pending 交易 |
| Gas | 从解析层和 mempool 获取 Gas 等级 | |
| 广播 | 记录交易广播结果 |
每条链的账户创建、公钥生成、地址生成、签名操作均由独立的 SDK 完成:
注册模式统一为 (currency, WalletInstance) 映射:
// 钱包 SDK 配置
const wallets = [
{ currency: Currency.BTC, wallet: BtcWallet },
{ currency: Currency.ETH, wallet: EthWallet },
{ currency: Currency.SOL, wallet: SolanaWallet },
// ...
];
// 统一注册
const registerWallet = (wallets, register) => {
wallets.forEach(({ currency, wallet: Wallet, hook }) => {
hook && hook();
register(currency, new Wallet());
});
};Keyring 是密钥管理的核心模块,负责 HD 派生路径配置和多链密钥的统一管理:
派生路径配置示例(BTC):
| 地址类型 | 派生路径 | 说明 |
|---|---|---|
| SegWit Nested (P2SH) | m/49'/0'/0'/0/{index} | 兼容性好 |
| SegWit Native (Bech32) | m/84'/0'/0'/0/{index} | 推荐 |
| Taproot | m/86'/0'/0'/0/{index} | 最新标准 |
测试网使用 coin_type = 1(如 m/84'/1'/0'/0/{index})。
Keyring 查找逻辑:
// 根据链名称获取 HDKeyring 类
const getHDKeyringByChain = (baseChain, coinType) => {
if (typeof coinType === 'undefined') {
throw new Error(`Cannot find HDKeyring for chain: ${baseChain}`);
}
return hdKeyringMap[baseChain] || hdKeyringMap.default;
};每条链需要配置三个核心标识:
| 配置项 | 说明 | 示例 |
|---|---|---|
| coinId | 链的唯一数字标识 | BTC: 1, SOL: 1800, TRX: 14 |
| localType | 链的字符串标识 | 'bitcoin', 'solana', 'tron' |
| RPC 信息 | 节点连接配置 | chainId, rpcUrl, symbol, explorerUrl |
网络配置包含完整的链元数据:
{
baseChain: 'bitcoin',
chainId: 0,
chainName: 'Bitcoin',
coinId: 1,
coinType: 0,
decimalNum: 8,
symbol: 'BTC',
localType: 'bitcoin',
providerType: 'bitcoin',
// ...
}发送交易由三个核心页面组成,每条链独立实现:
路由配置:通过 configs 匹配链类型,动态加载对应组件:
// 根据 baseCoinId 和 coinId 匹配对应链的发送页面
const config = configs.find(({ match }) => {
return match(baseCoinId, coinId, protocolId);
});
const { AddressPage, AmountPage, ConfirmPage } = config.pages;相对简单的模块,由 symbol(币种)、chainName(链名)、address(地址)组成收款码页面。
标准的订单 CRUD 操作,从后端服务读取和更新交易记录。
浏览器插件通过 Inpage 脚本向每个 DApp 页面注入 Provider 对象,使 DApp 能与钱包通信:
每条链的 Provider 独立创建:
// 创建 Bitcoin Provider
const createBitcoinInpageProvider = () => {
const BaseBitcoinInpageProvider = createBaseBitcoinInpageProvider({
providerType: PROVIDER_BITCOIN,
localType: BITCOIN
});
return class BitcoinInpageProvider extends BaseBitcoinInpageProvider {
constructor(connectionStream, { jsonRpcStreamName, logger, provider }) {
super(connectionStream, { jsonRpcStreamName, logger, provider });
provider.bitcoin = this; // 挂载到 window 对象
}
};
};Provider 的 RPC 请求通过 Middleware 路由到对应的处理函数:
BTC Middleware 支持的 RPC 方法:
| 方法 | 类别 | 说明 |
|---|---|---|
getPublicKey | 查询 | 获取地址公钥 |
getCompressedPublicKey | 查询 | 获取压缩公钥 |
validateAddress | 查询 | 验证 BTC 地址 |
getBalance | 查询 | 获取余额 |
getInscriptions | 查询 | 获取 Ordinals 铭文 |
signMessage | 签名 | 消息签名 |
signPsbt / signPsbts | 签名 | PSBT 签名(单/批量) |
send / sendBtc | 交易 | 发送 BTC |
sendPsbt | 交易 | 发送已签名 PSBT |
pushTx / pushPsbt | 广播 | 广播原始交易 |
mint / inscribe | 铭文 | BRC-20/Runes Mint |
watchAsset | 资产 | 添加自定义 Token |
DApp 交易签名后的处理流程:
签名后数据处理关键步骤:
txHashBTC 铭文操作(BRC-20、Runes)有独立的签名流程:
Runes Mint 签名数据结构:
const signData = {
type: 'RUNEMAIN',
inputs: utxo.map(item => ({
...item,
privateKey,
publicKey,
address,
})),
outputs: [{
address,
amount: outValue,
data: inscriptions[0],
}],
runeData: {
etching: null,
useDefaultOutput: false,
defaultOutput: 0,
burn: false,
mint: true,
mintNum: inscriptions.length,
},
feePerB: satBytes,
};DApp 交易确认采用类型转发模式,根据链和方法动态路由到对应 UI 组件:
// 根据请求类型路由到对应确认页面
switch (method) {
case BTCRequestEnum.SIGN_MESSAGE:
return <SignMessage unapproved={unapproved} />;
case BTCRequestEnum.MINT:
return <Mint unapproved={unapproved} />;
case BTCRequestEnum.WATCHASSET:
return <WatchAsset unapproved={unapproved} />;
// ...
}每个 DApp API 接口的支持程度不同,各页面自行控制错误提示和功能限制:
| 条件 | 处理 |
|---|---|
| MPC 钱包 + Runes Etch | 不支持,显示错误提示 |
| 硬件钱包 | 部分操作暂不支持 |
| UTXO 不足 | 显示手续费错误 |
插件使用 Redux 管理全局状态:
| 模块 | 说明 |
|---|---|
ducks/ | Redux reducer 模块化拆分 |
selectors/ | 派生数据计算(类似 Vuex getters) |
contexts/ | React Context 补充共享状态 |
hooks/ | 封装业务逻辑的自定义 hooks |
Background 和 UI 之间通过适配层通信,业务逻辑对底层通信方式无感知:
接入一条新链需要完成以下模块的配置:
| 生态 | 代表链 | 签名算法 |
|---|---|---|
| EVM | Ethereum, Polygon, BSC, Arbitrum | ECDSA secp256k1 |
| BTC | Bitcoin (主网/测试网/Signet) | ECDSA secp256k1 |
| SVM | Solana | Ed25519 |
| Tron | TRON | ECDSA secp256k1 |
| Move | Aptos, Sui | Ed25519 / secp256k1 |
| Cosmos | Cosmos Hub, Osmosis | secp256k1 |
| Others | Cardano, Stacks, StarkNet | 各自签名算法 |
浏览器插件钱包的核心架构围绕 Background(后台服务)+ UI(用户界面)+ Inpage(DApp 注入) 三大运行环境构建:
- 账户体系:通过统一的钱包 SDK 注册和 Keyring 密钥管理,支持数十条链的密钥派生和签名
- DApp 交互:通过 Provider 注入 + Middleware 路由 + Handler 处理的三层架构,实现多链 RPC 方法的统一管理
- 状态管理:Redux + backgroundConnect 的通信适配层,使 BG-UI 跨进程通信对业务透明
- 可扩展性:新链接入只需按标准化流程配置 SDK → Keyring → Provider → Middleware → UI 页面