eth_getStorageAt
Retrieves storage data from a specific slot within a smart contract deployed on Polygon. This method provides low-level access to contract state, enabling deep inspection of variables, mappings, and complex data structures.
Parameters​
Parameter | Type | Required | Description |
---|---|---|---|
address | string | Yes | Contract address (20 bytes) to query storage from |
position | string | Yes | Storage slot position in hex format (32 bytes, 0x-prefixed) |
blockNumber | string | Yes | Block number in hex or block tag ("latest", "earliest", "pending") |
Storage Position Guide​
- Sequential variables: Start at slot 0x0, increment by 1
- Mapping entries:
keccak256(concat(key, mapping_slot))
- Array elements: Length at slot, elements at
keccak256(slot) + index
- Nested structures: Follow Solidity's storage layout rules
Returns​
Returns a 32-byte hex-encoded string representing the storage content. Values smaller than 32 bytes are padded with leading zeros.
Code Examples​
- cURL
- JavaScript
- Python
# Read storage from USDC contract on Polygon
curl -X POST https://api-polygon-mainnet-full.n.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getStorageAt",
"params": [
"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"0x2",
"latest"
],
"id": 1
}'
// Polygon storage reader utility
class PolygonStorageReader {
constructor(apiKey) {
this.endpoint = `https://api-polygon-mainnet-full.n.dwellir.com/${apiKey}`;
}
async getStorageAt(address, position, block = 'latest') {
const response = await fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
method: 'eth_getStorageAt',
params: [address, position, block],
id: 1
})
});
const data = await response.json();
return data.result;
}
// Calculate storage position for mapping
getMappingSlot(key, baseSlot) {
const keyHex = key.replace('0x', '').padStart(64, '0');
const slotHex = baseSlot.toString(16).padStart(64, '0');
return ethers.utils.keccak256('0x' + keyHex + slotHex);
}
// Parse common data types
parseStorageValue(hex, type) {
switch(type) {
case 'uint256':
return BigInt(hex).toString();
case 'address':
return '0x' + hex.slice(-40);
case 'bool':
return hex.slice(-1) === '1';
default:
return hex;
}
}
}
// Example: Read USDC contract data
const reader = new PolygonStorageReader('YOUR_API_KEY');
const usdcAddress = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174';
// Read total supply (slot 2)
reader.getStorageAt(usdcAddress, '0x2')
.then(result => {
const totalSupply = reader.parseStorageValue(result, 'uint256');
console.log('USDC Total Supply:', totalSupply);
});
// Read specific balance (balances mapping at slot 0)
const userAddress = '0x1234567890123456789012345678901234567890';
const balanceSlot = reader.getMappingSlot(userAddress, 0);
reader.getStorageAt(usdcAddress, balanceSlot)
.then(result => {
const balance = reader.parseStorageValue(result, 'uint256');
console.log('User USDC Balance:', balance);
});
import requests
from web3 import Web3
from eth_utils import to_checksum_address
class PolygonStorageInspector:
def __init__(self, api_key):
self.api_url = f'https://api-polygon-mainnet-full.n.dwellir.com/{api_key}'
self.w3 = Web3()
def get_storage_at(self, address, position, block='latest'):
"""Retrieve storage value from contract"""
payload = {
"jsonrpc": "2.0",
"method": "eth_getStorageAt",
"params": [to_checksum_address(address), position, block],
"id": 1
}
response = requests.post(
self.api_url,
json=payload,
headers={'Content-Type': 'application/json'}
)
result = response.json()
if 'error' in result:
raise Exception(f"RPC Error: {result['error']}")
return result['result']
def get_mapping_storage(self, contract_addr, mapping_slot, key):
"""Calculate and retrieve mapping storage value"""
# Convert key to bytes32
if isinstance(key, str) and key.startswith('0x'):
key_bytes = bytes.fromhex(key[2:].zfill(64))
else:
key_bytes = key.to_bytes(32, 'big')
# Convert slot to bytes32
slot_bytes = mapping_slot.to_bytes(32, 'big')
# Calculate storage position
position = self.w3.keccak(key_bytes + slot_bytes).hex()
return self.get_storage_at(contract_addr, position)
def parse_storage_value(self, hex_value, data_type):
"""Parse storage value based on type"""
if data_type == 'uint256':
return int(hex_value, 16)
elif data_type == 'address':
return to_checksum_address('0x' + hex_value[-40:])
elif data_type == 'bool':
return hex_value[-1] == '1'
elif data_type == 'bytes32':
return hex_value
else:
return hex_value
# Example usage with Polygon contracts
inspector = PolygonStorageInspector('YOUR_API_KEY')
# Analyze USDC contract on Polygon
usdc_contract = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174'
# Read contract metadata
name_storage = inspector.get_storage_at(usdc_contract, '0x3')
symbol_storage = inspector.get_storage_at(usdc_contract, '0x4')
decimals_storage = inspector.get_storage_at(usdc_contract, '0x5')
total_supply = inspector.get_storage_at(usdc_contract, '0x2')
print(f"Total Supply: {inspector.parse_storage_value(total_supply, 'uint256')}")
# Check balance for specific address
user_addr = '0x1234567890123456789012345678901234567890'
balance_hex = inspector.get_mapping_storage(usdc_contract, 0, user_addr)
balance = inspector.parse_storage_value(balance_hex, 'uint256')
print(f"User Balance: {balance / 10**6} USDC") # USDC has 6 decimals
# Read allowance (nested mapping at slot 1)
# allowances[owner][spender] = amount
owner = '0x1234567890123456789012345678901234567890'
spender = '0x0987654321098765432109876543210987654321'
# First hash: keccak256(owner + slot1)
owner_slot = inspector.w3.keccak(
bytes.fromhex(owner[2:].zfill(64) + '1'.zfill(64))
).hex()
# Second hash: keccak256(spender + owner_slot)
allowance_hex = inspector.get_mapping_storage(usdc_contract, int(owner_slot, 16), spender)
allowance = inspector.parse_storage_value(allowance_hex, 'uint256')
print(f"Allowance: {allowance / 10**6} USDC")
Response Example​
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x000000000000000000000000000000000000000000000000000009184e72a000"
}
Practical Applications​
- DeFi Analysis: Inspect liquidity pool reserves, user positions, and protocol parameters
- Token Auditing: Verify token supply, distribution, and holder balances
- Proxy Detection: Identify implementation contracts behind proxy patterns
- Gaming/NFT: Access metadata, ownership records, and game state
- Governance: Read voting weights, proposal data, and delegation mappings
Storage Layout Examples​
ERC-20 Token Storage​
// Typical ERC-20 storage layout
const slots = {
totalSupply: '0x2', // slot 2
balances: 0, // mapping at slot 0
allowances: 1, // nested mapping at slot 1
name: '0x3', // slot 3
symbol: '0x4', // slot 4
decimals: '0x5' // slot 5
};
Complex Mapping Calculations​
// balances[user] - simple mapping
const balanceSlot = keccak256(userAddress.padStart(64, '0') + '00'.repeat(31) + '00');
// allowances[owner][spender] - nested mapping
const ownerSlot = keccak256(ownerAddress.padStart(64, '0') + '00'.repeat(31) + '01');
const allowanceSlot = keccak256(spenderAddress.padStart(64, '0') + ownerSlot.slice(2));
Dynamic Array Access​
// Array length at slot 6
const arrayLength = await getStorageAt(contract, '0x6');
// First element at keccak256(0x6)
const firstElementSlot = keccak256('0x' + '6'.padStart(64, '0'));
// Subsequent elements: firstElementSlot + index
const secondElementSlot = '0x' + (BigInt(firstElementSlot) + 1n).toString(16);
Performance Tips​
- Use historical block numbers for time-series analysis
- Batch multiple storage reads for efficiency
- Cache frequently accessed storage positions
- Consider storage packing when reading adjacent slots
- Validate storage layout against contract source code
Need help? Contact our support team or check the Polygon documentation.