本文档介绍浏览器插件钱包的技术架构,包括多链账户体系、Keyring 密钥管理、DApp Provider 注入、交易签名全链路等核心实现。
浏览器插件钱包运行在三个独立环境中,各司其职:
graph TB
subgraph "浏览器插件架构"
BG["Background 环境<br/>后台常驻服务"]
UI["UI 环境<br/>用户界面渲染"]
INPAGE["Inpage 环境<br/>DApp 页面注入"]
end
BG -->|"状态更新 sendUpdate"| UI
UI -->|"backgroundConnect API 调用"| BG
INPAGE -->|"JSON-RPC Stream"| BG
BG -->|"响应结果"| INPAGE
| 环境 | 职责 | 关键能力 |
|---|---|---|
| Background | 私钥存储、签名执行、网络请求、钓鱼拦截 | 插件 Storage、后台常驻运行时 |
| UI | 页面渲染、用户交互 | 浏览器 Webview |
| Inpage (DApp) | 向 DApp 页面注入 Provider 对象 | 插件代码注入能力 |
从区块链节点到用户界面的完整链路:
graph LR
subgraph "区块链层"
NODE["全节点<br/>自建/第三方"]
SDK["签名 SDK<br/>多链签名管理"]
end
subgraph "数据层"
ANALYSIS["链上解析<br/>轻量解析/合约解析"]
DW["数仓服务<br/>UTXO/Gas/广播/NFT"]
end
subgraph "服务层"
BACKEND["后端服务<br/>交易记录/资产查询"]
end
subgraph "客户端层"
PLUGIN["浏览器插件<br/>UI + Background"]
end
NODE --> ANALYSIS
NODE --> SDK
ANALYSIS --> DW
DW --> BACKEND
SDK --> PLUGIN
BACKEND --> PLUGIN
各层职责:
| 层级 | 模块 | 说明 |
|---|---|---|
| 区块链节点 | 全节点 | 自建节点确保数据未被篡改 |
| 签名 SDK | 各链私钥生成、地址生成、交易签名 | |
| 数据解析 | 轻量解析 | 快速解析关键链上数据(交易、事件) |
| 合约解析 | DeFi 智能合约项目解析 | |
| 数仓 | UTXO | BTC NFT 的 UTXO 信息和 pending 交易 |
| Gas | 从解析层和 mempool 获取 Gas 等级 | |
| 广播 | 记录交易广播结果 |
每条链的账户创建、公钥生成、地址生成、签名操作均由独立的 SDK 完成:
graph TB
REG["registerWallet()"] --> BTC["BtcWallet"]
REG --> ETH["EthWallet"]
REG --> SOL["SolanaWallet"]
REG --> TRX["TronWallet"]
REG --> APT["AptosWallet"]
REG --> MORE["...更多链"]
注册模式统一为 (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 派生路径配置和多链密钥的统一管理:
graph TB
SEED["助记词 / 私钥"] --> KR["KeyringController"]
KR --> HD["HDKeyring<br/>助记词派生"]
KR --> SIMPLE["SimpleKeyring<br/>私钥导入"]
HD --> ETH_KR["EthereumHDKeyring"]
HD --> BTC_KR["BtcHDKeyring"]
HD --> SOL_KR["SolanaHDKeyring"]
HD --> DEFAULT["DefaultHDKeyring"]
SIMPLE --> ETH_SK["EthereumSimpleKeyring"]
SIMPLE --> BTC_SK["BtcSimpleKeyring"]
SIMPLE --> DEFAULT_SK["DefaultSimpleKeyring"]
派生路径配置示例(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',
// ...
}发送交易由三个核心页面组成,每条链独立实现:
graph LR
A["选择收款地址<br/>AddressPage"] --> B["输入金额<br/>AmountPage"] --> C["交易确认<br/>ConfirmPage"]
C --> D["Gas 估算"]
C --> E["SDK 签名"]
E --> F["广播上链"]
路由配置:通过 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 能与钱包通信:
sequenceDiagram
participant DApp as DApp 网页
participant Inpage as Inpage Provider
participant Stream as JSON-RPC Stream
participant BG as Background
Note over DApp,BG: 插件加载时注入 Provider
Inpage->>DApp: 注入 window.bitcoin / window.ethereum 等
DApp->>Inpage: 调用 Provider 方法
Inpage->>Stream: 序列化为 JSON-RPC 请求
Stream->>BG: 转发到 Background
BG->>BG: 执行业务逻辑
BG-->>Stream: 返回结果
Stream-->>Inpage: 反序列化响应
Inpage-->>DApp: 返回给 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 路由到对应的处理函数:
graph TB
REQ["DApp RPC 请求"] --> MW["Provider Middleware"]
MW --> BTC_MW["bitcoinMiddleware"]
MW --> ETH_MW["ethereumMiddleware"]
MW --> SOL_MW["solanaMiddleware"]
MW --> MORE_MW["...其他链"]
BTC_MW --> SIGN["签名类方法<br/>signMessage / signPsbt"]
BTC_MW --> QUERY["查询类方法<br/>getBalance / getPublicKey"]
BTC_MW --> TX["交易类方法<br/>send / sendPsbt"]
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 交易签名后的处理流程:
sequenceDiagram
participant DApp as DApp
participant UI as 插件 UI
participant BG as Background
participant Handler as DApp Handler
participant API as 后端 API
DApp->>BG: 发起交易请求
BG->>UI: 弹出确认窗口
UI->>UI: 用户确认签名
UI->>BG: 确认结果
BG->>Handler: 执行签名逻辑
Note over Handler: 签名后处理
Handler->>Handler: 计算 txHash
Handler->>API: 广播交易(postTx)
API-->>Handler: 广播结果
Handler->>Handler: 更新请求状态为 Submitted
Handler-->>DApp: 返回 txHash
签名后数据处理关键步骤:
txHashBTC 铭文操作(BRC-20、Runes)有独立的签名流程:
graph TB
INPUT["DApp 输入参数"] --> PARSE["解析铭文数据<br/>type / tick / amount"]
PARSE --> UTXO["获取 UTXO"]
UTXO --> BUILD["构建签名数据<br/>inputs + outputs + runeData"]
BUILD --> FEE["估算手续费<br/>estimateFee()"]
FEE --> SIGN["SDK 签名<br/>signTransaction()"]
SIGN --> RESULT["输出统一结构"]
RESULT --> R1["commitTx"]
RESULT --> R2["revealTxs"]
RESULT --> R3["commitTxFee / revealTxFees"]
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,
};graph TB
PAGES["pages/"] --> HOME["home/<br/>首页 · 解锁 · 引导"]
PAGES --> CRYPTO["crypto/<br/>转账 · 交易"]
PAGES --> DAPP["dapp-connect/<br/>DApp 交互"]
PAGES --> WALLET["wallet/<br/>钱包管理"]
PAGES --> SETTING["setting/<br/>设置中心"]
PAGES --> TOOLS["convenient-tools/<br/>便捷工具"]
CRYPTO --> ADDR["address/ 地址簿"]
CRYPTO --> MANAGE["manage/ 币管理"]
CRYPTO --> TRADE["trade/ 发送·接收·历史"]
CRYPTO --> DEX["dex/ Swap 入口"]
DAPP --> ENTRY["DappEntry/ 入口处理"]
DAPP --> CONFIRM["confirm-transaction/ 交易确认"]
DAPP --> PERM["permissions-connect/ 授权弹窗"]
DAPP --> CHAINS["各链处理<br/>evm/solana/cosmos/sui/..."]
WALLET --> IMPORT["import/ 钱包导入"]
WALLET --> MPC["mpc/ MPC 钱包"]
WALLET --> MGMT["manage/ 钱包管理"]
SETTING --> SAFE["SafetyLevel/ 安全等级"]
SETTING --> BACKUP["WalletBackUp/ 备份"]
SETTING --> PWD["password/ 密码管理"]
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 管理全局状态:
graph LR
UI["UI 组件"] -->|"dispatch action"| STORE["Redux Store"]
STORE -->|"useSelector"| UI
STORE --> DUCKS["ducks/<br/>Reducers 模块"]
STORE --> SELECTORS["selectors/<br/>派生数据"]
BG["Background"] -->|"sendUpdate"| STORE
| 模块 | 说明 |
|---|---|
ducks/ | Redux reducer 模块化拆分 |
selectors/ | 派生数据计算(类似 Vuex getters) |
contexts/ | React Context 补充共享状态 |
hooks/ | 封装业务逻辑的自定义 hooks |
Background 和 UI 之间通过适配层通信,业务逻辑对底层通信方式无感知:
sequenceDiagram
participant UI as UI 模块
participant BC as backgroundConnect
participant BG as Background 模块
UI->>BC: 调用钱包方法
BC->>BG: 转发请求(Chrome Runtime API)
BG->>BG: 执行业务逻辑
BG->>BC: sendUpdate 通知状态变化
BC->>UI: 更新 Redux Store
接入一条新链需要完成以下模块的配置:
graph TB
NEW["接入新链"] --> S1["1. 注册钱包 SDK"]
NEW --> S2["2. 配置 Keyring 派生路径"]
NEW --> S3["3. 配置网络标识<br/>coinId / localType"]
NEW --> S4["4. 配置兜底 RPC"]
NEW --> S5["5. 创建 Provider<br/>(DApp 注入)"]
NEW --> S6["6. 配置 Middleware<br/>(RPC 路由)"]
NEW --> S7["7. 实现转账页面<br/>地址/金额/确认"]
NEW --> S8["8. 实现 DApp Handler<br/>(签名广播)"]
| 生态 | 代表链 | 签名算法 |
|---|---|---|
| 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 页面