Overview
Withdrawal represents a validator withdrawal from the beacon chain to the execution layer. Introduced in EIP-4895 (Shanghai/Capella upgrade), withdrawals enable validators to exit with their stake or receive periodic reward payments.
Type: Object containing index, validatorIndex, address, and amount
Structure
type Withdrawal = {
readonly index: WithdrawalIndex; // Global withdrawal counter
readonly validatorIndex: ValidatorIndex; // Validator receiving withdrawal
readonly address: Address; // Recipient address (execution layer)
readonly amount: Gwei; // Amount in Gwei (NOT Wei!)
};
Key Concepts
- Amount: Specified in Gwei (10^9 wei), not Wei
- Two types: Full exits (32 ETH) and partial withdrawals (rewards)
- Automatic: No transaction needed, processed by protocol
- Rate limited: Maximum 16 withdrawals per block
Methods
Withdrawal.from(params)
Create Withdrawal from components.
import { Withdrawal } from '@tevm/primitives';
const withdrawal = Withdrawal.from({
index: 1000000n,
validatorIndex: 123456,
address: "0x742d35Cc6634C0532925a3b844Bc9e7595f251e3",
amount: 32000000000n, // 32 ETH in Gwei
});
Withdrawal.equals(a, b)
Check if two withdrawals are equal.
const a = Withdrawal.from({ /* ... */ });
const b = Withdrawal.from({ /* ... */ });
Withdrawal.equals(a, b); // true if all fields match
Usage Examples
Process validator exit
import { Withdrawal, ValidatorIndex, WithdrawalIndex } from '@tevm/primitives';
interface ValidatorExit {
validator: ValidatorIndex;
withdrawalAddress: string;
balance: bigint; // In Gwei
}
function createExitWithdrawal(
exit: ValidatorExit,
currentIndex: WithdrawalIndex
): Withdrawal {
return Withdrawal.from({
index: currentIndex,
validatorIndex: exit.validator,
address: exit.withdrawalAddress,
amount: exit.balance,
});
}
const exit: ValidatorExit = {
validator: ValidatorIndex.from(123456),
withdrawalAddress: "0x742d35Cc6634C0532925a3b844Bc9e7595f251e3",
balance: 32000000000n, // 32 ETH
};
const withdrawal = createExitWithdrawal(
exit,
WithdrawalIndex.from(1000000n)
);
Track partial withdrawals
import { Withdrawal } from '@tevm/primitives';
interface PartialWithdrawal {
withdrawal: Withdrawal;
isPartial: boolean;
epoch: bigint;
}
function identifyPartialWithdrawals(
withdrawals: Withdrawal[]
): PartialWithdrawal[] {
return withdrawals.map(w => ({
withdrawal: w,
// Partial withdrawals typically < 32 ETH
isPartial: w.amount < 32000000000n,
epoch: calculateEpoch(w.index),
}));
}
Convert amounts
import { Withdrawal } from '@tevm/primitives';
function gweiToWei(gwei: bigint): bigint {
return gwei * 1_000_000_000n;
}
function weiToGwei(wei: bigint): bigint {
return wei / 1_000_000_000n;
}
const withdrawal = Withdrawal.from({
index: 1000000n,
validatorIndex: 123456,
address: "0x742d35Cc6634C0532925a3b844Bc9e7595f251e3",
amount: 32000000000n, // 32 ETH in Gwei
});
// Convert to Wei for execution layer
const amountWei = gweiToWei(withdrawal.amount); // 32000000000000000000n
Build withdrawal tree
import { Withdrawal } from '@tevm/primitives';
interface WithdrawalBlock {
blockNumber: bigint;
withdrawals: Withdrawal[];
withdrawalsRoot: string;
}
function processWithdrawalBlock(block: WithdrawalBlock) {
console.log(`Block ${block.blockNumber}`);
console.log(`Total withdrawals: ${block.withdrawals.length}`);
let totalGwei = 0n;
for (const w of block.withdrawals) {
totalGwei += w.amount;
console.log(
` Validator ${w.validatorIndex}: ${w.amount} Gwei → ${w.address}`
);
}
const totalEth = Number(totalGwei) / 1_000_000_000;
console.log(`Total: ${totalEth} ETH`);
}
Important Notes
Amount is in Gwei, not Wei! Always multiply by 10^9 when converting to Wei for execution layer operations.
Maximum 16 withdrawals per block - The protocol rate-limits withdrawals to prevent excessive state changes.
References