Skip to main content
Utilities for working with proxy patterns including ERC-1967 storage slots, ERC-1167 minimal proxies, and ERC-3448 MetaProxies.
Implements ERC-1967 (Proxy Storage Slots), ERC-1167 (Minimal Proxy), and ERC-3448 (MetaProxy).

Overview

Proxy patterns enable contract upgradeability by separating logic (implementation) from state (proxy). The proxy delegates calls to the implementation while maintaining its own storage. Proxy Patterns:
  • ERC-1967: Standard storage slots for proxy metadata (implementation, admin, beacon)
  • ERC-1167: Minimal proxy clone (45 bytes runtime) for gas-efficient deployment
  • ERC-3448: MetaProxy with embedded metadata for factory patterns

Quick Start

import * as Proxy from '@tevm/voltaire/Proxy';
import { eth_getStorageAt } from '@tevm/voltaire/rpc';

// Read implementation address from proxy
const implementation = await eth_getStorageAt(
  proxyAddress,
  Proxy.IMPLEMENTATION_SLOT
);

// Read admin address
const admin = await eth_getStorageAt(
  proxyAddress,
  Proxy.ADMIN_SLOT
);

console.log('Implementation:', implementation);
console.log('Admin:', admin);

Proxy Patterns Comparison

FeatureERC-1967ERC-1167ERC-3448
PurposeStorage slotsMinimal cloneClone + metadata
Bytecode sizeVaries55 bytes (creation)55 + metadata + 32
Runtime sizeVaries45 bytes45 bytes (metadata not in runtime)
Gas costStandardMinimalMinimal + metadata
UpgradeabilityYes (UUPS/Transparent)NoNo
MetadataNoNoYes
Use caseUpgradeable proxiesGas-efficient clonesFactory patterns

API Documentation

Usage Patterns

UUPS Proxy Detection

import * as Proxy from '@tevm/voltaire/Proxy';
import { eth_getStorageAt } from '@tevm/voltaire/rpc';

async function detectUUPSProxy(address: string) {
  // Check if ERC-1967 implementation slot is set
  const implementation = await eth_getStorageAt(
    address,
    Proxy.IMPLEMENTATION_SLOT
  );

  if (implementation === '0x' + '00'.repeat(32)) {
    return null; // Not a proxy
  }

  return {
    type: 'UUPS',
    implementation,
    slot: Proxy.IMPLEMENTATION_SLOT
  };
}

Clone Factory

import * as Proxy from '@tevm/voltaire/Proxy';
import { Address } from '@tevm/voltaire/Address';

class CloneFactory {
  implementation: Uint8Array;
  proxyBytecode: Uint8Array;

  constructor(implementationAddress: string) {
    this.implementation = Address.from(implementationAddress);
    this.proxyBytecode = Proxy.generateErc1167(this.implementation);
  }

  async deployClone(initData?: Uint8Array) {
    // Deploy proxy
    const address = await deploy(this.proxyBytecode);

    // Initialize if needed
    if (initData) {
      await call(address, initData);
    }

    return address;
  }

  isClone(bytecode: Uint8Array): boolean {
    if (!Proxy.isErc1167(bytecode)) {
      return false;
    }

    const impl = Proxy.parseErc1167(bytecode);
    return impl.equals(this.implementation);
  }
}

MetaProxy Factory with Metadata

import * as Proxy from '@tevm/voltaire/Proxy';
import { Address } from '@tevm/voltaire/Address';
import { keccak256 } from '@tevm/voltaire/crypto';

class MetaProxyFactory {
  async deployWithMetadata(
    implementation: string,
    creator: string,
    salt: string
  ) {
    const metadata = new TextEncoder().encode(JSON.stringify({
      creator,
      salt,
      timestamp: Date.now(),
      chainId: 1
    }));

    const bytecode = Proxy.generateErc3448(
      Address.from(implementation),
      metadata
    );

    // Calculate deterministic address (CREATE2)
    const address = Address.calculateCreate2Address(
      Address.from(this.factoryAddress),
      keccak256(new TextEncoder().encode(salt)),
      bytecode
    );

    return { bytecode, address, metadata };
  }

  async readMetadata(proxyAddress: string) {
    const code = await eth_getCode(proxyAddress);

    if (!Proxy.isErc3448(code)) {
      throw new Error('Not a MetaProxy');
    }

    const { implementation, metadata } = Proxy.parseErc3448(code);
    const metadataObj = JSON.parse(
      new TextDecoder().decode(metadata)
    );

    return {
      implementation: implementation.toHex(),
      ...metadataObj
    };
  }
}

Specification References

  • Storage - Namespaced storage for upgradeable contracts
  • Address - Address utilities including CREATE2
  • Bytecode - Bytecode analysis and manipulation