Type Definitions - Protocol State Structure
Overview
These type definitions provide the structure for managing state across all components of the Uniswap V2 processor, extending the base protocol state with specific pool data.
Core Type Definition
import { PoolConfig } from '../model';
import { ActiveBalance, ProtocolState } from '@absinthe/common';
import { PoolProcessState } from '../model';
import { PoolState } from '../model';
interface ProtocolStateUniv2 extends ProtocolState {
config: PoolConfig;
state: PoolState;
processState: PoolProcessState;
activeBalances: Map<string, ActiveBalance>;
}
export { ProtocolStateUniv2 };
State Structure Breakdown
Base ProtocolState (from @absinthe/common)
The base interface provides common fields for all protocol adapters:
interface ProtocolState {
balanceWindows: TimeWeightedBalanceWindow[]; // Queued balance events
transactions: TransactionEvent[]; // Queued transaction events
}
Extended ProtocolStateUniv2
Adds Uniswap V2-specific state management:
interface ProtocolStateUniv2 extends ProtocolState {
// Database entities for this pool
config: PoolConfig; // Token metadata and configuration
state: PoolState; // Current reserves and blockchain state
processState: PoolProcessState; // Processing timestamps and state
// In-memory processing data
activeBalances: Map<string, ActiveBalance>; // Current user LP balances
// Inherited from ProtocolState:
// balanceWindows: TimeWeightedBalanceWindow[] // Events to send to API
// transactions: TransactionEvent[] // Swap events to send to API
}
State Component Details
config: PoolConfig
Purpose: Stores pool and token metadata discovered from blockchain
PoolConfig {
id: string; // e.g., "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc-config"
lpToken: Token; // LP token metadata (address, decimals)
token0: Token; // First underlying token (address, decimals, coingeckoId)
token1: Token; // Second underlying token (address, decimals, coingeckoId)
}
Lifecycle:
- Initialized once when pool first encountered
- Loaded from database on processor restart
- Immutable after creation
state: PoolState
Purpose: Current blockchain state of the pool
PoolState {
id: string; // e.g., "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc-state"
pool: PoolConfig; // Reference to pool config
reserve0: bigint; // Current token0 reserves
reserve1: bigint; // Current token1 reserves
totalSupply: bigint; // Current LP token supply
lastBlock: number; // Last processed block
lastTsMs: bigint; // Last processed timestamp
updatedAt: Date; // Database update timestamp
}
Lifecycle:
- Updated every time
computeLpTokenPrice()
is called - Synchronized with blockchain via
updatePoolStateFromOnChain()
- Persisted to database after each batch
processState: PoolProcessState
Purpose: Tracks timing for periodic operations
PoolProcessState {
id: string; // e.g., "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc-process-state"
pool: PoolConfig; // Reference to pool config
lastInterpolatedTs?: bigint; // Last time balance flush was performed
}
Usage:
- Controls when to create
EXHAUSTED
balance windows - Ensures periodic balance snapshots happen on time boundaries
- Prevents duplicate time windows across restarts
activeBalances: Map<string, ActiveBalance>
Purpose: In-memory tracking of current user LP balances
Map<userAddress, ActiveBalance> where:
ActiveBalance {
balance: bigint; // Current LP token balance (raw amount)
updatedBlockTs: number; // Timestamp when balance last changed
updatedBlockHeight: number; // Block height when balance last changed
}
Example:
activeBalances = new Map([
[
'0x742d35Cc1F54834567...',
{
balance: 1500000000000000000n, // 1.5 LP tokens (18 decimals)
updatedBlockTs: 1640995200, // Jan 1, 2022 12:00:00
updatedBlockHeight: 13916166, // Block when balance changed
},
],
[
'0x8ba1f109551bD432...',
{
balance: 50000000000000000000n, // 50 LP tokens
updatedBlockTs: 1640998800, // Jan 1, 2022 13:00:00
updatedBlockHeight: 13916200, // Different block
},
],
]);
Lifecycle:
- Updated on every Transfer event (mint/burn/transfer)
- Used to calculate time-weighted balances
- Persisted as JSON in database during finalization
- Loaded from database on processor restart
State Usage Patterns
During Initialization
const protocolState: ProtocolStateUniv2 = {
config: poolConfig || new PoolConfig({}),
state: poolState || new PoolState({}),
processState: poolProcessState || new PoolProcessState({}),
activeBalances: activeBalances || new Map<string, ActiveBalance>(),
balanceWindows: [], // Empty array for new events
transactions: [], // Empty array for new events
};
During Event Processing
// Swap event: Add to transactions array
protocolState.transactions.push(swapTransaction);
// Transfer event: Update activeBalances + add to balanceWindows
const newWindows = processValueChange({...});
protocolState.balanceWindows.push(...newWindows);
During Periodic Flush
// Check timing using processState
const needsFlush = currentTime > protocolState.processState.lastInterpolatedTs + windowDuration;
if (needsFlush) {
// Create EXHAUSTED windows for all active users
for (const [userAddress, balance] of protocolState.activeBalances) {
protocolState.balanceWindows.push({
userAddress,
trigger: TimeWindowTrigger.EXHAUSTED,
// ... other fields
});
}
}
During Finalization
// Transform accumulated events for API
const balances = toTimeWeightedBalance(protocolState.balanceWindows, ...);
const transactions = toTransaction(protocolState.transactions, ...);
// Send to API
await apiClient.send(balances);
await apiClient.send(transactions);
// Persist state to database
await ctx.store.upsert(protocolState.config);
await ctx.store.upsert(protocolState.state);
await ctx.store.upsert(protocolState.processState);
Type Safety Benefits
- Compile-time validation: TypeScript ensures all required fields are present
- IDE support: Auto-completion and refactoring across the codebase
- Clear interfaces: Documents expected structure for each state component
- Extension pattern: Easy to add new fields while maintaining compatibility
This type structure provides a clean, organized way to manage the complex state required for accurate time-weighted balance tracking and event processing in Uniswap V2 pools.