Skip to content

Ethereum (ETH) Technical Reference

This document provides a comprehensive technical reference for Ethereum implementation in the go-crypto-wallet system. It covers specifications, protocol details, and implementation notes to help AI agents and developers understand Ethereum's architecture and implement features correctly.

Architecture & Transaction Flow

DocumentDescription
architecture.md[SSOT] ETH wallet architecture — wallet roles, use case assignments, Clean Architecture boundary maps (EOA + Safe), port interfaces, offline signing detail
multisig.mdETH Safe multisig — Safe v1.4.1 implementation, EIP-712 signing flow, file format, CLI commands, E2E Pattern 3
docs/transaction-flow.mdChain-agnostic 3-wallet setup, signing, and monitoring flows

For Ethereum-specific concerns on top of the common flow, see ETH-Specific Flow Details below.

Key point: ETH supports both single-sig EOA and Safe multisig flows. For single-sig, only Watch and Keygen wallets are required. For Safe multisig (E2E Pattern 3), all three wallets are used: Watch proposes and submits, Keygen and Sign wallets each sign offline. See multisig.md for the Safe multisig implementation details.


Table of Contents

  1. Overview
  2. Core Specifications
  3. Address Types & Key Derivation
  4. Transaction Architecture
  5. Transaction Types
  6. Signing Mechanism
  7. ERC-20 Token Support
  8. Network & Confirmation Guidelines
  9. Fee Management
  10. Wallet Implementation
  11. RPC & API Reference
  12. Security Considerations
  13. Testing Resources
  14. Official References
  15. ETH-Specific Flow Details

Overview

What is Ethereum?

Ethereum is a decentralized platform with smart contract functionality. Unlike Bitcoin's UTXO model, Ethereum uses an account-based model where each account has a balance and a nonce. Transactions modify account state rather than consuming and creating outputs.

Key Characteristics (2026)

PropertyValue
Launch DateJuly 30, 2015
Block Time~12 seconds (post-Merge)
ConsensusProof of Stake (since September 2022 Merge)
Native CurrencyEther (ETH)
Smallest UnitWei (1 ETH = 10^18 Wei)
Cryptographic Curvesecp256k1
Signature AlgorithmECDSA
Address Length20 bytes (40 hex characters)

Protocol Upgrade Timeline

YearUpgradeKey Features
2019IstanbulEIP-1884, opcode repricing
2021London (EIP-1559)Base fee + priority fee model
2022The MergeProof of Stake transition
2023ShanghaiStaking withdrawals
2024DencunProto-danksharding (EIP-4844)

Core Specifications

Cryptographic Primitives

Elliptic Curve (secp256k1)

Ethereum uses the same secp256k1 elliptic curve as Bitcoin for signing:

Private Key:  32 bytes (256-bit scalar)
Public Key:   64 bytes (uncompressed, without 04 prefix) or 65 bytes with prefix
Address:      20 bytes = Keccak256(pubkey)[12:]

Hash Functions

FunctionUsage
Keccak256Address derivation, transaction hashing, function selectors
RLPTransaction serialization encoding
SHA3Generic hashing (note: Ethereum uses Keccak256, not NIST SHA3)

Address Derivation

1. Generate secp256k1 key pair
2. Take uncompressed public key (64 bytes, no prefix)
3. Compute Keccak256 hash (32 bytes)
4. Take last 20 bytes
5. Apply EIP-55 checksum encoding

Data Encoding

FormatUsage
RLPTransaction serialization
HexAddresses, transaction data (0x prefix)
EIP-55Mixed-case checksum address encoding
ABISmart contract call data encoding

Address Types & Key Derivation

Address Type

This system supports EOA (Externally Owned Account) addresses for key generation and single-sig flows. Safe (Gnosis Safe v1.4.1) smart contract wallets are also supported for multisig flows — see multisig.md.

EOA addresses:

  • Are 20 bytes (40 hex chars) with 0x prefix
  • Example: 0x742d35Cc6634C0532925a3b8BC9e7595f0bEb123
  • Validated via common.IsHexAddress(addr)

HD Wallet Derivation Path

Standard: BIP44 with Ethereum coin type

m / 44' / 60' / account' / 0 / address_index
ComponentValueNotes
Purpose44'BIP44
Coin Type60'SLIP-0044 Ethereum
AccountSee table belowNon-hardened for deposit/payment/stored
Change0External chain only (no internal change addresses)
Index0..NSequential address generation

Account Types and Derivation Paths

AccountIndexHardenedPathUse
deposit0Nom/44'/60'/0'/0/iAggregate client funds
payment1Nom/44'/60'/1'/0/iOutgoing payments
stored2Nom/44'/60'/2'/0/iCold storage
auth111Yesm/44'/60'/11''/0/iAuthentication key 1
auth212Yesm/44'/60'/12''/0/iAuthentication key 2

Note: Deposit/payment/stored use non-hardened derivation so the Watch Wallet can derive child addresses from the extended public key (xpub) without private keys. Auth accounts use hardened derivation for enhanced security.


Transaction Architecture

Account Model

Ethereum uses an account-based model (not UTXO):

Account State = {
    nonce:    uint64    // Number of transactions sent
    balance:  *big.Int  // Wei balance
    codeHash: []byte    // Empty for EOA
    storage:  map       // Empty for EOA
}

Key Concepts:

  • Each transaction from an address must use the next sequential nonce
  • Transactions are identified by hash (not UTXO references)
  • Balance changes are atomic (no partial spending)

Nonce Management

Critical for transaction ordering and double-spend prevention:

go
// Get pending nonce (includes in-flight transactions)
nonce, err = eth.GetTransactionCount(ctx, fromAddr, QuantityTagPending)

// For batch operations, increment additionalNonce per transaction
effectiveNonce = nonce + additionalNonce

Rules:

  • Always use pending state to account for unconfirmed transactions
  • Nonces must be sequential — gaps cause transactions to be stuck
  • Increment additionalNonce when creating multiple transactions in the same batch

Gas

Gas is the unit of computation cost on Ethereum:

Transaction Fee = Gas Used × Effective Gas Price

// Legacy (pre-EIP-1559):
Fee = gasLimit × gasPrice

// EIP-1559 (post-London):
Fee = gasLimit × (baseFee + priorityFee)

Gas Estimation:

go
estimatedGas, err = eth.EstimateGas(ctx, msg)

Transaction Types

Legacy Transactions (Type 0)

Uses a single gasPrice field. Still supported on all networks.

go
types.LegacyTx{
    Nonce:    nonce,
    GasPrice: gasPrice,
    Gas:      gasLimit,
    To:       &toAddr,
    Value:    amount,
    Data:     data,
}

EIP-1559 Transactions (Type 2)

Introduced in the London hard fork (2021). Separates gas price into base fee (burned) + priority fee (to validator).

go
types.DynamicFeeTx{
    ChainID:   chainID,
    Nonce:     nonce,
    GasTipCap: maxPriorityFeePerGas,  // tip to validator
    GasFeeCap: maxFeePerGas,           // max total fee willing to pay
    Gas:       gasLimit,
    To:        &toAddr,
    Value:     amount,
    Data:      data,
}

Fee Calculation (EIP-1559):

go
maxPriorityFeePerGas = 2 Gwei  // configurable via config.Ethereum.MaxPriorityFeePerGas
maxFeePerGas = (baseFeePerGas * 2) + maxPriorityFeePerGas

EIP-1559 Detection:

go
// Check if network supports EIP-1559 by presence of baseFeePerGas in latest block
supported = eth.SupportsEIP1559(ctx)

Current Implementation Note: The Watch Wallet currently creates Legacy transactions (types.LegacyTx). The EIP-1559 path (CreateRawTransactionEIP1559) exists in the infrastructure layer but is not yet used by the Watch use case.


Signing Mechanism

Signer Types

SignerTransaction TypeDescription
types.HomesteadSignerLegacyOriginal signer (no replay protection)
types.NewEIP155Signer(chainID)LegacyReplay protection (EIP-155)
types.NewLondonSigner(chainID)Legacy + EIP-1559Used in this system

Signing Flow

go
// 1. Decode hex transaction
tx := new(types.Transaction)
rlp.DecodeBytes(rawBytes, tx)

// 2. Get private key from keystore
privateKey := keystore.GetPrivKey(address, password)

// 3. Determine chain ID
// netID 1 → chainID 1 (mainnet)
// other → chainID 4 (testnet/regtest)

// 4. Create London signer (supports both legacy and EIP-1559)
signer := types.NewLondonSigner(chainID)

// 5. Sign transaction
signedTx, err := types.SignTx(tx, signer, privateKey)

// 6. Verify sender address
sender, err := types.Sender(signer, signedTx)

// 7. Encode to hex for file output

Key Storage

Private keys are stored in the go-ethereum keystore format on the local filesystem:

File: UTC--{timestamp}--{address}
Path: Configurable (default: ./data/keystore/)
Encryption: scrypt (StandardScryptN, StandardScryptP)
Password: Required to decrypt (set in configuration)

Key Import (Keygen Wallet):

go
// Uses local filesystem keystore (compatible with Anvil)
// Does NOT use personal_importRawKey RPC
ks := keystore.NewKeyStore(keyDir, keystore.StandardScryptN, keystore.StandardScryptP)
account, err := ks.ImportECDSA(ecdsaPrivKey, password)

ERC-20 Token Support

Overview

ERC-20 token transfers are supported via the erc20 infrastructure package.

Key Difference from ETH transfers:

ETH TransferERC-20 Transfer
To addressRecipientContract address
ValueTransfer amount0 (always)
DataEmptyABI-encoded transfer() call
Fee payerSenderMaster address

Function Selector

ERC-20 transfer(address,uint256) is encoded as:

go
// Function selector = first 4 bytes of Keccak256("transfer(address,uint256)")
selector := crypto.Keccak256([]byte("transfer(address,uint256)"))[:4]

// Call data = selector + padded address (32 bytes) + padded amount (32 bytes)
data = append(selector, paddedAddress...)
data = append(data, paddedAmount...)

Limitations

  • Requires prior approve() transaction for some flows
  • Master address pays ETH gas fees (not token holder)
  • No ERC-721 or ERC-1155 support

Network & Confirmation Guidelines

Supported Networks

NetworkChain IDPurposeRPC Port
Mainnet1Production8545
Sepolia11155111Public testnet8545
Holesky17000Staking testnet8545
Anvil (local)31337Local development8545

Chain ID Mapping in this system:

go
// netID 1 → chainID 1 (mainnet)
// other → chainID 4 (treated as testnet; historical Rinkeby ID)

Confirmation Guidelines

ConfirmationsRisk LevelTypical Use Case
0 (pending)HighDevelopment only
1MediumLow-value transfers
6LowStandard commerce
12Very LowLarge transactions
32+NegligibleHigh-value custody

Fee Management

Fee Components (EIP-1559)

ComponentDescriptionSet By
Base FeeBurned by protocol, adjusts per blockProtocol
Priority Fee (tip)Paid to validatorSender (default: 2 Gwei)
Max FeeMaximum total gas price willing to paySender

Fee Sources

SourceMethod
Gas Price (legacy)eth_gasPrice RPC
Base FeeLatest block header (baseFeePerGas)
Priority FeeConfigurable (default 2 Gwei)
Gas Estimateeth_estimateGas RPC

Fee Calculation per Transaction Type

Deposit / Transfer (full balance sweep):

transferAmount = balance - (gasPrice × estimatedGas)

Payment (fixed amount):

senderCost = amount + (gasPrice × estimatedGas)
recipientReceives = amount

Wallet Implementation

Architecture SSOT: Wallet roles, use case assignments, Clean Architecture boundary map, and signing flows are documented in architecture.md. This section provides a quick-reference summary only.

Wallet Roles (Summary)

WalletSingle-sig EOASafe MultisigNetwork
WatchCreate transactions, broadcast, monitorPropose multisig tx, submit execTransaction, monitorOnline
KeygenGenerate keys, sign transactionsGenerate keys, sign as Safe owner 1Offline (air-gapped)
SignNot usedSign as Safe owner 2…nOffline (air-gapped)

See architecture.md for the complete use case assignment table, architecture boundary map, and offline signing detail.

Database Schema (ETH-specific tables)

TableDescription
account_keyKey generation tracking with status
addressGenerated Ethereum addresses
full_pubkeyExtended public keys
eth_txUnsigned/signed transaction records
eth_detail_txPer-transaction detail (nonce, gas, amounts)
payment_requestOutbound payment requests

Address Status Lifecycle

AddrStatusHDKeyGenerated
    → (import privkey)
AddrStatusPrivKeyImported

Transaction Status Lifecycle

TxTypeSent ──(confirmations >= threshold)──> TxTypeDone ──(notified)──> TxTypeNotified

RPC & API Reference

Key go-ethereum Methods Used

MethodDescription
ethclient.BalanceAtGet account balance at block
ethclient.PendingNonceAtGet pending nonce
ethclient.SendTransactionBroadcast raw transaction
eth_gasPriceGet current gas price (legacy)
eth_estimateGasEstimate gas for transaction
eth_getTransactionCountGet nonce (use pending tag)
eth_getTransactionReceiptGet receipt (for confirmation status)
eth_getBlockByNumberGet block (for baseFeePerGas detection)
eth_callCall contract function (read-only)
net_versionGet network/chain ID

Go Libraries

LibraryVersionPurpose
go-ethereumv1.17.0Ethereum client library
ethclientHTTP/WS client
core/typesTransaction types (LegacyTx, DynamicFeeTx)
cryptoKeccak256, PubkeyToAddress
accounts/keystoreEncrypted key storage
commonHexToAddress, IsHexAddress
rlpRLP encode/decode
btcd/btcec/v2ECDSA private key operations
btcd/btcutil/hdkeychainBIP44 HD key derivation

Application Port Interface

The Ethereumer interface (defined in internal/application/ports/api/eth/interface.go) provides:

  • Balance operations: GetTotalBalance, BalanceAt
  • Network: Syncing, ProtocolVersion, NetVersion
  • Block: BlockNumber, GetBlockByNumber, EnsureBlockNumber
  • Transaction: CreateRawTransaction, CreateRawTransactionEIP1559, SendRawTransaction
  • Gas: GasPrice, EstimateGas
  • Signing: SignOnRawTransaction
  • Key management: ToECDSA, GetPrivKey, ImportRawKey

Security Considerations

Private Key Security

  • NEVER log or expose private keys
  • Private keys only exist on offline Keygen/Sign wallets
  • Keys stored encrypted via scrypt in local keystore files
  • Watch Wallet holds no private keys — only public addresses

Keystore Security

// Encryption: scrypt with standard parameters
N = 1 << 18 (StandardScryptN)
P = 1       (StandardScryptP)
r = 8

Warning: The keystore password is currently hardcoded in the codebase. This must be externalized to a secure configuration source before production use.

Transaction Security

  • Always validate destination address with common.IsHexAddress(addr)
  • Verify nonce before signing to prevent replay
  • Check sender address after signing (compare recovered address with expected)
  • EIP-155 replay protection is enforced via LondonSigner

Anvil / Local Development

  • Anvil is detected by version string containing "Anvil"
  • Key operations use local filesystem (not RPC) for Anvil compatibility
  • Chain ID is forced to testnet value for non-mainnet environments

Testing Resources

Local Development with Anvil

Anvil (from Foundry) is the recommended local Ethereum node for development:

bash
# Start Anvil with deterministic accounts
anvil --deterministic

# With specific chain ID
anvil --chain-id 1337

# Fork mainnet state
anvil --fork-url https://mainnet.infura.io/v3/<key>

Block Explorers

NetworkExplorer
Mainnetetherscan.io
Sepoliasepolia.etherscan.io
Holeskyholesky.etherscan.io

Testnet Faucets

NetworkFaucet
Sepoliasepoliafaucet.com
Holeskyholesky-faucet.pk910.de

Official References

Ethereum Improvement Proposals (EIPs)

EIPTitleRelevance
EIP-55Mixed-case checksum address encodingAddress format
EIP-155Simple replay attack protectionChain ID in signatures
EIP-191Signed data standardMessage signing
EIP-712Typed structured data hashing and signingSafe multisig safeTxHash computation
EIP-1559Fee market changeBase fee + priority fee
EIP-2718Typed Transaction EnvelopeTransaction type field
EIP-2930Access list transactions (Type 1)Optional optimization
EIP-4844Shard blob transactionsL2 scaling

BIP Standards Used

BIPTitleUsage
BIP32HD WalletsKey derivation
BIP39Mnemonic CodesSeed generation
BIP44Multi-Account HDDerivation path standard
SLIP-0044Coin TypesCoin type 60 = Ethereum

Official Documentation


ETH-Specific Flow Details

The common 3-wallet setup, signing, and monitoring flows are defined in the chain-agnostic reference: docs/transaction-flow.md. This section describes Ethereum-specific concerns on top of that common flow.

Single-Sig Flow (Ethereum)

Follows the common single-sig flow.

Ethereum-specific steps:

  • Watch Wallet fetches the pending nonce via eth_getTransactionCount with pending tag
  • Watch Wallet fetches gas price via eth_gasPrice and estimates gas via eth_estimateGas
  • Watch Wallet serializes the transaction as RLP-encoded hex
  • Keygen Wallet decodes the hex, signs with types.NewLondonSigner(chainID), re-encodes to hex
  • Watch Wallet sends via eth_sendRawTransaction
  • Watch Wallet polls for receipt via eth_getTransactionReceipt to confirm finality

Transaction Types vs Use Cases

Use CaseWatch ActionNonce SourceAmount Calculation
DepositClient address → deposit addressPending nonce of client addressbalance - fee
PaymentPayment address → external addressPending nonce of payment addressFixed amount from payment_request
Transferdeposit/stored → other internalPending nonce of source addressbalance - fee or fixed amount

Multisig

Ethereum EOA does not natively support multisig at the protocol level (unlike Bitcoin's P2SH/P2WSH). This system implements multisig via Gnosis Safe v1.4.1 — an audited smart contract that enforces an m-of-n threshold before executing any transaction.

The implementation uses a file-based, offline-signing workflow:

  1. Watch Wallet proposes a transaction and writes an unsigned JSON file
  2. Each owner (Keygen or Sign wallet) verifies the EIP-712 safeTxHash offline and appends a signature
  3. When the threshold is reached, Watch Wallet submits execTransaction on-chain

See multisig.md for the complete reference including file format, CLI commands, EIP-712 signing details, and E2E Pattern 3 (2-of-2 Safe payment).


Document Version: 1.1 Last Updated: 2026-03-08 Maintainer: go-crypto-wallet team