eth_getStorageAt - Linea RPC Method
Read the value from a storage slot at a given contract address on Linea. Essential for reading contract state directly, proxy implementation verification, and storage layout analysis.
Returns the value from a storage position at a given address on Linea. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.
Why Linea? Build on Consensys-backed zkEVM L2 with $1B+ TVL and 807% growth in 2025 with 15-30x lower fees than Ethereum mainnet, 6,200 TPS throughput, SWIFT integration with 12+ institutions, and $725M Consensys backing.
When to Use This Method
eth_getStorageAt is essential for enterprise developers, DeFi builders, and teams seeking Consensys ecosystem integration:
- Reading Private State — Access contract state variables that have no public getter, including variables marked as
privateorinternalin Solidity - Proxy Implementation Verification — Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on enterprise DeFi (Aave, Renzo), institutional cross-border payments via SWIFT pilots, and zkEVM-native applications
- Storage Layout Analysis — Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- State Change Monitoring — Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances
Request Parameters
The address of the contract to read storage from
Hex-encoded storage slot position (e.g., 0x0 for the first slot)
Block number as hex, or tag: latest, earliest, pending
Response Body
The 32-byte value stored at the requested position, zero-padded on the left
Error Responses
Code Examples
curl -X POST https://api-linea-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getStorageAt",
"params": [
"0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f",
"0x0",
"latest"
],
"id": 1
}'Common Use Cases
1. Verify Proxy Implementation Address
Read the EIP-1967 implementation slot to verify which contract a proxy points to on Linea:
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';
const provider = new JsonRpcProvider('https://api-linea-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
async function getProxyImplementation(proxyAddress) {
// EIP-1967 implementation slot:
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);
// Extract the address from the 32-byte value (last 20 bytes)
const implAddress = '0x' + value.slice(26);
if (implAddress === '0x' + '0'.repeat(40)) {
console.log('Not a standard EIP-1967 proxy or no implementation set');
return null;
}
console.log(`Proxy: ${proxyAddress}`);
console.log(`Implementation: ${implAddress}`);
return implAddress;
}
// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
return '0x' + value.slice(26);
}2. Read Solidity Mapping Values
Calculate the storage slot for a mapping entry and read it:
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';
const provider = new JsonRpcProvider('https://api-linea-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
async function readMapping(contractAddress, mappingSlot, key) {
// For mapping(address => uint256) at slot N:
// slot = keccak256(abi.encode(key, N))
const abiCoder = AbiCoder.defaultAbiCoder();
const encoded = abiCoder.encode(
['address', 'uint256'],
[key, mappingSlot]
);
const slot = keccak256(encoded);
const value = await provider.getStorage(contractAddress, slot);
return BigInt(value);
}
// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());3. Storage Layout Inspector
Scan multiple storage slots to analyze a contract's state:
async function inspectStorage(provider, address, slotCount = 10) {
console.log(`Inspecting storage for ${address} on Linea:`);
console.log('─'.repeat(80));
const results = [];
for (let i = 0; i < slotCount; i++) {
const value = await provider.getStorage(address, i);
const isZero = value === '0x' + '0'.repeat(64);
if (!isZero) {
// Try to interpret the value
const asNumber = BigInt(value);
const asAddress = '0x' + value.slice(26);
const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);
console.log(`Slot ${i}: ${value}`);
if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
console.log(` → Possible address: ${asAddress}`);
} else {
console.log(` → As uint256: ${asNumber}`);
}
results.push({ slot: i, value, asNumber });
}
}
return results;
}Error Handling
Common errors and solutions:
| Error Code | Description | Solution |
|---|---|---|
| -32602 | Invalid params | Verify the address is a valid 20-byte hex string, position is a valid hex quantity, and block parameter is valid |
| -32603 | Internal error | Retry with exponential backoff |
| -32000 | Execution error | The requested block may be pruned — try latest or a more recent block |
| -32005 | Rate limit exceeded | Implement caching and reduce request frequency |
async function safeGetStorage(provider, address, slot, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await provider.getStorage(address, slot);
} catch (error) {
if (error.code === -32000) {
// Block may be pruned — try latest
return await provider.getStorage(address, slot, 'latest');
}
if (error.code === -32005) {
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
continue;
}
if (i === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
}
}
}Related Methods
eth_call— Execute a contract function call without a transactioneth_getCode— Get the bytecode deployed at an addresseth_getBalance— Get the ETH balance of an addresseth_getBlockByNumber— Get block details for historical storage queries
eth_getCode
Get contract bytecode on Linea. Essential for verifying smart contracts for enterprise DeFi (Aave, Renzo), institutional cross-border payments via SWIFT pilots, and zkEVM-native applications.
eth_getTransactionCount
Get the number of transactions sent from an address (nonce) on Linea. Essential for nonce management, transaction signing, and detecting stuck transactions.