Skip to main content

calculateErc7201(keccak256: Function, id: string): Uint8Array

Calculates a collision-resistant storage slot using the ERC-7201 formula: keccak256(keccak256(id) - 1) & ~0xff. The last byte is cleared to provide an additional 256 slots for related storage variables. Parameters:
  • keccak256: (data: Uint8Array) => Uint8Array - Keccak256 hash function
  • id: string - Namespace identifier (recommend reverse domain notation)
Returns: Uint8Array - 32-byte storage slot with last byte cleared Example:
import { calculateErc7201 } from '@tevm/voltaire/Storage';
import { keccak256 } from '@tevm/voltaire/crypto';

// Calculate storage slot for namespace
const slot = calculateErc7201(keccak256, 'example.main.storage');

console.log(Buffer.from(slot).toString('hex'));
// "c7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181c0"
// Note: Last byte is 0x00
Defined in: src/primitives/Storage/calculateErc7201.js

Formula Breakdown

1. innerHash = keccak256(id)
2. decremented = innerHash - 1
3. outerHash = keccak256(decremented)
4. result = outerHash & ~0xff  // Clear last byte
The formula provides:
  • Collision resistance: Double hash prevents preimage attacks
  • 256 slots: Last byte cleared provides slots 0x00-0xFF for related variables
  • Deterministic: Same ID always produces same slot

Usage Patterns

Solidity Storage Pattern

import { calculateErc7201 } from '@tevm/voltaire/Storage';
import { keccak256 } from '@tevm/voltaire/crypto';

// Calculate slot for Solidity contract
const namespace = 'com.example.mycontract.storage';
const slot = calculateErc7201(keccak256, namespace);

// Convert to Solidity hex literal
const hexSlot = '0x' + Buffer.from(slot).toString('hex');

console.log(`
bytes32 private constant STORAGE_LOCATION = ${hexSlot};

struct MyStorage {
  uint256 value;
  mapping(address => uint256) balances;
}

function _getStorage() private pure returns (MyStorage storage $) {
  assembly {
    $.slot := STORAGE_LOCATION
  }
}
`);
import { calculateErc7201 } from '@tevm/voltaire/Storage';
import { keccak256 } from '@tevm/voltaire/crypto';

// Main storage slot
const baseSlot = calculateErc7201(keccak256, 'my.contract.storage');

// Related slots (last byte can be 0x00 - 0xFF)
const adminSlot = new Uint8Array(baseSlot);
adminSlot[31] = 0x01;

const configSlot = new Uint8Array(baseSlot);
configSlot[31] = 0x02;

const dataSlot = new Uint8Array(baseSlot);
dataSlot[31] = 0x03;

// All slots guaranteed collision-free

Namespace Conventions

import { calculateErc7201 } from '@tevm/voltaire/Storage';
import { keccak256 } from '@tevm/voltaire/crypto';

// ✅ Good: Reverse domain notation
calculateErc7201(keccak256, 'org.uniswap.v3.storage');
calculateErc7201(keccak256, 'com.openzeppelin.access.ownable');

// ✅ Good: Contract-specific namespaces
calculateErc7201(keccak256, 'MyToken.balances');
calculateErc7201(keccak256, 'MyToken.allowances');

// ❌ Bad: Too generic (collision risk across projects)
calculateErc7201(keccak256, 'storage');
calculateErc7201(keccak256, 'data');

Comparison with ERC-8042

FeatureERC-7201ERC-8042
Formulakeccak256(keccak256(id) - 1) & ~0xffkeccak256(id)
Related slots256 (last byte)None
Collision resistanceHigher (double hash)Standard (single hash)
Use caseGeneral upgradeable contractsDiamond Standard

See Also