Docs
Supported ChainsZetachainJSON-RPC APIAccount Methods

eth_getStorageAt - Zetachain RPC Method

Read the value from a storage slot at a given contract address on Zetachain. 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 Zetachain. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

Why Zetachain? Build on the universal omnichain blockchain enabling cross-chain smart contracts across 50+ chains including Bitcoin with native Bitcoin support, 50+ chain interoperability via UNISON, no bridging required, and partnerships with Curve and SushiSwap.

When to Use This Method

eth_getStorageAt is essential for cross-chain dApp developers, Bitcoin DeFi builders, and teams requiring native multi-chain interoperability:

  • Reading Private State — Access contract state variables that have no public getter, including variables marked as private or internal in Solidity
  • Proxy Implementation Verification — Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on omnichain DeFi, native Bitcoin smart contracts, cross-chain asset management, and unified liquidity aggregation
  • 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

Request
addressDATA (20 bytes)

The address of the contract to read storage from

positionQUANTITY

Hex-encoded storage slot position (e.g., 0x0 for the first slot)

blockParameterQUANTITY|TAG

Block number as hex, or tag: latest, earliest, pending

Response Body

Response
resultDATA (32 bytes)

The 32-byte value stored at the requested position, zero-padded on the left

Error Responses

Errors
Error Response-32602

Code Examples

Bash
curl -X POST https://api-zetachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0x5F0b1a82749cb4E2278EC87F8BF6B618dC71a8bf",
      "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 Zetachain:

JavaScript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-zetachain-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:

JavaScript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-zetachain-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 = '0x5F0b1a82749cb4E2278EC87F8BF6B618dC71a8bf';
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:

JavaScript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Zetachain:`);
  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 CodeDescriptionSolution
-32602Invalid paramsVerify the address is a valid 20-byte hex string, position is a valid hex quantity, and block parameter is valid
-32603Internal errorRetry with exponential backoff
-32000Execution errorThe requested block may be pruned — try latest or a more recent block
-32005Rate limit exceededImplement caching and reduce request frequency
JavaScript
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));
    }
  }
}