Documentation Index
Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
Use this file to discover all available pages before exploring further.
GasRefund
GasRefund represents gas refunded after transaction execution. Post-London (EIP-3529), refunds are capped at gasUsed / 5.
Type Definition
type GasRefundType = bigint & { readonly [brand]: "GasRefund" };
Branded bigint representing gas refund (always non-negative).
Gas Refund Mechanics
Pre-London (Before EIP-3529)
Large refunds were possible from:
- SSTORE clear: 15,000 gas (clearing storage slot)
- SELFDESTRUCT: 24,000 gas (destroying contract)
Problem: Caused “gas tokens” - exploiting refunds for profit.
Post-London (EIP-3529)
Refunds significantly reduced:
- Cap: Maximum refund =
gasUsed / 5 (20% of gas used)
- SSTORE clear: Still generates 15,000 refund, but capped
- SELFDESTRUCT: No longer gives refund
Effect: Eliminated gas token exploits, simplified gas economics.
API
Constructors
from(value)
Create GasRefund from number, bigint, or string.
import { GasRefund } from '@tevm/primitives';
// SSTORE clear refund
const refund = GasRefund.from(15000n);
// No refund
const noRefund = GasRefund.from(0n);
// From number
const ref2 = GasRefund.from(15000);
Throws: InvalidFormatError if value is negative.
Conversions
toNumber(refund)
Convert to number.
const refund = GasRefund.from(15000n);
GasRefund.toNumber(refund); // 15000
toBigInt(refund)
Convert to bigint (identity operation).
const refund = GasRefund.from(15000n);
GasRefund.toBigInt(refund); // 15000n
toHex(refund)
Convert to hex string.
const refund = GasRefund.from(15000n);
GasRefund.toHex(refund); // "0x3a98"
Comparisons
equals(ref1, ref2)
Check equality.
GasRefund.equals(15000n, 15000n); // true
GasRefund.equals(15000n, 20000n); // false
Utilities
cappedRefund(refund, gasUsed)
Apply EIP-3529 refund cap.
const refund = GasRefund.from(15000n); // SSTORE clear
const gasUsed = 50000n;
// Cap = 50000 / 5 = 10000
const capped = GasRefund.cappedRefund(refund, gasUsed);
// 10000n (reduced from 15000n)
Post-London enforcement: min(refund, gasUsed / 5)
Usage Examples
Calculate Effective Gas Cost
import { GasUsed, GasRefund } from '@tevm/primitives';
const gasUsed = GasUsed.from(60000n);
const gasPrice = 20_000_000_000n; // 20 gwei
// Transaction clears 2 storage slots
const totalRefund = GasRefund.from(30000n); // 2 × 15000
// Apply EIP-3529 cap
const capped = GasRefund.cappedRefund(totalRefund, GasUsed.toBigInt(gasUsed));
// 12000n (60000 / 5)
// Calculate net cost
const grossCost = GasUsed.calculateCost(gasUsed, gasPrice);
const refundValue = GasRefund.toBigInt(capped) * gasPrice;
const netCost = grossCost - refundValue;
console.log(`Gross cost: ${grossCost} Wei`);
console.log(`Refund: ${refundValue} Wei`);
console.log(`Net cost: ${netCost} Wei`);
Compare Pre/Post London
import { GasRefund } from '@tevm/primitives';
const sstoreClear = GasRefund.from(15000n);
const gasUsed = 50000n;
// Pre-London: Full refund
const preLondon = GasRefund.toBigInt(sstoreClear);
console.log(`Pre-London refund: ${preLondon}`); // 15000
// Post-London: Capped refund
const postLondon = GasRefund.cappedRefund(sstoreClear, gasUsed);
console.log(`Post-London refund: ${postLondon}`); // 10000
const reduction = preLondon - GasRefund.toBigInt(postLondon);
console.log(`Reduction: ${reduction} gas (${(reduction / preLondon * 100).toFixed(0)}%)`);
Multiple Storage Clears
import { GasRefund } from '@tevm/primitives';
// Transaction clears 5 storage slots
const slotsCleared = 5;
const refundPerSlot = 15000n;
const totalRefund = GasRefund.from(BigInt(slotsCleared) * refundPerSlot);
// 75000n total potential refund
const gasUsed = 100000n;
// Apply cap
const capped = GasRefund.cappedRefund(totalRefund, gasUsed);
// 20000n (100000 / 5)
console.log(`Potential refund: ${GasRefund.toBigInt(totalRefund)}`); // 75000
console.log(`Capped refund: ${capped}`); // 20000
console.log(`Refund lost: ${75000n - capped}`); // 55000
Refund Sources
SSTORE Operations
| Operation | Gas Cost | Refund (Pre-London) | Refund (Post-London) |
|---|
| Zero → Non-zero | 20,000 | 0 | 0 |
| Non-zero → Non-zero | 5,000 | 0 | 0 |
| Non-zero → Zero | 5,000 | 15,000 | Capped at gasUsed/5 |
| Non-zero → Same | 100 (warm) | 0 | 0 |
SELFDESTRUCT
| Era | Gas Cost | Refund |
|---|
| Pre-London | 5,000 | 24,000 |
| Post-London | 5,000 | 0 |
EIP-3529 Rationale
Problems with Old Refund System
-
Gas Tokens: Exploited refunds for profit
- Store data when gas cheap
- Clear data when gas expensive
- Get refund at high gas price
-
Block Variability: Large refunds caused unpredictable block gas usage
-
Complexity: Made gas economics hard to reason about
Post-3529 Benefits
- No Gas Tokens: Capped refunds eliminate exploit
- Predictable Costs: Users pay closer to actual gas used
- Simpler Economics: Easier to estimate transaction costs
- Faster State Growth: Reduced incentive to keep unnecessary storage
effectiveRefund = min(totalRefund, gasUsed / 5)
Where:
totalRefund: Sum of all refunds from SSTORE operations
gasUsed: Actual gas consumed by transaction
- Division rounds down (integer division)
Usage Patterns
Check if Refund is Capped
import { GasRefund } from '@tevm/primitives';
function isRefundCapped(refund: bigint, gasUsed: bigint): boolean {
const refundObj = GasRefund.from(refund);
const capped = GasRefund.cappedRefund(refundObj, gasUsed);
return GasRefund.toBigInt(capped) < refund;
}
const refund = 15000n;
const gasUsed = 50000n;
console.log(isRefundCapped(refund, gasUsed)); // true
Calculate Maximum Possible Refund
import { GasRefund } from '@tevm/primitives';
function maxRefund(gasUsed: bigint): bigint {
return gasUsed / 5n;
}
const gasUsed = 100000n;
console.log(`Max refund: ${maxRefund(gasUsed)}`); // 20000
London Hard Fork
Date: August 5, 2021 (Block 12,965,000)
Changes:
- EIP-3529: Refund reduction
- EIP-1559: Fee market change
- EIP-3198: BASEFEE opcode
- EIP-3541: Reject new contracts starting with 0xEF
Impact: Most transactions now have zero or minimal refunds.
See Also