web3.js web3-eth-accounts 使用指南:Ethereum 账户管理与交易签名
web3.js web3-eth-accounts 使用指南Ethereum 账户管理与交易签名【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.jsweb3-eth-accounts是 web3.js 的官方子包专门负责 Ethereum 账户的生成、管理与数据/交易签名。本文基于当前仓库中该包的 README 展开并结合其源码account.ts、wallet.ts、tx/目录等深入讲解账户创建、消息签名、三类 Typed Transaction 签名、V3 Keystore 加解密与内存钱包的完整用法读者学完后可以直接在 Node.js 或浏览器应用中落地账户管理功能。包定位与功能总览web3-eth-accounts是 web3.js 的模块化子包之一其职责定义在源码注释中The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.即生成 Ethereum 账户、签名交易、签名任意数据。它同时提供账户生成create、privateKeyToAccount地址/公钥推导privateKeyToAddress、privateKeyToPublicKey消息签名与验签hashMessage、sign、signRaw、recover、recoverTransaction交易签名signTransaction支持 legacy、EIP-2930、EIP-1559Keystoreencrypt、decryptV3 JSON Keystorescrypt / pbkdf2钱包Wallet内存钱包create/add/get/remove/clear/encrypt/decrypt/save/load从源码结构看包的公共导出入口在 packages/web3-eth-accounts/src/index.ts它统一导出wallet、account、types、schemas以及common链参数、EIP、硬分叉定义和tx交易类型实现两大内部模块。tx/目录中的 tx/index.ts 明确标注其交易实现源自ethereumjs/txv4.1.1 的思路提供了Transactionlegacy、AccessListEIP2930Transaction、FeeMarketEIP1559Transaction以及TransactionFactory工厂。安全提示源自源码官方注释该包尚未经过审计NOT been audited在生产环境使用前必须妥善清理内存、安全保管私钥并充分测试交易收发功能。请务必阅读本文最后一节的注意事项。安装与前置条件安装推荐使用 NPM 或 Yarn 安装# NPM npm install web3-eth-accounts # Yarn yarn add web3-eth-accounts环境要求依据包的 package.json项目要求Node.js14npm6.12.0ECMAScriptES2020tsconfig目标包管理器开发Yarn / Lerna包同时提供lib/commonjsCJS与lib/esmESM双格式产物exports字段中require指向 CJS、import指向 ESM可无缝用于 Node 与打包工具。依赖关系该包依赖以下运行时库见 package.jsonethereum-cryptography提供 secp256k1 曲线、AES、scrypt/pbkdf2 等密码学原语ethereumjs/rlpRLP 编码crc-32校验计算web3-errors/web3-types/web3-utils/web3-validator错误类型、TS 类型、工具函数与 JSON Schema 校验两种使用方式方式一通过web3主包访问推荐安装web3主包后账户功能挂载在web3.eth.accounts下import Web3 from web3; const web3 new Web3(Web3.givenProvider || ws://some.local-or-remote.node:8546); const account web3.eth.accounts.create(); const result web3.eth.accounts.hashMessage(Test Message);这种模式下web3.eth.accounts.signTransaction是有状态的web3主包在 packages/web3/src/accounts.ts 中通过initAccountsForContext(context)将账户模块与Web3Context绑定签名前会先调用web3-eth的prepareTransactionForSigning(transaction, context)自动补齐nonce、chainId等网络相关信息。该上下文绑定逻辑在 packages/web3/src/web3.ts 中通过initAccountsForContext(this)注入。方式二独立使用子包轻量应用只安装web3-eth-accounts按需导入函数适合对包体积敏感的应用import { create, hashMessage, signTransaction, Transaction } from web3-eth-accounts; const account create(); const result hashMessage(Test Message);⚠️重要区别独立导入时signTransaction是无状态的。源码 account.ts 明确说明由于没有网络访问能力去获取账户nonce与chainId函数依赖调用方传入完整的交易对象如需签名不完整的交易对象应使用web3.eth.accounts.sign。同样privateKeyToAccount返回的signTransaction会直接抛出TransactionSigningError(Do not have network access to sign the transaction)见 account.ts。账户的创建与导入create生成全新账户const account web3.eth.accounts.create(); // { // address: 0xbD504f977021b5E5DdccD8741A368b147B3B38bB, // privateKey: 0x964ced1c69ad27a311c432fdc0d8211e987595f7eb34ab405a5f16bdc9563ec5, // signTransaction: [Function], // sign: [Function], // encrypt: [AsyncFunction] // }源码 account.ts 显示create使用secp256k1.utils.randomPrivateKey()来自经审计的ethereum-cryptography包基于密码学安全随机数生成 32 字节私钥再交给privateKeyToAccount组装账户对象。privateKeyToAccount从私钥导入账户const account web3.eth.accounts.privateKeyToAccount( 0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709, ); // 返回 { address: 0xb8CE9ab6943e0eCED004cDe8e3bBed6568B2Fa01, privateKey: 0x348c..., sign, signTransaction, encrypt }privateKeyToAddress私钥推导地址web3.eth.accounts.privateKeyToAddress( 0xbe6383dad004f233317e46ddb46ad31b16064d14447a95cc1d8c8d4bc61c3728, ); // 0xEB014f8c8B418Db6b45774c326A0E64C78914dC0privateKeyToPublicKey私钥推导公钥web3.eth.accounts.privateKeyToPublicKey( 0x1e046a882bb38236b646c9f135cf90ad90a140810f439875f2a6dd8e50fa261f, true, // isCompressed: true 返回 33 字节压缩公钥false 返回 65 字节非压缩公钥 );底层原理私钥校验与地址推导上述函数都经过parseAndValidatePrivateKeyaccount.ts做统一校验输入为string或Uint8Array十六进制字符串长度必须为 660x 64 位否则抛PrivateKeyLengthError字节长度必须为 32 字节否则抛PrivateKeyLengthError转换失败抛InvalidPrivateKeyError。地址推导过程见privateKeyToAddressaccount.ts由私钥经 secp256k1 得到非压缩公钥前缀0x04去掉前缀字节后对剩余部分做 Keccak-256 哈希sha3Raw注意这里是 Ethereum 的 Keccak 而非 NIST SHA3取哈希后 20 字节后 40 个十六进制字符即为地址经toChecksumAddress生成 EIP-55 校验和格式地址。消息签名与验签hashMessage生成 Ethereum 签名消息哈希web3.eth.accounts.hashMessage(Hello world); // 0x8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede // 传入 UTF8 Hex 编码的消息也会被解码后再哈希结果一致 web3.eth.accounts.hashMessage(web3.utils.utf8ToHex(Hello world)); // 0x8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede // skipPrefixtrue 时不添加 Ethereum 前缀 web3.eth.accounts.hashMessage(Hello world, true); // 0xed6c11b0b5b808960df26f5bfc471d04c1995b0ffd2055925ad1be28d6baadfd源码 account.ts 揭示其实现消息按\x19Ethereum Signed Message:\n message.length message包装后用 Keccak-256sha3Raw哈希。skipPrefix参数默认false控制是否跳过该前缀。sign签名任意数据带 Ethereum 前缀web3.eth.accounts.sign( Some data, 0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318, ); // { // message: Some data, // messageHash: 0x1da44b586eb0729ff70a73c326926f6ed5a25f5b056e7f47fbc6e58d86871655, // v: 0x1c, // r: 0xb91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd, // s: 0x6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a029, // signature: 0xb91467...f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c // }signRaw签名原始数据无前缀signRaw使用hashMessage(data, true)跳过 Ethereum 前缀直接对原始数据哈希签名account.ts适合需要与其他链或自定义协议兼容的场景。signMessageWithPrivateKey底层签名组件sign与signRaw最终都调用signMessageWithPrivateKey(hash, privateKey)account.ts它使用 secp256k1 对消息哈希签名返回{ messageHash, v, r, s, signature }。其中v recovery 27signature为r || s || v的拼接。recover从签名恢复地址const data Some data; const sigObj web3.eth.accounts.sign( data, 0xbe6383dad004f233317e46ddb46ad31b16064d14447a95cc1d8c8d4bc61c3728, ); // 方式一传入签名对象 web3.eth.accounts.recover(sigObj); // 方式二传入 v / r / s web3.eth.accounts.recover(data, sigObj.v, sigObj.r, sigObj.s); // 0xEB014f8c8B418Db6b45774c326A0E64C78914dC0recover支持三种入参形态签名对象、完整签名串、(data, v, r, s)分量形式并从签名中解析v大于 26 时减去 27恢复公钥再推导地址account.ts。recoverTransaction从 RLP 交易恢复签名地址web3.eth.accounts.recoverTransaction( 0xf869808504e3b29200831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca008025a0c9cf86333bcb065d140032ecaab5d9281bde80f21b9687b3e94161de42d51895a0727a108a0b8d101465414033c3f705a9c7b826e596766046ee1183dbc8aeaa68, ); // 0x2c7536E3605D9C16a7a3D7b1898e529396a65c23实现上先通过TransactionFactory.fromSerializedData解码 RLP 数据得到交易对象再取getSenderAddress()account.ts。交易签名支持三种交易类型signTransaction接受一个TypedTransaction即Transaction/AccessListEIP2930Transaction/FeeMarketEIP1559Transaction三者之一见 types.ts签名后返回{ messageHash, v, r, s, rawTransaction, transactionHash }。签名 Legacy 交易import { signTransaction, Transaction } from web3-eth-accounts; signTransaction( new Transaction({ to: 0x118C2E5F57FD62C2B5b46a5ae9216F4FF4011a07, value: 0x186A0, gasLimit: 0x520812, gasPrice: 0x09184e72a000, data: , chainId: 1, nonce: 0, }), 0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318, );签名 EIP-1559 交易signTransaction( new Transaction({ to: 0xF0109fC8DF283027b6285cc889F5aA624EaC1F55, maxPriorityFeePerGas: 0x3B9ACA00, maxFeePerGas: 0xB2D05E00, gasLimit: 0x6A4012, value: 0x186A0, data: , chainId: 1, nonce: 0, }), 0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318, );签名 EIP-2930 交易带 Access ListsignTransaction( new Transaction({ chainId: 1, nonce: 0, gasPrice: 0x09184e72a000, gasLimit: 0x2710321, to: 0xF0109fC8DF283027b6285cc889F5aA624EaC1F55, value: 0x186A0, data: , accessList: [ { address: 0x0000000000000000000000000000000000000101, storageKeys: [ 0x0000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000000000000000000000000000000000000000060a7, ], }, ], }), 0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318, );签名返回结构示例{ messageHash: 0x28b7b75f7ba48d588a902c1ff4d5d13cc0ca9ac0aaa39562368146923fb853bf, v: 0x25, r: 0x601b0017b0e20dd0eeda4b895fbc1a9e8968990953482214f880bae593e71b5, s: 0x690d984493560552e3ebdcc19a65b9c301ea9ddc82d3ab8cfde60485fd5722ce, rawTransaction: 0xf869808609184e72a0008352081294118c2e5f57fd62c2b5b46a5ae9216f4ff4011a07830186a08025a00601b0017b0e20dd0eeda4b895fbc1a9e8968990953482214f880bae593e71b5a0690d984493560552e3ebdcc19a65b9c301ea9ddc82d3ab8cfde60485fd5722ce, transactionHash: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 }签名实现细节signTransactionaccount.ts的核心流程调用transaction.sign(hexToBytes(privateKey))生成签名校验v/r/s是否存在否则抛TransactionSigningError调用signedTx.validate(true)做严格校验任何错误都会汇总抛出rawTransaction为signedTx.serialize()的十六进制transactionHash为 raw 交易数据的 Keccak-256 哈希。TransactionFactorytransactionFactory.ts负责按类型分发type字段为 0 时创建 legacy 交易、1 时创建 EIP-2930、2 时创建 EIP-1559无type字段时默认 legacy还提供fromSerializedData按首字节判断类型与fromBlockBodyData区分 Uint8Array 与数组并可通过registerTransactionType注册自定义交易类型。此外src/common/目录内置了 mainnet/goerli/sepolia 等链参数、20 EIP 定义与全部硬分叉配置如 common/chains/mainnet.ts、common/eips、common/hardforks保证交易签名时的参数正确性。V3 Keystore加密与解密encrypt将私钥加密为 Ethereum V3 JSON KeystoreWeb3 Secret Storagedecrypt反向还原账户。使用 scrypt 加密web3.eth.accounts .encrypt( 0x67f476289210e3bef3c1c75e4de993ff0a00663df00def84e73aa7411eac18a6, 123, { n: 8192, iv: web3.utils.hexToBytes(0xbfb43120ae00e9de110f8325143a2709), salt: web3.utils.hexToBytes(0x210d0ec956787d865358ac45716e6dd42e68d48e346d795746509523aeb477dd), }, ) .then(console.log);输出示例{ version: 3, id: c0cb0a94-4702-4492-b6e6-eb2ac404344a, address: cda9a91875fc35c8ac1320e098e584495d66e47c, crypto: { ciphertext: cb3e13e3281ff3861a3f0257fad4c9a51b0eb046f9c7821825c46b210f040b8f, cipherparams: { iv: bfb43120ae00e9de110f8325143a2709 }, cipher: aes-128-ctr, kdf: scrypt, kdfparams: { n: 8192, r: 8, p: 1, dklen: 32, salt: 210d0ec956787d865358ac45716e6dd42e68d48e346d795746509523aeb477dd }, mac: efbf6d3409f37c0084a79d5fdf9a6f5d97d11447517ef1ea8374f51e581b7efd } }使用 pbkdf2 加密web3.eth.accounts .encrypt(0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709, 123, { iv: bfb43120ae00e9de110f8325143a2709, salt: 210d0ec956787d865358ac45716e6dd42e68d48e346d795746509523aeb477dd, c: 262144, kdf: pbkdf2, }) .then(console.log);输出中kdfparams为{ dklen: 32, salt, c: 262144, prf: hmac-sha256 }。解密 Keystore 还原账户web3.eth.accounts .decrypt( { version: 3, id: c0cb0a94-4702-4492-b6e6-eb2ac404344a, address: cda9a91875fc35c8ac1320e098e584495d66e47c, crypto: { ciphertext: cb3e13e3281ff3861a3f0257fad4c9a51b0eb046f9c7821825c46b210f040b8f, cipherparams: { iv: bfb43120ae00e9de110f8325143a2709 }, cipher: aes-128-ctr, kdf: scrypt, kdfparams: { n: 8192, r: 8, p: 1, dklen: 32, salt: 210d0ec956787d865358ac45716e6dd42e68d48e346d795746509523aeb477dd, }, mac: efbf6d3409f37c0084a79d5fdf9a6f5d97d11447517ef1ea8374f51e581b7efd, }, }, 123, ) .then(console.log); // { address: 0xcdA9A91875fc35c8Ac1320E098e584495d66e47c, privateKey: 67f4..., sign, signTransaction, encrypt }参数说明与底层逻辑encrypt的可选参数CipherOptions见 account.ts参数默认值说明kdfscrypt密钥派生函数可选scrypt或pbkdf2其他值抛InvalidKdfErrorn8192scrypt 的 CPU/内存成本参数r8scrypt 块大小参数p1scrypt 并行化参数c262144pbkdf2 迭代次数小于 1000 抛PBKDF2IterationsErrordklen32派生密钥长度iv随机 16 字节初始化向量长度必须为 16 字节否则抛IVLengthErrorsalt随机 32 字节盐值加密链路私钥 → 派生密钥scrypt/pbkdf2→ 取派生密钥前 16 字节作为 AES-128-CTR 密钥加密私钥得到ciphertext→mac Keccak(derivedKey[16:32] || ciphertext)。decrypt则校验 JSON Schemaschemas.ts 中的keyStoreSchema要求包含crypto/id/version/address字段、校验version 3否则抛KeyStoreVersionError、按 KDF 派生密钥并比对mac不一致抛KeyDerivationError最后用 AES-CTR 解密得到私钥。Wallet内存钱包管理多账户Wallet继承自Web3BaseWalletwallet.ts是内存中的多账户容器其账户可直接被web3.eth.sendTransaction()或合约方法send()内部使用import { Web3 } from web3; const web3 new Web3(http://127.0.0.1:7545); const wallet await web3.eth.accounts.wallet.create(2); const signature wallet.at(0).sign(Test Data); // 使用钱包内账户 // 先给账户充值再发送交易内部用钱包账户签名 const receipt await web3.eth.sendTransaction({ from: wallet.at(0).address, to: 0xdAC17F958D2ee523a2206206994597C13D831ec7, value: 1, // ... });核心方法速查方法说明create(numberOfAccounts)批量生成账户并加入钱包不会覆盖已有账户返回钱包本身add(account \| privateKey)用账户对象或私钥字符串添加账户地址重复时打印警告并使用原索引get(addressOrIndex)按地址不区分大小写或索引获取账户不存在返回undefinedremove(addressOrIndex)移除账户成功返回true找不到返回falseclear()安全清空钱包所有账户清空_addressMap并将数组长度置 0encrypt(password, options?)用密码加密钱包内所有账户返回 Keystore V3 对象数组decrypt(encryptedWallets, password)批量解密 Keystore 数组并逐个add回钱包save(password, keyName?)仅浏览器将加密后的钱包写入localStorage默认 key 为web3js_walletload(password, keyName?)仅浏览器从localStorage读取并解密钱包// 添加账户 web3.eth.accounts.wallet.add(0xbce9b59981303e76c4878b1a6d7b088ec6b9dd5c966b7d5f54d7a749ff683387); // 移除账户 web3.eth.accounts.wallet.remove(0x85D70633b90e03e0276B98880286D0D055685ed7); // true // 整体加密/解密 await web3.eth.accounts.wallet.create(1); await web3.eth.accounts.wallet.encrypt(abc).then(console.log); // 浏览器持久化 await web3.eth.accounts.wallet.save(test#!$); // true await web3.eth.accounts.wallet.load(test#!$);save/load通过静态方法Wallet.getStorage()wallet.ts探测window.localStorage可用性并识别QuotaExceededErrorFirefox 为NS_ERROR_DOM_QUOTA_REACHED等存储异常无可用存储时抛出Local storage not available.。内部用_addressMap地址小写 → 索引维护 O(1) 的地址查找_defaultKeyName默认存储键为web3js_wallet。开发与测试脚本包内package.json提供完整的工程化脚本见 package.json脚本说明clean用rimraf清理dist/与lib/build并行构建 CJS、ESM 与类型声明build:cjs/build:esm/build:typeslint/lint:fix用eslint检查 / 自动修复format用prettier格式化代码test/test:unit运行单元测试jest配置于test/unit/jest.config.jstest:integration运行集成测试test/integration/jest.config.jstest:ciCI 环境运行带覆盖率输出的测试test:coverage:unit/test:coverage:integration输出单元 / 集成测试覆盖率仓库中对应测试位于 packages/web3-eth-accounts/test/unit含account.test.ts、wallet.test.ts以及common/、tx/子目录的交易与链参数测试和 packages/web3-eth-accounts/test/integrationfixtures 则提供 EIP-1559/EIP-2930 交易与各链配置的 JSON 样本可供深入验证签名与解码行为。安全与生产环境注意事项结合源码官方注释account.ts与包特性以下几点在生产环境中务必重视本包未经安全审计私钥生成、签名、加解密等路径应在充分评估后使用私钥妥善保管避免硬编码在源码中Keystore 密码应使用强口令并独立保存内存清理使用完毕后及时清空私钥相关的临时变量与Uint8Array无状态签名的局限独立使用web3-eth-accounts时signTransaction不访问网络需自行补齐nonce/chainId在web3主包内使用则会通过 packages/web3/src/accounts.ts 自动补齐交易签名前充分测试尤其是收款地址与金额防止误签。总结web3-eth-accounts以模块化的方式提供了 Ethereum 账户体系的完整能力从create/privateKeyToAccount的账户生成与导入到sign/signRaw/recover的消息签名验签再到覆盖 legacy、EIP-2930、EIP-1559 三类交易的signTransaction以及 V3 Keystore 的encrypt/decrypt和可持久化的Wallet内存钱包。它既可独立安装用于轻量应用也可通过web3.eth.accounts集成进主包以获得网络感知的交易签名能力。相关源码、测试与链参数定义均可在 packages/web3-eth-accounts 目录下继续深入研究。【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考