Accounts & Wallets

Difficulty: Beginner

Ethereum has two fundamentally different types of accounts: Externally Owned Accounts (EOAs) and Contract Accounts. Understanding the distinction between them is essential for working with the Ethereum network. Both account types live in the same address space (20-byte addresses displayed as 40-character hexadecimal strings prefixed with '0x') and can hold ETH, but they operate very differently.

An Externally Owned Account (EOA) is controlled by a private key held by a human user (or managed by software on their behalf). EOAs have no code - they are simple key-pair-based accounts. An EOA can initiate transactions by signing them with its private key. Every transaction on Ethereum must originate from an EOA - contract accounts cannot initiate transactions on their own. The address of an EOA is derived by taking the Keccak-256 hash of the public key and using the last 20 bytes. Each EOA maintains a nonce (an incrementing counter of transactions sent), which prevents replay attacks and ensures transaction ordering.

A Contract Account is created when a smart contract is deployed to the blockchain. Unlike EOAs, contract accounts do have code - the compiled bytecode of the smart contract - and they also have their own persistent storage. Contract accounts do not have private keys and cannot initiate transactions. They can only execute code in response to transactions or messages received from EOAs or other contracts. The address of a contract account is deterministically computed from the deployer's address and their nonce at the time of deployment (or using CREATE2, which uses a salt value for deterministic addressing).

Both account types share a common state structure stored in Ethereum's global state trie. Each account has four fields: nonce (transaction count for EOAs, contract creation count for contract accounts), balance (the amount of ETH held, in wei), storageRoot (a hash of the account's storage trie - empty for EOAs), and codeHash (the hash of the account's bytecode - a hash of empty data for EOAs). This state is updated with every transaction and must be maintained by all full nodes in the network.

Wallets are software interfaces that manage one or more EOAs on behalf of a user. A wallet generates and securely stores private keys, derives public keys and addresses, signs transactions, and provides a user-friendly interface for interacting with the blockchain. Browser wallets like MetaMask, hardware wallets like Ledger, and mobile wallets like Rainbow each offer different trade-offs between convenience and security. Hierarchical Deterministic (HD) wallets use a single seed phrase (typically 12 or 24 words following BIP-39) to derive an unlimited number of key pairs, making backup and recovery straightforward.

Code examples

Creating and Managing Wallets with ethers.js

import { ethers } from 'ethers';

// 1. Create a random new wallet
const randomWallet = ethers.Wallet.createRandom();
console.log('=== New Random Wallet ===');
console.log('Address:', randomWallet.address);
console.log('Private Key:', randomWallet.privateKey);
console.log('Mnemonic:', randomWallet.mnemonic.phrase);

// 2. Recover a wallet from a mnemonic phrase
const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
const recoveredWallet = ethers.Wallet.fromPhrase(mnemonic);
console.log('\n=== Recovered from Mnemonic ===');
console.log('Address:', recoveredWallet.address);

// 3. Derive multiple accounts from same mnemonic (HD wallet)
const hdNode = ethers.HDNodeWallet.fromPhrase(mnemonic);
for (let i = 0; i < 3; i++) {
  const child = hdNode.deriveChild(i);
  console.log(`Account ${i}: ${child.address}`);
}

// 4. Create wallet from a private key
const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
const walletFromKey = new ethers.Wallet(privateKey);
console.log('\n=== Wallet from Private Key ===');
console.log('Address:', walletFromKey.address);

// 5. Sign a message
const message = 'Hello, Ethereum!';
const signature = await walletFromKey.signMessage(message);
console.log('\n=== Signed Message ===');
console.log('Message:', message);
console.log('Signature:', signature);

// 6. Verify the signature (recover the signer's address)
const recoveredAddress = ethers.verifyMessage(message, signature);
console.log('Recovered address:', recoveredAddress);
console.log('Matches:', recoveredAddress === walletFromKey.address);

This demonstrates the core wallet operations: creating a new wallet with a random private key and mnemonic, recovering a wallet from a mnemonic, deriving multiple accounts from a single seed (HD wallet pattern), creating a wallet from a raw private key, signing messages, and verifying signatures. These are the building blocks of identity and authentication on Ethereum.

Key points

Concepts covered

EOA, Contract Account, Nonce, Private Key