更新记录
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-Android 与 H5 端 (同一自研核心 编译为 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实证对齐 TronWebtrx.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 /
@dcloudioCLI 完成。
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。

收藏人数:
购买普通授权版(
试用
赞赏(0)
下载 1178
赞赏 0
下载 12437781
赞赏 1934
赞赏
京公网安备:11010802035340号