Skip to content

浏览器插件钱包技术实现

本文档介绍浏览器插件钱包的技术架构,包括多链账户体系、Keyring 密钥管理、DApp Provider 注入、交易签名全链路等核心实现。


一、架构总览

1.1 三大运行环境

浏览器插件钱包运行在三个独立环境中,各司其职:

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 对象插件代码注入能力

1.2 全链路架构

从区块链节点到用户界面的完整链路:

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 智能合约项目解析
数仓UTXOBTC NFT 的 UTXO 信息和 pending 交易
Gas从解析层和 mempool 获取 Gas 等级
广播记录交易广播结果

二、账户体系

2.1 多链钱包 SDK 注册

每条链的账户创建、公钥生成、地址生成、签名操作均由独立的 SDK 完成:

graph TB
    REG["registerWallet()"] --> BTC["BtcWallet"]
    REG --> ETH["EthWallet"]
    REG --> SOL["SolanaWallet"]
    REG --> TRX["TronWallet"]
    REG --> APT["AptosWallet"]
    REG --> MORE["...更多链"]

注册模式统一为 (currency, WalletInstance) 映射:

javascript
// 钱包 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());
  });
};

2.2 Keyring 密钥管理

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}推荐
Taprootm/86'/0'/0'/0/{index}最新标准

测试网使用 coin_type = 1(如 m/84'/1'/0'/0/{index})。

Keyring 查找逻辑

javascript
// 根据链名称获取 HDKeyring 类
const getHDKeyringByChain = (baseChain, coinType) => {
  if (typeof coinType === 'undefined') {
    throw new Error(`Cannot find HDKeyring for chain: ${baseChain}`);
  }
  return hdKeyringMap[baseChain] || hdKeyringMap.default;
};

2.3 网络配置

每条链需要配置三个核心标识:

配置项说明示例
coinId链的唯一数字标识BTC: 1, SOL: 1800, TRX: 14
localType链的字符串标识'bitcoin', 'solana', 'tron'
RPC 信息节点连接配置chainId, rpcUrl, symbol, explorerUrl

网络配置包含完整的链元数据:

javascript
{
  baseChain: 'bitcoin',
  chainId: 0,
  chainName: 'Bitcoin',
  coinId: 1,
  coinType: 0,
  decimalNum: 8,
  symbol: 'BTC',
  localType: 'bitcoin',
  providerType: 'bitcoin',
  // ...
}

三、转账模块

3.1 发送交易

发送交易由三个核心页面组成,每条链独立实现:

graph LR
    A["选择收款地址<br/>AddressPage"] --> B["输入金额<br/>AmountPage"] --> C["交易确认<br/>ConfirmPage"]
    C --> D["Gas 估算"]
    C --> E["SDK 签名"]
    E --> F["广播上链"]

路由配置:通过 configs 匹配链类型,动态加载对应组件:

javascript
// 根据 baseCoinId 和 coinId 匹配对应链的发送页面
const config = configs.find(({ match }) => {
  return match(baseCoinId, coinId, protocolId);
});

const { AddressPage, AmountPage, ConfirmPage } = config.pages;

3.2 接收资产

相对简单的模块,由 symbol(币种)、chainName(链名)、address(地址)组成收款码页面。

3.3 交易历史

标准的订单 CRUD 操作,从后端服务读取和更新交易记录。


四、DApp 交互

4.1 Provider 注入

浏览器插件通过 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 独立创建

javascript
// 创建 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 对象
    }
  };
};

4.2 Middleware 路由

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

4.3 签名与广播

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

签名后数据处理关键步骤

  1. 使用 SDK 计算 txHash
  2. 构建广播参数(from、to、coinAmount、signedTx 等)
  3. 根据交易来源选择广播接口(DApp / DEX)
  4. 更新 DApp 请求状态

4.4 BTC 铭文操作(Mint / Etch)

BTC 铭文操作(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 签名数据结构

javascript
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,
};

五、UI 层模块划分

5.1 页面模块结构

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/ 密码管理"]

5.2 DApp 交易确认

DApp 交易确认采用类型转发模式,根据链和方法动态路由到对应 UI 组件:

javascript
// 根据请求类型路由到对应确认页面
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 不足显示手续费错误

六、状态管理

6.1 Redux 架构

插件使用 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

6.2 BG-UI 通信

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

七、多链支持策略

7.1 新链接入清单

接入一条新链需要完成以下模块的配置:

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/>(签名广播)"]

7.2 目前支持的链生态

生态代表链签名算法
EVMEthereum, Polygon, BSC, ArbitrumECDSA secp256k1
BTCBitcoin (主网/测试网/Signet)ECDSA secp256k1
SVMSolanaEd25519
TronTRONECDSA secp256k1
MoveAptos, SuiEd25519 / secp256k1
CosmosCosmos Hub, Osmosissecp256k1
OthersCardano, Stacks, StarkNet各自签名算法

总结

浏览器插件钱包的核心架构围绕 Background(后台服务)+ UI(用户界面)+ Inpage(DApp 注入) 三大运行环境构建:

  1. 账户体系:通过统一的钱包 SDK 注册和 Keyring 密钥管理,支持数十条链的密钥派生和签名
  2. DApp 交互:通过 Provider 注入 + Middleware 路由 + Handler 处理的三层架构,实现多链 RPC 方法的统一管理
  3. 状态管理:Redux + backgroundConnect 的通信适配层,使 BG-UI 跨进程通信对业务透明
  4. 可扩展性:新链接入只需按标准化流程配置 SDK → Keyring → Provider → Middleware → UI 页面