Skip to main content

Try it Live

Run Authorization examples in the interactive playground

Gas Calculations

Calculate gas costs for EIP-7702 authorizations.

Overview

EIP-7702 authorization processing has two gas cost components:
  1. Base cost per authorization: 12,500 gas
  2. Empty account cost: 25,000 gas (if delegated address is empty)
Total cost: (authCount * 12500) + (emptyCount * 25000)

Constants

Authorization.PER_AUTH_BASE_COST = 12500n;      // Base gas per authorization
Authorization.PER_EMPTY_ACCOUNT_COST = 25000n;  // Gas for empty account

calculateGasCost

Calculate total gas cost for authorization list.
Authorization.calculateGasCost.call(
  authList: Authorization.Item[],
  emptyAccounts: number
): bigint
Parameters:
  • authList: Array of authorizations
  • emptyAccounts: Number of empty accounts being delegated to
Returns: Total gas cost as bigint Formula: (authList.length * 12500) + (emptyAccounts * 25000)

Usage

import { Authorization } from 'tevm';

const authList: Authorization.Item[] = [auth1, auth2, auth3];
const emptyAccountCount = 2;

const gas = Authorization.calculateGasCost.call(authList, emptyAccountCount);
console.log(`Total gas: ${gas}`);
// gas = (3 * 12500) + (2 * 25000) = 87500

Examples

All empty accounts:
const gas = Authorization.calculateGasCost.call([auth1, auth2], 2);
// (2 * 12500) + (2 * 25000) = 75000
No empty accounts:
const gas = Authorization.calculateGasCost.call([auth1, auth2], 0);
// (2 * 12500) + (0 * 25000) = 25000
Empty list:
const gas = Authorization.calculateGasCost.call([], 0);
// (0 * 12500) + (0 * 25000) = 0
Mixed:
const gas = Authorization.calculateGasCost.call([auth1, auth2, auth3, auth4], 1);
// (4 * 12500) + (1 * 25000) = 75000

getGasCost

Calculate gas cost for single authorization.
Authorization.getGasCost.call(
  auth: Authorization.Item,
  isEmpty: boolean
): bigint
Parameters:
  • auth: Authorization to calculate cost for
  • isEmpty: Whether delegated address is empty
Returns: Gas cost as bigint Formula:
  • If empty: 12500 + 25000 = 37500
  • If not empty: 12500

Usage

import { Authorization } from 'tevm';

const auth: Authorization.Item = {...};

// Empty account
const gasIfEmpty = Authorization.getGasCost.call(auth, true);
console.log(`Gas (empty): ${gasIfEmpty}`);
// 37500

// Non-empty account
const gasIfNotEmpty = Authorization.getGasCost.call(auth, false);
console.log(`Gas (not empty): ${gasIfNotEmpty}`);
// 12500

Empty Account Detection

What is an Empty Account?

An account is considered empty if:
  • Balance = 0
  • Nonce = 0
  • Code length = 0
  • Storage is empty

Checking if Account is Empty

import { Authorization } from 'tevm';

async function isAccountEmpty(address: Address): Promise<boolean> {
  const balance = await getBalance(address);
  const nonce = await getNonce(address);
  const code = await getCode(address);

  return balance === 0n && nonce === 0n && code.length === 0;
}

// Use in gas calculation
const isEmpty = await isAccountEmpty(auth.address);
const gas = Authorization.getGasCost.call(auth, isEmpty);

Counting Empty Accounts

import { Authorization } from 'tevm';

async function countEmptyAccounts(
  authList: Authorization.Item[]
): Promise<number> {
  let count = 0;

  for (const auth of authList) {
    const isEmpty = await isAccountEmpty(auth.address);
    if (isEmpty) count++;
  }

  return count;
}

// Use in gas calculation
const emptyCount = await countEmptyAccounts(authList);
const gas = Authorization.calculateGasCost.call(authList, emptyCount);

Gas Estimation Patterns

Pre-transaction Estimation

Estimate gas before sending transaction:
import { Authorization } from 'tevm';

async function estimateAuthGas(
  authList: Authorization.Item[]
): Promise<bigint> {
  // Count empty accounts
  const emptyCount = await countEmptyAccounts(authList);

  // Calculate authorization gas
  const authGas = Authorization.calculateGasCost.call(authList, emptyCount);

  // Add execution gas (estimated separately)
  const executionGas = 21000n; // Base transaction cost
  const totalGas = authGas + executionGas;

  return totalGas;
}

const gasEstimate = await estimateAuthGas(authList);
console.log(`Estimated gas: ${gasEstimate}`);

With Gas Limit

Check if within gas limit:
import { Authorization } from 'tevm';

async function validateGasLimit(
  authList: Authorization.Item[],
  gasLimit: bigint
): Promise<void> {
  const emptyCount = await countEmptyAccounts(authList);
  const authGas = Authorization.calculateGasCost.call(authList, emptyCount);

  if (authGas > gasLimit) {
    throw new Error(
      `Authorization gas (${authGas}) exceeds limit (${gasLimit})`
    );
  }
}

await validateGasLimit(authList, 100000n);

Per-Authorization Cost Breakdown

Calculate cost for each authorization:
import { Authorization } from 'tevm';

interface AuthGasCost {
  auth: Authorization.Item;
  isEmpty: boolean;
  gas: bigint;
}

async function calculateIndividualCosts(
  authList: Authorization.Item[]
): Promise<AuthGasCost[]> {
  const costs: AuthGasCost[] = [];

  for (const auth of authList) {
    const isEmpty = await isAccountEmpty(auth.address);
    const gas = Authorization.getGasCost.call(auth, isEmpty);

    costs.push({ auth, isEmpty, gas });
  }

  return costs;
}

const costs = await calculateIndividualCosts(authList);
costs.forEach((c, i) => {
  console.log(`Auth ${i}: ${c.gas} gas (${c.isEmpty ? 'empty' : 'not empty'})`);
});

Optimization Strategies

Minimize Empty Account Delegations

Empty accounts cost more gas. Prefer delegating to deployed contracts:
// More expensive: delegate to empty address
const expensiveAuth = {
  chainId: 1n,
  address: emptyAddress,  // 37,500 gas
  nonce: 0n
};

// Cheaper: delegate to deployed contract
const cheaperAuth = {
  chainId: 1n,
  address: deployedContract,  // 12,500 gas
  nonce: 0n
};

Batch Size Optimization

Larger batches have better gas efficiency per authorization:
import { Authorization } from 'tevm';

function calculateEfficiency(
  authCount: number,
  emptyCount: number
): number {
  const totalGas = Authorization.calculateGasCost.call(
    new Array(authCount).fill(null),
    emptyCount
  );

  const gasPerAuth = Number(totalGas) / authCount;
  return gasPerAuth;
}

// Examples
calculateEfficiency(1, 0);   // 12,500 gas/auth
calculateEfficiency(10, 0);  // 12,500 gas/auth (same)
calculateEfficiency(1, 1);   // 37,500 gas/auth
calculateEfficiency(10, 5);  // 25,000 gas/auth (better due to batching)

Deduplication

Remove duplicate delegations to save gas:
import { Authorization, Address } from 'tevm';

function deduplicateByAddress(
  authList: Authorization.Item[]
): Authorization.Item[] {
  const seen = new Set<string>();
  const unique: Authorization.Item[] = [];

  for (const auth of authList) {
    const key = Address.toHex(auth.address);

    if (!seen.has(key)) {
      seen.add(key);
      unique.push(auth);
    }
  }

  return unique;
}

const deduplicated = deduplicateByAddress(authList);
const savedAuths = authList.length - deduplicated.length;
const savedGas = BigInt(savedAuths) * Authorization.PER_AUTH_BASE_COST;

console.log(`Saved ${savedGas} gas by removing ${savedAuths} duplicates`);

Gas Cost Tables

Base Costs

ScenarioAuthorizationsEmpty AccountsGas Cost
Single (empty)1137,500
Single (not empty)1012,500
Batch of 5 (all empty)55187,500
Batch of 5 (none empty)5062,500
Batch of 10 (5 empty)105250,000

Cost per Authorization

Empty AccountsGas per Auth
All empty37,500
None empty12,500
50% empty25,000 (average)
25% empty18,750 (average)

Advanced Patterns

Dynamic Gas Pricing

Adjust authorization list based on gas price:
import { Authorization } from 'tevm';

async function optimizeAuthList(
  authList: Authorization.Item[],
  maxGas: bigint
): Promise<Authorization.Item[]> {
  // Calculate costs
  const costs = await calculateIndividualCosts(authList);

  // Sort by cost (cheapest first)
  costs.sort((a, b) => Number(a.gas - b.gas));

  // Include as many as possible within budget
  const optimized: Authorization.Item[] = [];
  let totalGas = 0n;

  for (const cost of costs) {
    if (totalGas + cost.gas <= maxGas) {
      optimized.push(cost.auth);
      totalGas += cost.gas;
    }
  }

  return optimized;
}

const optimized = await optimizeAuthList(authList, 100000n);

Gas Budget Allocation

Allocate gas budget across authorization types:
import { Authorization } from 'tevm';

interface GasBudget {
  empty: bigint;
  nonEmpty: bigint;
}

function allocateBudget(totalGas: bigint): GasBudget {
  // Reserve 60% for base costs, 40% for empty accounts
  const baseGas = (totalGas * 60n) / 100n;
  const emptyGas = (totalGas * 40n) / 100n;

  return { empty: emptyGas, nonEmpty: baseGas };
}

function calculateMaxAuths(budget: GasBudget): {
  maxEmpty: number;
  maxNonEmpty: number;
} {
  const maxEmpty = Number(budget.empty / Authorization.PER_EMPTY_ACCOUNT_COST);
  const maxNonEmpty = Number(budget.nonEmpty / Authorization.PER_AUTH_BASE_COST);

  return { maxEmpty, maxNonEmpty };
}

const budget = allocateBudget(100000n);
const max = calculateMaxAuths(budget);
console.log(`Can include up to ${max.maxNonEmpty} non-empty or ${max.maxEmpty} empty`);

Gas Monitoring

Track gas usage during processing:
import { Authorization } from 'tevm';

class GasMonitor {
  private totalGas = 0n;
  private authCount = 0;
  private emptyCount = 0;

  async addAuth(auth: Authorization.Item): Promise<void> {
    const isEmpty = await isAccountEmpty(auth.address);
    const gas = Authorization.getGasCost.call(auth, isEmpty);

    this.totalGas += gas;
    this.authCount++;
    if (isEmpty) this.emptyCount++;
  }

  getStats(): {
    totalGas: bigint;
    authCount: number;
    emptyCount: number;
    avgGasPerAuth: number;
  } {
    return {
      totalGas: this.totalGas,
      authCount: this.authCount,
      emptyCount: this.emptyCount,
      avgGasPerAuth: Number(this.totalGas) / this.authCount
    };
  }

  reset(): void {
    this.totalGas = 0n;
    this.authCount = 0;
    this.emptyCount = 0;
  }
}

const monitor = new GasMonitor();
for (const auth of authList) {
  await monitor.addAuth(auth);
}
console.log(monitor.getStats());

Testing

Test Gas Calculations

import { Authorization } from 'tevm';

// Empty list
expect(Authorization.calculateGasCost.call([], 0)).toBe(0n);

// Single non-empty
expect(Authorization.getGasCost.call(auth, false)).toBe(12500n);

// Single empty
expect(Authorization.getGasCost.call(auth, true)).toBe(37500n);

// Batch
const gas = Authorization.calculateGasCost.call([auth1, auth2, auth3], 2);
expect(gas).toBe(87500n); // (3 * 12500) + (2 * 25000)

Performance

Gas calculation is O(1):
// Constant time calculation
const gas = Authorization.calculateGasCost.call(authList, emptyCount);
// gas = authList.length * 12500 + emptyCount * 25000

See Also