Skip to content

链服务实现规范

各链适配器的具体实现要求


概述

本文档定义各链适配器的实现规范,补充接口定义中未涵盖的链特有逻辑。


架构层次

┌─────────────────────────────────────────────────────────┐
│                    chain-adapter                        │
│              (统一的链适配器接口)                        │
└────────────────────────┬────────────────────────────────┘

         ┌───────────────┴───────────────┐
         │                               │
         ▼                               ▼
┌─────────────────────┐       ┌─────────────────────┐
│  内部依赖 (SDK)      │       │  外部依赖 (API)      │
│  services/*-sdk/    │       │  apis/bnqkl_wallet/ │
├─────────────────────┤       ├─────────────────────┤
│ • 密钥派生           │       │ • 余额查询          │
│ • 交易构建           │       │ • 区块信息          │
│ • 交易签名           │       │ • 交易广播          │
│ • 密码验证           │       │ • 交易历史          │
│ (本地计算)           │       │ (网络请求)          │
└─────────────────────┘       └─────────────────────┘

内部依赖 (services/bioforest-sdk/):

  • 链核心逻辑的本地实现
  • 不可替换,是 chain-adapter 的底层支柱
  • 必须自主完备:对外暴露完整 API,内部封装外部依赖
  • 其他 service 只能依赖内部 SDK,不能直接依赖外部 API

外部依赖 (apis/bnqkl_wallet/):

  • 第三方 API 服务的封装
  • 可替换(如切换 RPC 节点)
  • 只被内部 SDK 使用,不被其他 service 直接依赖

适配器架构

基础接口

typescript
interface IChainAdapter {
  readonly chainId: string
  readonly chainType: ChainConfigType  // 'bioforest' | 'evm' | 'bip39' | 'custom'
  
  // 核心服务
  readonly identity: IIdentityService
  readonly asset: IAssetService
  readonly transaction: ITransactionService
  readonly chain: IChainService
  
  // 可选服务
  readonly staking: IStakingService | null
  
  // 生命周期
  initialize(config: ChainConfig): Promise<void>
  dispose(): void
}

适配器注册

typescript
interface IAdapterRegistry {
  register(type: ChainConfigType, factory: AdapterFactory): void
  getAdapter(chainId: string): IChainAdapter
  hasAdapter(chainId: string): boolean
}

type AdapterFactory = (config: ChainConfig) => IChainAdapter

BioForest 链

适用于:BFMeta, CCChain, PMChain, BFChainV2, BTGMeta, BIWMeta, ETHMeta, Malibu

密钥派生

项目规范
算法Ed25519
种子生成SHA256(secret) → 32 bytes
地址格式{prefix} + Base58Check(RIPEMD160(SHA256(publicKey)))
默认前缀b

交易类型

类型用途手续费基准
转账原生代币/资产转移动态(链上查询)
设置交易密码启用二次签名固定
资产销毁销毁代币动态

交易密码(二次签名)

BioForest 链支持可选的交易密码机制:

交易创建流程:
1. 检查地址是否启用交易密码
2. 如已启用,签名时需提供 paySecret
3. 交易体包含 signSignature 字段

API 端点

接口路径说明
余额查询/wallet/{chain}/address/balance查询地址余额
地址信息/wallet/{chain}/address/info查询地址详情(含二次签名状态)
交易广播/wallet/{chain}/transactions/broadcast广播签名交易
交易查询/wallet/{chain}/transactions/query查询历史交易
手续费查询/wallet/{chain}/blockAveFee获取建议手续费

特殊处理

  • MUST 检查地址是否冻结(isFreezed)
  • MUST 处理交易密码验证
  • SHOULD 缓存区块高度用于交易构建

SDK 集成

BioForest 链使用 @bfchain/core SDK 进行交易创建和签名。SDK 采用源代码集成 + 延迟加载架构。

目录结构

typescript
src/services/bioforest-sdk/
├── index.ts              // SDK 高层 API(公开接口)
├── types.ts              // 类型定义
├── genesis-block.json    // 创世块配置(~544KB,延迟加载)
└── internal/             // SDK 核心实现(从 @btgmeta-bundle 迁移)
    ├── index.ts          // setup() 入口函数
    ├── core/             // 核心类
    │   ├── @type.ts
    │   ├── const.ts
    │   └── core.ts       // BTGMetaBundleCore 类
    └── transaction/      // 交易控制器
        ├── @type.ts
        ├── index.ts
        ├── controller-base.ts  // 基础交易控制器
        ├── core.ts             // 核心交易逻辑
        └── exchange.ts         // 交换交易逻辑

延迟加载架构

SDK 体积较大(~1MB),MUST 使用 await import() 延迟加载,避免影响首屏性能:

typescript
// ❌ 错误:静态导入会打包进主 bundle
import { setup } from './internal'

// ✅ 正确:动态导入,按需加载
export async function getBioforestCore(): Promise<BioforestChainBundleCore> {
  const [{ setup }, genesis] = await Promise.all([
    import('./internal'),
    import('./genesis-block.json'),
  ])
  return setup(genesis.default, cryptoHelper, customRemark)
}

构建产物分析

Chunk大小加载时机
index.js~1.3MB首屏加载
bioforest-sdk-internal.js~983KB创建交易时延迟加载
genesis-block.js~544KB创建交易时延迟加载

依赖关系

@bfchain/core         # BioForest 链核心库(npm 包)
├── @bfchain/util     # 工具函数
└── buffer            # Buffer polyfill(已在 vite.config.ts 全局配置)

关键函数

typescript
// 高层 API(src/services/bioforest-sdk/index.ts)
createTransferTransaction(params)   // 创建转账交易
broadcastTransaction(rpcUrl, tx)    // 广播交易
getAddressInfo(rpcUrl, address)     // 获取地址信息(含二次签名状态)
verifyPayPassword(secret, pay, pk)  // 验证交易密码
setPayPassword(params)              // 设置交易密码

// 底层 API(通过 getBioforestCore() 获取)
core.transactionController.createTransferTransactionJSON()
core.transactionController.createSignatureTransactionJSON()
core.transactionController.getTransferTransactionMinFee()
core.accountBaseHelper().createSecondSecretKeypairV2()

交易密码流程

用户发起转账


getAddressInfo() 检查 secondPublicKey

       ├─ 无 secondPublicKey → 直接签名

       └─ 有 secondPublicKey → 需要交易密码


       用户输入交易密码


       verifyPayPassword() 验证

              ├─ 失败 → 提示错误

              └─ 成功 → 使用 paySecret 签名


              createTransferTransaction()


              broadcastTransaction()

EVM 链

适用于:Ethereum, BSC, Polygon 等 EVM 兼容链

密钥派生

项目规范
算法secp256k1
派生路径m/44'/60'/0'/0/
地址格式0x + Keccak256(publicKey)[12:32]
ChecksumEIP-55 大小写校验

Gas 估算

typescript
interface GasEstimate {
  gasLimit: bigint      // 执行上限
  gasPrice: bigint      // Legacy 模式
  maxFeePerGas?: bigint // EIP-1559 模式
  maxPriorityFeePerGas?: bigint
}

手续费档位

档位说明时间预估
slow基础价格 × 1.0~1 分钟
standard基础价格 × 1.5~30 秒
fast基础价格 × 2.0~15 秒

代币转账(ERC-20)

1. 构建 transfer(to, amount) 调用数据
2. 估算 Gas(合约调用通常 ~65,000)
3. 设置 to = 合约地址
4. 设置 value = 0
5. 设置 data = 编码后的调用数据

特殊处理

  • MUST 自动检测 EIP-1559 支持
  • MUST 正确处理 nonce 冲突
  • SHOULD 预留 20% Gas 余量

Tron 链

密钥派生

项目规范
算法secp256k1
派生路径m/44'/195'/0'/0/
地址格式Base58Check(0x41 + Keccak256(publicKey)[12:32])
地址前缀T

资源模型

Tron 使用带宽和能量而非 Gas:

资源用途获取方式
带宽普通交易免费额度 / 冻结 TRX
能量智能合约冻结 TRX

手续费估算

typescript
interface TronFeeEstimate {
  bandwidth: number     // 所需带宽
  energy: number        // 所需能量(合约调用)
  feeLimit: bigint      // 最大费用(SUN)
  activationFee?: bigint // 地址激活费(1.1 TRX)
}

地址激活

  • MUST 检查目标地址是否已激活
  • MUST 未激活地址首次转入需额外 1.1 TRX
  • SHOULD 在 UI 提示用户激活费用

TRC-20 转账

类似 ERC-20,但需注意:

  • 能量消耗通常 10,000-30,000
  • 需预估 feeLimit 防止失败

Bitcoin 链

密钥派生

项目规范
算法secp256k1
派生路径m/44'/0'/0'/0/{index} (Legacy)
地址格式P2PKH (1xxx), P2WPKH (bc1xxx)

UTXO 模型

Bitcoin 使用 UTXO(未花费交易输出)模型:

typescript
interface UTXO {
  txid: string
  vout: number
  value: bigint   // satoshi
  scriptPubKey: string
  txHex?: string  // 某些钱包需要完整交易
}

交易构建

1. 获取地址所有 UTXO
2. 选择足够的 UTXO(需覆盖金额 + 手续费)
3. 构建交易输入
4. 构建交易输出(收款地址 + 找零地址)
5. 签名所有输入
6. 广播交易

手续费估算

typescript
// 手续费 = 交易大小(vbytes) × 费率(sat/vB)
const estimatedSize = inputs * 148 + outputs * 34 + 10
const fee = estimatedSize * feeRate

特殊处理

  • MUST 正确处理 UTXO 选择
  • MUST 计算找零输出
  • SHOULD 支持 RBF(Replace-By-Fee)

错误处理

通用错误码

错误码说明
CHAIN_NOT_SUPPORTED不支持的链类型
NETWORK_ERROR网络请求失败
INSUFFICIENT_BALANCE余额不足
INSUFFICIENT_FEE手续费不足
INVALID_ADDRESS地址格式错误
TRANSACTION_REJECTED交易被拒绝

链特有错误

错误码说明
BioForestADDRESS_FROZEN地址已冻结
BioForestPAYSECRET_REQUIRED需要交易密码
TronADDRESS_NOT_ACTIVATED地址未激活
TronENERGY_INSUFFICIENT能量不足
EVMNONCE_TOO_LOWNonce 冲突
BitcoinUTXO_INSUFFICIENT可用 UTXO 不足

参考实现

参考 mpay 项目中的实现:

文件路径
BFMetalibs/wallet-base/services/wallet/bfmeta/
Ethereumlibs/wallet-base/services/wallet/ethereum/
Tronlibs/wallet-base/services/wallet/tron/
BSClibs/wallet-base/services/wallet/binance/
Bitcoinlibs/wallet-base/services/wallet/bitcoin/

Released under the MIT License.