The eth_getProof
method returns the account and storage values of a specific address, including cryptographic Merkle proofs. These proofs verify that the data is correctly included in the state trie without requiring trust in the data provider.
This method retrieves both account proofs and storage proofs for specified storage keys.
The address of the account to get proofs for
Array of storage keys to generate proofs for
Block number or block tag
The account information with Merkle proofs
The address of the account
Array of rlp-serialized MerkleTree nodes from stateRoot to the account leaf
The account's balance in wei (hex)
Hash of the account's code (hex)
The account's nonce (hex)
Hash of the account's storage trie root (hex)
Array of storage proofs for requested keys
The requested storage key
The storage value (hex)
Array of rlp-serialized MerkleTree nodes from storageHash to storage value
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"accountProof": [
"0xf90211a0...",
"0xf90211a0...",
"0xf90211a0...",
"0xf90211a0...",
"0xf90211a0...",
"0xf90211a0...",
"0xf90131a0..."
],
"balance": "0x0",
"codeHash": "0x34eb8290afdbf2cea95f7afaefe0e2e8071e0b9a3ee4f106c8f3b6a45663e05d",
"nonce": "0x1",
"storageHash": "0x9588589599c2d368b2c109def4942d664838b3b681e80898fae7d2d140fe6a4c",
"storageProof": [
{
"key": "0x0000000000000000000000000000000000000000000000000000000000000002",
"value": "0x000000000000000000000000000000000000000001cc3c3beb2aa7ced800000",
"proof": ["0xf90211a0...", "0xf90211a0...", "0xf90211a0...", "0xf90211a0..."]
},
{
"key": "0x29ae811400000000000000000000000000000000000000000000000000000000",
"value": "0x0000000000000000000000000000000000000000000000000000000000000001",
"proof": ["0xf90211a0...", "0xf90211a0...", "0xf90211a0...", "0xf90211a0..."]
}
]
}
}
A Merkle proof allows verification that a piece of data is part of a Merkle tree without having the entire tree. In Ethereum:
// Example: Verify total supply of DAI using a proof
const ethers = require('ethers');
const RLP = require('rlp');
const { keccak256 } = ethers.utils;
async function verifyTokenSupply(tokenAddress) {
// DAI token's totalSupply is stored at storage slot 2
const totalSupplySlot = '0x0000000000000000000000000000000000000000000000000000000000000002';
// Get the latest block hash for reference
const blockData = await provider.send('eth_getBlockByNumber', ['latest', false]);
const blockHash = blockData.hash;
// Get the proof
const proof = await provider.send('eth_getProof', [tokenAddress, [totalSupplySlot], 'latest']);
console.log(`Block hash: ${blockHash}`);
console.log(`State root: ${blockData.stateRoot}`);
console.log(`Storage hash: ${proof.storageHash}`);
// Extract total supply from the proof
const totalSupplyHex = proof.storageProof[0].value;
const totalSupply = BigInt(totalSupplyHex);
const totalSupplyDecimal = totalSupply / 10n ** 18n; // DAI has 18 decimals
console.log(`Verified total supply: ${totalSupplyDecimal.toString()} DAI`);
// To actually verify the proof cryptographically, we would need to:
// 1. Verify the account proof from stateRoot to storageHash
// 2. Verify the storage proof from storageHash to totalSupply value
// This requires implementing Merkle-Patricia trie verification
return {
blockHash,
stateRoot: blockData.stateRoot,
storageHash: proof.storageHash,
totalSupply: totalSupplyDecimal.toString(),
};
}
// Usage
const daiProof = await verifyTokenSupply('0x6B175474E89094C44Da98b954EedeAC495271d0F');
For Solidity contracts, storage slots can be determined as follows:
keccak256(abi.encode(key, slot))
where slot
is the position of the mapping declarationkeccak256(slot) + index
keccak256(abi.encode(userAddress, 3))