更新记录

1.0.0(2026-07-19)

首发:多链 HD 钱包核心(32 API)。

  • BIP39 助记词 / BIP44·SLIP-0010 派生;EVM/BTC/Solana/Cosmos/Tron 五链地址与离线签名(消息/typed-data/交易);加密 keystore
  • secp256k1 恒定时间实现;私钥全程留原生层不回 JS;纯离线(不 RPC/不广播/不查余额)
  • App-Android + H5 双端同一核心 32 API 全量;iOS 原生库随包就绪、适配下版本解锁

平台兼容性

uni-app x(5.14)

Chrome Safari Android iOS 鸿蒙 微信小程序
5.0 - × ×

nex-wallet —— 高性能 EVM HD 钱包核心

🌐 在线演示:https://nexdemo.test.chengaofeng.cn —— 浏览器直接看真实渲染效果(48 组件 H5 门户,无需安装)

自研纯离线 EVM 分层确定性(HD)钱包核心,secp256k1 采用恒定时间实现,提供 uni-app 可调的 JS API。支持 App-AndroidH5 端 (同一自研核心 编译为 H5 端,见下「H5 端」);iOS:原生绑定层已就绪(原生扩展库 + Swift 绑定随包),UTS 转译适配下版本解锁;小程序不支持。

同一份 utssdk 插件同时支持 uni-app x(uvue)经典 uni-app(vue3),无需分叉。 绝不联网(铁律):不做 JSON-RPC、不广播交易、不查余额/nonce/gas、不连节点。signTransaction 只产出已签名 raw tx hex,广播由上层 app 负责(nonce/gasLimit/chainId/fee 由调用方传入)。也不做合约 ABI 编码——tx.data(calldata)由调用方拼好传入。 链范围:EVM 系(ETH + 所有 EVM 兼容链 BSC/Polygon/Arbitrum/Base…,共用 secp256k1 + 同套 tx 格式,只 chainId 不同)。批 2 已扩 BTC(secp256k1,HD 地址 + 消息签名)+ Solana(ed25519 + SLIP-0010)——见下「BTC + Solana 多链 API」章。批 5 再扩 Cosmos(secp256k1 + bech32 实现,前缀可配)+ Tron(secp256k1 + keccak + base58check)——见下「Cosmos + Tron 多链 API」章。

安全模型与诚实边界(请务必读完)

本插件把私钥/seed 留在原生层(原生句柄),签名走句柄,默认不跨语言边界 进 JS(仅 exportMnemonic / exportKeystore 显式导出)。

能挡的

  • JS 层最常见的泄露面——console.log(privateKey)、JS 堆内存 dump、把私钥写进日志 / 上报——因为私钥默认不在 JS 侧。
  • secp256k1 恒定时间实现 的 ECDSA 是 constant-time 设计,抗 timing 侧信道,强于多数纯 JS big-number 实现。

挡不住的(据实声明,别误信「金融级绝对安全」)

  • 设备被 root / 越狱后,恶意代码可注入 JS 层、直接调用本插件签名接口签任意交易——插件无法区分「合法调用」与「被劫持调用」。最终授权确认(金额 / 收款地址二次确认 UI)是上层 app 的责任
  • 私钥在签名瞬间必然以明文存在于进程内存(原生层用 Zeroizing 尽量缩短驻留);对抗物理取证 / 冷启动攻击需硬件级方案。
  • 真正硬件级要 iOS Secure Enclave / Android Keystore(TEE)。注意:secp256k1 在 iOS Secure Enclave 不原生支持(SE 只认 P-256/NIST),EVM 钱包无法直接用 SE 托管签名 → 列为未来扩展,首版不承诺。

结论:首版定位是「比纯 JS 钱包显著更安全的软件钱包核心」,不是硬件级冷钱包。请在你的 app 里补上交易二次确认、设备完整性检测等上层防护。

EVM API(12 个:5 工厂函数 + 1 对象 7 方法)

多链 API 见下方「BTC + Solana 多链 API」「Cosmos + Tron 多链 API」章(本插件共 32 个 API:EVM 12 + BTC 6 + Solana 6 + Cosmos 4 + Tron 4)。

JS API 签名 说明
工厂 generateMnemonic generateMnemonic(wordCount): string 生成助记词,wordCount ∈ {12,15,18,21,24};其它值抛错
validateMnemonic validateMnemonic(mnemonic): boolean 校验助记词(词表 + 校验和 + 词数),不抛错
walletFromMnemonic walletFromMnemonic(mnemonic, passphrase?): EvmWallet 导入助记词建 HD 根;passphrase = BIP39 第 25 词(可空)
walletFromKeystore walletFromKeystore(keystoreJson, password): EvmWallet 从加密 keystore 解锁;口令错 / 被篡改抛 Decrypt 错误
walletFromPrivateKey walletFromPrivateKey(privateKeyHex): EvmWallet 导入裸私钥(非 HD,单账户,仅 accountIndex 0)
对象 getAddress w.getAddress(accountIndex): string 派生 m/44'/60'/0'/0/{i} 的 EIP-55 校验和地址
getPublicKey w.getPublicKey(accountIndex): string 该账户压缩公钥 hex(0x02/0x03 + 32B X)
signMessage w.signMessage(accountIndex, message): string EIP-191 personal_sign,返回 65B 签名 hex
signTypedData w.signTypedData(accountIndex, typedDataJson): string EIP-712 v4,返回 65B 签名 hex
signTransaction w.signTransaction(accountIndex, tx): string 签 legacy / EIP-1559 交易,返回已签名 raw tx hex
exportKeystore w.exportKeystore(password): string 加密导出 keystore JSON(落盘交调用方)
exportMnemonic w.exportMnemonic(): string 导出助记词(明文回 JS,慎用);私钥钱包无助记词 → 抛错

类型

// 交易签名入参(大整数 wei/gas 一律十进制 string —— JS number > 2^53 失真)
type EvmTransaction = {
  to?: string;                  // 收款 0x 地址;合约部署留空
  value: string;                // wei,十进制大数字符串
  data: string;                 // 0x... calldata(调用方拼好),默认 "0x"
  nonce: number;
  gasLimit: string;             // u256 十进制
  chainId: number;              // 1=主网,56=BSC,137=Polygon…
  maxFeePerGas?: string;        // 有此字段 → EIP-1559(type2)
  maxPriorityFeePerGas?: string;
  gasPrice?: string;            // 无 1559 字段而有它 → legacy(type0)
};
  • 交易类型判定:有 maxFeePerGas → EIP-1559;否则有 gasPrice → legacy;两者都无 → 抛参数错误。
  • 签名返回signMessage / signTypedData 返回 65 字节紧凑签名 hex(0x + r||s||v,v = recId + 27);signTransaction 返回可直接 eth_sendRawTransaction 的 raw tx hex(EIP-1559 为 0x02...、legacy 为 0x...)。
  • keystore 为本仓私有格式(PBKDF2-HMAC-SHA256 600k 轮 + AES-256-GCM,AEAD 认证加密),不与 MetaMask/geth 互导(v3 互导列后续批次)。口令错或密文被篡改,解锁抛 Decrypt 错误(不会返回错误结果)。
  • 出错时原生层抛 WalletException(Android) / WalletError(iOS),UTS 侧透传给 JS,请 try/catch

用法

import {
  EvmWallet, EvmTransaction,
  generateMnemonic, validateMnemonic,
  walletFromMnemonic, walletFromKeystore, walletFromPrivateKey
} from "@/uni_modules/nex-wallet";

// 1. 生成 / 校验助记词
const mnemonic = generateMnemonic(12);     // 12 词,备份给用户
validateMnemonic(mnemonic);                // true

// 2. 导入建钱包(HD 根)
const w = walletFromMnemonic(mnemonic, null);
const addr = w.getAddress(0);              // 0x...(EIP-55 校验和地址)

// 3. 签名
const sig = w.signMessage(0, "hello");     // EIP-191,0x...(65B)
const rawTx = w.signTransaction(0, {
  to: "0xRecipient...", value: "1000000000000000000", // 1 ETH = 1e18 wei(字符串!)
  data: "0x", nonce: 5, gasLimit: "21000", chainId: 1,
  maxFeePerGas: "30000000000", maxPriorityFeePerGas: "1500000000",
} as EvmTransaction);                       // 已签名 raw tx hex,自行广播

// 4. 备份 / 恢复
const ks = w.exportKeystore("user-password");        // 加密 keystore JSON,落盘
const w2 = walletFromKeystore(ks, "user-password");  // 解锁恢复

钱包对象构造一次反复用(私钥留 自研,方法走句柄);不要把私钥/助记词存进 JS 变量或上报服务器。

BTC + Solana 多链 API(批 2 新增 12 个,全仓共 24 个)

在 EVM 钱包之外新增两个对象:BtcWallet(比特币,secp256k1)与 SolanaWallet(Solana,ed25519)。同样私钥/seed 留 自研、走顶层工厂建对象、错误向 JS 透传。全自研 密码学(RIPEMD 实现/base58 实现/bech32 实现/ed25519 实现/hmac),无额外系统依赖。

BtcWallet(6 个)—— 仅地址 + 消息签名

诚实边界(v1):BTC 仅做地址派生Bitcoin Signed Message 消息签名不做 UTXO 交易签名(无 PSBT、无 input 选择/找零构造)。转账签名是后续能力。

JS API 签名 说明
工厂 btcWalletFromMnemonic (mnemonic, passphrase?): BtcWallet 导入助记词建 HD 根
btcWalletFromWif (wif): BtcWallet 导入 mainnet WIF 单私钥(单账户,account/index 仅 0)
对象 getAddress w.getAddress(account, index, kind): string kind="p2pkh"(BIP44,base58check '1' 开头) / "p2wpkh"(BIP84,bech32 实现 'bc1q' 开头);其它抛错
getPublicKey w.getPublicKey(account, index): string 压缩公钥 hex(无 0x;默认 BIP84 路径)
signMessage w.signMessage(account, index, message): string Bitcoin Signed Message,返回 base64 签名
exportWif w.exportWif(account, index): string 导出 mainnet 压缩 WIF(默认 BIP84 路径)
import { btcWalletFromMnemonic } from "@/uni_modules/nex-wallet";
const w = btcWalletFromMnemonic("abandon abandon ... about", null);
w.getAddress(0, 0, "p2wpkh");  // bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu(BIP84 规范例)
w.getAddress(0, 0, "p2pkh");   // 1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA(BIP44)
w.signMessage(0, 0, "hello");  // base64(header||r||s)

SolanaWallet(6 个)

JS API 签名 说明
工厂 solanaWalletFromMnemonic (mnemonic, passphrase?): SolanaWallet 导入助记词建 HD 根(SLIP-0010)
solanaWalletFromSecretKey (secretKeyBase58): SolanaWallet 导入 base58 64B 私钥(单账户,account 仅 0)
对象 getAddress w.getAddress(account): string base58 公钥(Solana 地址即公钥)。派生 m/44'/501'/account'/0'
getPublicKey w.getPublicKey(account): string base58(与地址相同,保留以与 EVM/BTC API 对称)
signMessage w.signMessage(account, message): string ed25519 签名 → base58(64B)
exportSecretKey w.exportSecretKey(account): string base58 64B 私钥(seed[32]‖pubkey[32])
import { solanaWalletFromMnemonic } from "@/uni_modules/nex-wallet";
const s = solanaWalletFromMnemonic("abandon abandon ... about", null);
const addr = s.getAddress(0);            // base58 地址(= 公钥)
const sig = s.signMessage(0, "hello");   // base58(64B) ed25519 签名

算法:BTC 复用 EVM 同款 BIP32/secp256k1 派生(仅路径 purpose 不同:44=P2PKH、84=P2WPKH),地址 = hash160(压缩公钥) 再 base58check(P2PKH)或 bech32 实现(P2WPKH)。Solana 用 SLIP-0010(HMAC-SHA512,ed25519 仅硬化派生),地址 = base58(32B 公钥)。正确性由黄金向量驱动(BIP84 规范 Appendix + SLIP-0010 官方测试向量 + Bitcoin wiki 经典地址),见 docs/wallet-plugin-contract.md

诚实边界(与 EVM 一致):均软件级钱包,secp256k1 不入 iOS Secure Enclave;设备 root/越狱后运行时被注入照样能调签名接口——最终授权确认是上层 app 的责任。

Cosmos + Tron 多链 API(批 5 新增 8 个,全仓共 32 个)

再新增两个对象:CosmosWallet(Cosmos 系,secp256k1 + bech32 实现)与 TronWallet(波场,secp256k1 + keccak + base58check)。同样私钥/seed 留 自研、走顶层工厂建对象、错误向 JS 透传,全自研,无额外系统依赖。

CosmosWallet(4 个)—— 地址(前缀可配)+ 消息签名

诚实边界(v1):仅地址派生ADR-36(简化)消息签名不做交易签名(不拼 StdSignDoc / 不广播)。signMessage 为 ADR-36 的简化形态(裸 sha256(消息) 摘要),适用于「本插件签、本插件/同算法验」的自洽场景;完整 ADR-36 互操作需上层拼 StdSignDoc

JS API 签名 说明
工厂 cosmosWalletFromMnemonic (mnemonic, passphrase?): CosmosWallet 导入助记词建 HD 根(coin type 118)
对象 getAddress w.getAddress(account, index, prefix): string bech32 实现 地址,prefix 可配("cosmos"/"osmo"/"juno"…)。派生 m/44'/118'/account'/0/index
getPublicKey w.getPublicKey(account, index): string 压缩公钥 hex(无 0x,33B)
signMessage w.signMessage(account, index, message): string ADR-36(简化):sha256→ECDSA(低 s),返回 base64(r‖s 64B)
import { cosmosWalletFromMnemonic } from "@/uni_modules/nex-wallet";
const c = cosmosWalletFromMnemonic("abandon abandon ... about", null);
c.getAddress(0, 0, "cosmos");  // cosmos19rl4cm2hmr8afy4kldpxz3fka4jguq0auqdal4
c.getAddress(0, 0, "osmo");    // osmo19rl4cm2hmr8afy4kldpxz3fka4jguq0a5m7df8(同公钥、换前缀)
c.signMessage(0, 0, "hello cosmos");  // base64(r‖s)

TronWallet(4 个)—— 地址 + 消息签名

诚实边界(v1):仅地址派生TIP-191 消息签名不做交易签名(不构造/不广播 TRX/TRC-20 交易)。signMessage 实证对齐 TronWeb trx.signMessageV2,可在波场生态验签。

JS API 签名 说明
工厂 tronWalletFromMnemonic (mnemonic, passphrase?): TronWallet 导入助记词建 HD 根(coin type 195)
对象 getAddress w.getAddress(account, index): string base58check 地址('T' 开头)。派生 m/44'/195'/account'/0/index
getPublicKey w.getPublicKey(account, index): string 压缩公钥 hex(无 0x,33B)
signMessage w.signMessage(account, index, message): string TIP-191:返回 0x + r‖s‖v(65B,v=recId+27)hex
import { tronWalletFromMnemonic } from "@/uni_modules/nex-wallet";
const t = tronWalletFromMnemonic("abandon abandon ... about", null);
t.getAddress(0, 0);            // TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH
t.signMessage(0, 0, "hello tron");  // 0x...(65B,对齐 tronweb signMessageV2)

算法:两链均复用 EVM 同款 BIP32/secp256k1 派生(仅 coin type 不同:118=Cosmos、195=Tron)。Cosmos 地址 = bech32 实现(prefix, ripemd160(sha256(压缩公钥)))(标准 bech32 实现);Tron 地址 = base58check(0x41 ‖ keccak256(未压缩公钥[1..])后 20 字节)(同 EVM 取址 + 0x41 前缀)。正确性由两套独立参考实现交叉互证的黄金向量驱动(Cosmos:@scure/bip32+@noble 与 @cosmjs;Tron:@scure/@noble 与 tronweb),见 docs/wallet-plugin-contract.md

诚实边界(与 EVM 一致):均软件级钱包,secp256k1 不入 iOS Secure Enclave;设备 root/越狱后运行时被注入照样能调签名接口——最终授权确认是上层 app 的责任。

出口合规附注

本插件使用标准、公开的密码学算法(secp256k1 ECDSA、keccak256、PBKDF2-HMAC-SHA256、AES-256-GCM),无私有 / 弱加密。上架 App Store 时如被问及加密出口合规(ITSAppUsesNonExemptEncryption 等),通常属「仅使用标准算法」豁免类别——请按你所在地区与苹果最新政策据实声明(同 uni-nex-crypto 插件)。本说明不构成法律意见。

平台与调试边界

  • app-android / H5(web)(iOS:绑定层就绪、转译下版本);各家小程序不支持。
  • 整包编译 / 真机调试 原生库 需 HBuilderX ≥ 4.26(自定义调试基座,原生运行依赖 远程依赖随云打包引入)。
  • iOS(下版本转译解锁后):原生扩展库 含 Swift,宿主原生工程需开启「支持 Swift」。
  • 最终 App 打包由 HBuilderX / @dcloudio CLI 完成。

H5 端(H5 端引擎)

  • 同一份 自研核心编到 H5 端引擎32-unknown-unknown,经 H5 端引擎-bindgen 生成 JS 绑定, utssdk/web/index.uts 加载(H5 端引擎 字节 base64 内嵌,无需额外静态资源部署)。
  • 全部 32 API 均可用(全内存型,无文件 API):私钥/seed 留 H5 端引擎 线性内存不回 JS 堆; 随机源(generateMnemonic 熵 / keystore 盐·nonce)走浏览器 crypto.getRandomValues,签名全 RFC6979 确定性。
  • H5 端引擎 初始化是异步的:建议应用启动时(仅 H5 分支)await ensureReady() 一次, 过早调用会抛「尚未就绪」错误。
  • 安全提示:浏览器环境的 XSS / 恶意扩展攻击面大于 App 端,主力资金钱包建议仍走 App 端; H5 端适合离线签名演示、只读派生、轻量签名场景。
  • 真值验证:node scripts/H5 端引擎-smoke/wallet.test.mjs(ethers/Hardhat/BIP84/cosmjs/tronweb 黄金向量,与 test 对齐)。
  • 范式与全插件推进计划见 docs/h5-H5 端引擎-support.md

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据。所有派生/签名均在本地完成,插件自身不发起任何网络请求(不做 RPC、不广播交易、不查询余额),私钥/助记词不出设备。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。