Track storage state changes for smart contracts to monitor specific storage slots, analyze contract state evolution, or track ownership changes.
Use Case
State diff tracking enables you to:
Monitor specific storage slots (e.g., owner, balance mappings)
Track contract state evolution over time
Analyze state changes without replaying transactions
Build state-based analytics
Code Example
curl --compressed -X POST 'https://portal.sqd.dev/datasets/ethereum-mainnet/stream' \
-H 'Content-Type: application/json' \
-d '{
"type": "evm",
"fromBlock": 19000000,
"toBlock": 19000003,
"fields": {
"block": {
"number": true,
"timestamp": true
},
"stateDiff": {
"address": true,
"key": true,
"kind": true,
"prev": true,
"next": true
}
},
"stateDiffs": [{
"address": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"]
}]
}'
Try it yourself with the interactive query interface below:
Key Parameters
Parameter Description addressContract address to track state changes keyStorage slot key (keccak256 hash for mappings) kindChange type: = (unchanged), + (added), * (modified), - (removed) prevPrevious value (null for new slots) nextNew value (null for removed slots)
State diffs capture all storage changes that occurred in a block, providing a complete picture of contract state evolution.
Expected Output
{
"header" : {
"number" : 18000123
},
"stateDiffs" : [
{
"address" : "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ,
"key" : "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" ,
"kind" : "*" ,
"prev" : "0x0000000000000000000000000000000000000000000000000000000000000064" ,
"next" : "0x00000000000000000000000000000000000000000000000000000000000000c8"
}
]
}
Filter by Contract
Track state changes for a specific contract:
curl --compressed -X POST 'https://portal.sqd.dev/datasets/ethereum-mainnet/stream' \
-H 'Content-Type: application/json' \
-d '{
"type": "evm",
"fromBlock": 19000000,
"toBlock": 19000003,
"fields": {
"stateDiff": {
"address": true,
"key": true,
"kind": true,
"prev": true,
"next": true
}
},
"stateDiffs": [{
"address": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"]
}]
}'
Understanding Change Types
The kind field indicates the type of state change:
Kind Meaning Description =Unchanged Storage slot was accessed but not modified +Added New storage slot created *Modified Existing storage slot value changed -Removed Storage slot deleted (SSTORE with zero value)
// Filter only modifications
for ( const block of blocks ) {
if ( block . stateDiffs ) {
for ( const diff of block . stateDiffs ) {
if ( diff . kind === "*" ) {
console . log ( `Modified at block ${ block . header . number } ` );
console . log ( ` ${ diff . prev } → ${ diff . next } ` );
}
}
}
}
Track Specific Storage Slots
Monitor a specific storage slot (e.g., owner variable):
// Example: Track owner changes
// Owner is typically at slot 0 for many contracts
const OWNER_SLOT = "0x0000000000000000000000000000000000000000000000000000000000000000" ;
const blocks = await dataSource . getBlocks ({
from: 19000000 ,
to: 19000003 ,
fields: {
block: { number: true , timestamp: true },
stateDiff: {
address: true ,
key: true ,
prev: true ,
next: true ,
},
},
stateDiffs: [
{
address: [ "0xYourContractAddress" ],
},
],
});
for ( const block of blocks ) {
if ( block . stateDiffs ) {
for ( const diff of block . stateDiffs ) {
if ( diff . key === OWNER_SLOT ) {
console . log ({
block: block . header . number ,
timestamp: block . header . timestamp ,
previousOwner: diff . prev ,
newOwner: diff . next ,
});
}
}
}
}
For mappings, the storage key is computed as keccak256(abi.encode(mappingKey, slot)). You’ll need to compute this off-chain to track specific mapping entries.
Monitor Balance Changes
Track balance mapping changes for ERC-20 tokens:
import { ethers } from "ethers" ;
// Compute storage key for balance mapping
function getBalanceSlot ( address : string , balanceMappingSlot : number ) : string {
// For Solidity: mapping(address => uint256) balances;
// Key = keccak256(abi.encode(address, slot))
return ethers . keccak256 (
ethers . AbiCoder . defaultAbiCoder (). encode (
[ "address" , "uint256" ],
[ address , balanceMappingSlot ]
)
);
}
const WALLET = "0x28C6c06298d514Db089934071355E5743bf21d60" ;
const BALANCE_SLOT = 1 ; // Balance mapping slot for USDC
const balanceKey = getBalanceSlot ( WALLET , BALANCE_SLOT );
// Query state diffs and filter for this specific key
const blocks = await dataSource . getBlocks ({
from: 19000000 ,
to: 19000003 ,
fields: {
stateDiff: { address: true , key: true , prev: true , next: true },
},
stateDiffs: [
{
address: [ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" ], // USDC
},
],
});
for ( const block of blocks ) {
if ( block . stateDiffs ) {
for ( const diff of block . stateDiffs ) {
if ( diff . key === balanceKey ) {
const prevBalance = BigInt ( diff . prev || "0" );
const nextBalance = BigInt ( diff . next || "0" );
console . log ( `Balance change: ${ prevBalance } → ${ nextBalance } ` );
}
}
}
}
Filter by contract : Always specify contract addresses to reduce data volume
Post-filter keys : Filter specific storage keys in your application
Understand data volume : State diffs can be very large for active contracts
Combine with logs : Use event logs to identify interesting blocks, then query state diffs
State diffs can generate massive amounts of data, especially for busy contracts. Always filter by specific contract addresses.
Common Use Cases
Track Governance Changes
Monitor governance parameter changes:
// Filter state changes for governance contracts
// Look for proposal state changes, quorum updates, etc.
Monitor Oracle Updates
Track price oracle state updates:
// Track specific storage slots for price data
// Useful for MEV analysis or price feed monitoring
Audit Storage Access
Analyze which storage slots are frequently accessed:
const slotAccessCounts = new Map < string , number >();
for ( const block of blocks ) {
if ( block . stateDiffs ) {
for ( const diff of block . stateDiffs ) {
const key = ` ${ diff . address } : ${ diff . key } ` ;
slotAccessCounts . set ( key , ( slotAccessCounts . get ( key ) || 0 ) + 1 );
}
}
}
Query Event Logs Track smart contract events
Query Transactions Monitor transaction activity
Query Traces Analyze internal transactions
API Reference View complete API docs