eth_getStorageAt - Mantle RPC Method
Read the value from a storage slot at a given contract address on Mantle. 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 Mantle. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.
Why Mantle? Build on the world's largest ZK rollup by TVL with $2.5B+ secured and deep Bybit integration with near-instant ZK finality via OP Succinct, $6.2B treasury backing, mETH liquid staking, and 25% Bybit trading fee discounts.
When to Use This Method
eth_getStorageAt is essential for DeFi developers, liquid staking builders, and teams seeking institutional exchange 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 liquid staking (mETH $1.87B TVL), institutional DeFi via Bybit, and yield optimization strategies
- 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-mantle-mainnet.n.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getStorageAt",
"params": [
"0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8",
"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 Mantle:
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';
const provider = new JsonRpcProvider('https://api-mantle-mainnet.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-mantle-mainnet.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 = '0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8';
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 Mantle:`);
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 Mantle. Essential for verifying smart contracts for liquid staking (mETH $1.87B TVL), institutional DeFi via Bybit, and yield optimization strategies.
eth_getTransactionCount
Get the number of transactions sent from an address (nonce) on Mantle. Essential for nonce management, transaction signing, and detecting stuck transactions.