跳转到内容

以太坊账户

以太坊账户

以太坊中有两种账户类型:外部账户(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 地址生成过程

  1. 生成随机私钥(256 位)
  2. 使用椭圆曲线加密(secp256k1)从私钥推导出公钥
  3. 对公钥进行 Keccak-256 哈希
  4. 取哈希值的最后 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 是一个单调递增的计数器,用于:

  1. 防止重放攻击:每笔交易必须包含正确的 nonce,防止同一笔交易被广播多次
  2. 确保交易顺序:保证交易按照顺序执行
// 使用 ethers.js 获取账户 nonce
const provider = new ethers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_KEY');
const nonce = await provider.getTransactionCount('0xYourAddress');
console.log('当前 nonce:', nonce);

账户余额

以太坊的最小单位是 wei,不同单位换算如下:

单位Wei 值
Wei1
Gwei10^9 Wei
Ether10^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);
// 发送 ETH
const 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 标准)
Terminal window
# 使用 .env 文件存储私钥(不要提交到版本控制)
PRIVATE_KEY=0x...

账户抽象(ERC-4337)

账户抽象是以太坊的重要升级方向,允许合约账户像 EOA 一样发起交易,提供更好的用户体验:

  • 社交恢复:无需私钥即可恢复账户
  • 批量交易:在一次操作中执行多个交易
  • 无 Gas 交易:由第三方代付 Gas 费用

总结

特征外部账户(EOA)合约账户
控制方式私钥代码逻辑
发起交易可以不能(只能响应)
关联代码
存储
创建成本免费需要 Gas

理解以太坊账户模型是进行智能合约开发和 DApp 开发的基础。两种账户类型共同构成了以太坊强大而灵活的状态机系统。