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.
StorageProof
EIP-1186 storage slot proof for trustless contract storage verification.
Overview
StorageProof represents a proof for a single storage slot in a contract’s storage trie. It enables trustless verification of contract storage values without executing transactions or trusting external providers.
Type Definition
type StorageProofType = {
/** The storage slot being proven */
readonly key: StorageKeyType;
/** The value stored at this slot (0 if uninitialized) */
readonly value: StorageValueType;
/** RLP-encoded Merkle Patricia Trie nodes (root to leaf) */
readonly proof: readonly Uint8Array[];
};
type StorageProofLike = StorageProofType | {
key: StorageKeyType;
value: StorageValueType;
proof: readonly Uint8Array[];
};
Usage
Create StorageProof
import * as StorageProof from './primitives/StorageProof/index.js';
const proof = StorageProof.from({
key: 0n, // storage slot 0
value: 1000000000000000000n, // stored value
proof: [/* RLP-encoded trie nodes */],
});
Compare Proofs
const isEqual = StorageProof.equals(proof1, proof2);
API Reference
Constructors
| Function | Description |
|---|
from(proof) | Create from StorageProofLike object |
Methods
| Function | Description |
|---|
equals(a, b) | Check if two proofs are equal |
Obtaining Storage Proofs
Use eth_getProof with storage slots:
const result = await provider.send("eth_getProof", [
contractAddress,
["0x0", "0x1", "0x2"], // storage slots to prove
"latest"
]);
// Extract storage proofs
const storageProofs = result.storageProof.map(sp => StorageProof.from(sp));
Storage Slot Calculation
Solidity storage layout determines slot numbers:
// Slot 0: first state variable
const slot0 = 0n;
// Mapping: keccak256(key . slot)
import * as Hash from './primitives/Hash/index.js';
const mappingSlot = Hash.keccak256(concat(
padLeft(key, 32),
padLeft(slot, 32)
));
// Dynamic array: keccak256(slot) + index
const arraySlot = BigInt(Hash.toHex(Hash.keccak256(padLeft(slot, 32)))) + index;
Verification Process
function verifyStorageProof(
proof: StorageProofType,
storageRoot: StateRootType
): boolean {
// 1. Compute path = keccak256(key)
const path = Hash.keccak256(proof.key);
// 2. Walk Merkle Patricia Trie from root
// 3. Verify proof nodes against path
// 4. Extract value from leaf
// 5. Compare with proof.value
return true;
}
Use Cases
Verify Token Balance
// ERC-20 balanceOf mapping at slot 0
const balanceSlot = keccak256(concat(
padLeft(holderAddress, 32),
padLeft(0n, 32) // balances mapping slot
));
const proof = await provider.send("eth_getProof", [
tokenContract,
[toHex(balanceSlot)],
"latest"
]);
// Verify against trusted state root
const isValid = verifyStorageProof(
proof.storageProof[0],
proof.storageHash
);
console.log(`Verified balance: ${proof.storageProof[0].value}`);
Cross-Chain Storage Proof
// Prove L1 contract storage for L2 verification
const l1Proof = await l1Provider.send("eth_getProof", [
l1Contract,
[storageSlot],
blockNumber
]);
// Submit to L2 bridge contract
await l2Bridge.verifyL1Storage(
l1Contract,
storageSlot,
l1Proof.storageProof[0].proof,
l1StateRoot
);
Oracle-Free Price Feeds
// Prove Uniswap pool reserves without oracle
const slot0 = "0x0"; // reserves slot
const proof = await provider.send("eth_getProof", [
uniswapPool,
[slot0],
trustedBlockNumber
]);
// Verify and extract reserves
const reserves = decodeReserves(proof.storageProof[0].value);
Proof Structure
Account's Storage Trie
|
+-- storageRoot (32 bytes)
|
+-- Merkle Patricia Trie
|
+-- keccak256(slot0) --> value0
| proof[0]
|
+-- keccak256(slot1) --> value1
| proof[1]
...
Common Storage Slots
| Pattern | Slot Calculation |
|---|
| Simple variable | Sequential from 0 |
| Mapping | keccak256(key . slot) |
| Nested mapping | keccak256(key2 . keccak256(key1 . slot)) |
| Dynamic array length | slot |
| Dynamic array element | keccak256(slot) + index |
Specification
See Also