以太坊账户
以太坊账户
以太坊中有两种账户类型:外部账户(EOA) 和 合约账户。理解这两种账户的区别是学习以太坊开发的基础。
账户类型
外部账户(Externally Owned Account,EOA)
外部账户是由私钥控制的账户,也就是普通用户使用的账户(如 MetaMask 钱包账户)。
特征:
- 由私钥/公钥对控制
- 没有关联的代码
- 可以发送 ETH 和发起交易
- 创建账户是免费的
- 可以发起交易
账户结构:
nonce:该账户发出的交易数量balance:账户持有的 ETH 余额(以 wei 为单位)
合约账户(Contract Account)
合约账户是部署到以太坊上的智能合约,由代码控制而非私钥。
特征:
- 由合约代码控制
- 有关联的代码和存储
- 只能在收到交易或消息时执行代码
- 创建需要支付 Gas 费用
- 不能主动发起交易(只能响应)
账户结构:
nonce:该账户创建的合约数量balance:账户持有的 ETH 余额storageRoot:账户存储内容的哈希(Merkle Patricia 树根)codeHash:该账户 EVM 代码的哈希
账户地址
以太坊地址是一个 20 字节(40 个十六进制字符)的标识符。
EOA 地址生成过程
- 生成随机私钥(256 位)
- 使用椭圆曲线加密(secp256k1)从私钥推导出公钥
- 对公钥进行 Keccak-256 哈希
- 取哈希值的最后 20 字节作为地址
私钥 → 公钥(椭圆曲线) → Keccak-256 哈希 → 取后20字节 → 地址合约地址生成
合约地址由创建者地址和 nonce 值决定:
// 使用 CREATE 操作码address = keccak256(rlp([sender_address, sender_nonce]))[12:]
// 使用 CREATE2 操作码(可预测地址)address = keccak256(0xff ++ sender_address ++ salt ++ keccak256(init_code))[12:]账户状态
以太坊全局状态是所有账户状态的映射。每个账户状态包含以下字段:
| 字段 | 说明 |
|---|---|
| nonce | 防止重放攻击的计数器 |
| balance | 账户 ETH 余额(单位:wei) |
| storageRoot | 账户存储树的根哈希(仅合约账户) |
| codeHash | 账户代码哈希(仅合约账户) |
Nonce 的作用
Nonce 是一个单调递增的计数器,用于:
- 防止重放攻击:每笔交易必须包含正确的 nonce,防止同一笔交易被广播多次
- 确保交易顺序:保证交易按照顺序执行
// 使用 ethers.js 获取账户 nonceconst provider = new ethers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_KEY');const nonce = await provider.getTransactionCount('0xYourAddress');console.log('当前 nonce:', nonce);账户余额
以太坊的最小单位是 wei,不同单位换算如下:
| 单位 | Wei 值 |
|---|---|
| Wei | 1 |
| Gwei | 10^9 Wei |
| Ether | 10^18 Wei |
// 使用 ethers.js 查询余额const balance = await provider.getBalance('0xYourAddress');console.log('余额(ETH):', ethers.formatEther(balance));账户交互
EOA 发起交易
const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_KEY');const wallet = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
// 发送 ETHconst tx = await wallet.sendTransaction({ to: '0xRecipientAddress', value: ethers.parseEther('0.1'), // 发送 0.1 ETH gasLimit: 21000,});
await tx.wait();console.log('交易已确认,哈希:', tx.hash);与合约账户交互
const contractABI = [...]; // ABI 定义const contractAddress = '0xContractAddress';
const contract = new ethers.Contract(contractAddress, contractABI, wallet);
// 调用合约函数const result = await contract.someFunction(param1, param2);账户安全
私钥管理最佳实践
- 永远不要将私钥硬编码在代码中
- 使用环境变量或加密存储管理私钥
- 建议使用硬件钱包(Ledger、Trezor)管理大额资产
- 定期备份助记词(BIP-39 标准)
# 使用 .env 文件存储私钥(不要提交到版本控制)PRIVATE_KEY=0x...账户抽象(ERC-4337)
账户抽象是以太坊的重要升级方向,允许合约账户像 EOA 一样发起交易,提供更好的用户体验:
- 社交恢复:无需私钥即可恢复账户
- 批量交易:在一次操作中执行多个交易
- 无 Gas 交易:由第三方代付 Gas 费用
总结
| 特征 | 外部账户(EOA) | 合约账户 |
|---|---|---|
| 控制方式 | 私钥 | 代码逻辑 |
| 发起交易 | 可以 | 不能(只能响应) |
| 关联代码 | 无 | 有 |
| 存储 | 无 | 有 |
| 创建成本 | 免费 | 需要 Gas |
理解以太坊账户模型是进行智能合约开发和 DApp 开发的基础。两种账户类型共同构成了以太坊强大而灵活的状态机系统。