A Ponder indexer for tracking Olympus Protocol's Convertible Deposits system. This indexer replaces the original Envio implementation and provides comprehensive indexing of events from three main contracts with real-time snapshot tracking.
The Olympus Convertible Deposits system allows users to deposit various assets (starting with USDS) for fixed periods and receive OHM tokens through an auction mechanism. The system includes:
- ConvertibleDepositAuctioneer: Manages auctions for converting deposits to OHM
- ConvertibleDepositFacility: Handles deposit creation, conversion, and yield management
- DepositRedemptionVault: Manages redemption of deposits with loan mechanisms
- Track deposit positions across different assets and time periods
- Monitor deposit creation, conversion, and remaining balances
- Support for multiple deposit periods (e.g., 3, 6, 12 months)
- Receipt token tracking for each position
- Auction mechanism for converting deposits to OHM
- Real-time auction parameter tracking (target, tick size, minimum price)
- Bid tracking and auction result monitoring
- Deposit period enable/disable management
- Automatic snapshot updates on bids and auction results
- Early redemption system with loan mechanisms
- Interest rate tracking and loan management
- Yield claiming and asset commitment tracking
- Reclaim rate configuration per asset period
- Redemption lifecycle tracking with loan details
- Real-time snapshots of auctioneer and facility states
- Historical tracking of all events and state changes
- Multi-asset support with proper decimal normalization
- Cross-contract relationship mapping
- Periodic snapshot creation via block handler (every 3000 blocks)
See SCHEMA.md for a detailed visualization of the schema.
- Asset: Supported deposit assets (USDS, etc.)
- DepositAsset: Asset configuration within the system
- DepositAssetPeriod: Time-based deposit periods (3, 6, 12 months)
- ConvertibleDepositPosition: Individual user deposit positions
- Auctioneer: Auction management per asset
- AuctioneerDepositPeriod: Period configuration for auctioneers
- DepositFacility: Main deposit management contract
- DepositFacilityAsset: Asset management within facilities
- DepositFacilityAssetPeriod: Period configuration for facility assets
- DepositRedemptionVault: Redemption and loan system
- DepositRedemptionVaultAssetConfiguration: Asset-specific redemption configuration
- Redemption: Redemption lifecycle tracking
- RedemptionLoan: Loan details for redemptions
- ReceiptToken: Receipt token tracking
- Depositor: User addresses who have created deposits
The indexer tracks 37 event types across three contracts:
Auctioneer Events (11):
Enabled,DisabledTickStepUpdated,AuctionTrackingPeriodUpdatedDepositPeriodEnableQueued,DepositPeriodEnabledDepositPeriodDisableQueued,DepositPeriodDisabledBid,AuctionParametersUpdated,AuctionResult
Facility Events (11):
Enabled,DisabledOperatorAuthorized,OperatorDeauthorizedAssetCommitted,AssetCommitCancelled,AssetCommitWithdrawnAssetPeriodReclaimRateSetCreatedDeposit,ReclaimedConvertedDeposits,ConvertedDepositClaimedYield
Redemption Vault Events (12):
Enabled,DisabledClaimDefaultRewardPercentageSetFacilityAuthorized,FacilityDeauthorizedAnnualInterestRateSet,MaxBorrowPercentageSetRedemptionStarted,RedemptionFinished,RedemptionCancelledLoanCreated,LoanRepaid,LoanDefaulted,LoanExtended
The indexer maintains periodic snapshots of contract state:
- AuctioneerSnapshot: Auctioneer state (day state, auction parameters)
- AuctioneerDepositPeriodSnapshot: Tick data per deposit period
- DepositFacilityAssetSnapshot: Asset-specific metrics (total deposits, pending redemptions, borrowed amounts, claimable yield)
Snapshots are created:
- Periodically via block handler (every 3000 blocks ≈ 1 hour)
- On-demand after state-changing events (bids, auction results, deposits, redemptions, etc.)
- Node.js v18.14+
- pnpm v8+
- PostgreSQL (optional, for local development)
# Navigate to the ponder-indexer directory
cd ponder-indexer
# Install dependencies
pnpm install
# Set up environment variables
cp .env.example .env
# Edit .env and set PONDER_RPC_URL_11155111 for Sepolia# Start the development server
pnpm devThe indexer will:
- Start indexing from block 9180152
- Create snapshots every 3000 blocks
- Provide GraphQL API at http://localhost:42069
- Provide REST API at http://localhost:42069/api
# Check TypeScript compilation
pnpm typecheck
# Run linting and formatting checks
pnpm lint:check
# Run linting with auto-fix
pnpm lintCurrently configured for:
- Sepolia Testnet (Chain ID: 11155111)
- ConvertibleDepositAuctioneer:
0xc14156AF3bF6c11b1c40C8f51f64bA5496870126 - ConvertibleDepositFacility:
0x87568265eb6Ea27f37613d242D4192B6f6771269 - DepositRedemptionVault:
0x69b2Be653BAB628116b360818BE75a2d97b45C4a
- Snapshot: Every 3000 blocks (≈ 1 hour at 12s block time)
- Starting from block 9180152
Ponder automatically generates a GraphQL API based on the schema. Example queries:
# Query deposit positions
query GetPositions($depositor: String!) {
convertibleDepositPositions(
where: { rDepositor: { address: { equals: $depositor } } }
) {
positionId
initialAmount
remainingAmount
conversionPrice
rAssetPeriod {
depositPeriod
rDepositAsset {
asset
rAsset {
symbol
}
}
}
}
}
# Query auction snapshots
query GetAuctionSnapshots($auctioneer: String!, $limit: Int = 10) {
auctioneerSnapshots(
where: { rAuctioneer: { address: { equals: $auctioneer } } }
orderBy: timestamp
orderDirection: desc
limit: $limit
) {
block
timestamp
ohmSold
isAuctionActive
depositPeriodSnapshots {
depositPeriod
currentTickPrice
currentTickCapacity
}
}
}
# Query redemptions with loans
query GetRedemptions($depositor: String!) {
redemptions(where: { rDepositor: { address: { equals: $depositor } } }) {
redemptionId
amount
redeemableAt
loans {
principal
interest
status
}
}
}All entities use composite primary keys:
- Address-based:
(chainId, address)for contracts, assets, depositors - Asset-based:
(chainId, asset)for DepositAsset - Period-based:
(chainId, depositAsset, depositPeriod)for periods - Position-based:
(chainId, positionId)for positions - Event-based:
(chainId, block, logIndex)for events - Snapshot-based:
(chainId, block, auctioneer)or(chainId, block, facility, depositAsset)for snapshots
All relation names use the r prefix (e.g., rAsset, rDepositAsset, rFacility, rAssetPeriod). This convention is necessary because:
-
Avoiding Naming Conflicts: Database tables have columns with names like
asset,depositAsset,facility, etc. If relations had the same names, Ponder's type system and query builder would have ambiguity issues when accessing these properties. -
Type Safety: The
rprefix clearly distinguishes between:- Database fields:
depositAsset.asset(the address string) - Relations:
depositAsset.rAsset(the related Asset entity with decimals, symbol, etc.)
- Database fields:
-
Field Length Limits: Some database systems or ORMs have field length limitations, and the short
rprefix keeps relation names concise while remaining clear. -
Consistency: All relations follow the same pattern, making the codebase easier to understand and maintain.
Example usage:
// Access the asset address (database field)
const assetAddress = depositAsset.asset;
// Access the full Asset entity with decimals (relation)
const decimals = depositAsset.rAsset.decimals;- Addresses: Stored as
hextype (lowercase) - BigInt: Stored as
biginttype (not text) - BigDecimal: Stored as
texttype (decimal normalization handled in application layer) - Timestamps: Stored as
biginttype
- Batch contract calls using
client.multicall()where possible - Fetch decimals inline using
withparameters to avoid multiple DB calls - Cache asset decimals to reduce redundant queries
- Fail loudly: All errors throw instead of being logged and skipped
- Position lookups revert if position not found (no silent failures)
- Snapshot creation fails if any period snapshot fails
ponder-indexer/
├── abis/ # Contract ABIs (TypeScript format)
│ ├── ConvertibleDepositAuctioneer.ts
│ ├── ConvertibleDepositFacility.ts
│ ├── DepositRedemptionVault.ts
│ ├── DepositManager.ts
│ └── DepositPositionManager.ts
├── src/
│ ├── contracts/ # Contract interaction utilities
│ │ ├── asset.ts
│ │ ├── auctioneer.ts
│ │ ├── depositFacility.ts
│ │ ├── position.ts
│ │ └── redemptionVault.ts
│ ├── entities/ # Entity creation/management
│ │ ├── asset.ts
│ │ ├── auctioneer.ts
│ │ ├── depositFacility.ts
│ │ ├── depositor.ts
│ │ ├── position.ts
│ │ ├── receiptToken.ts
│ │ ├── redemption.ts
│ │ ├── redemptionLoan.ts
│ │ ├── redemptionVault.ts
│ │ └── snapshot.ts
│ ├── handlers/ # Event handlers
│ │ ├── auctioneer.handlers.ts
│ │ ├── block.handlers.ts # Block-based snapshot creation
│ │ ├── depositFacility.handlers.ts
│ │ └── redemptionVault.handlers.ts
│ ├── utils/ # Utility functions
│ │ ├── decimal.ts # Decimal normalization
│ │ ├── ids.ts # ID generation helpers
│ │ └── env.ts # Environment variable helpers
│ ├── api/ # Custom API routes (if needed)
│ │ └── index.ts
│ ├── types.ts # Shared TypeScript types
│ └── index.ts # Main entry point (imports all handlers)
├── ponder.config.ts # Ponder configuration
├── ponder.schema.ts # Schema definition
├── SCHEMA.md # Schema visualization and documentation
└── package.json
# Regenerate GraphQL schema
pnpm codegen# Check for type errors
pnpm typecheck# Check linting without fixing
pnpm lint:check
# Auto-fix linting issues
pnpm lintThis Ponder indexer is a complete migration from the original Envio implementation. Key differences:
- TypeScript Schema: Uses Ponder's TypeScript-based schema instead of GraphQL
- Direct Contract Calls: Uses
client.readContract()andclient.multicall()instead of Envio's Effect API - Composite Primary Keys: Uses Ponder's
primaryKey()helper for composite keys - Simplified Snapshots: Removed
latestSnapshotentity - query by timestamp instead - Block Handlers: Uses Ponder's block interval system for periodic snapshots
- Relations: Explicitly defined using Ponder's
relations()helper
Snapshots are created:
-
Periodically: Every 3000 blocks (≈1 hour) via block handler
- Creates snapshots for all enabled auctioneers
- Creates snapshots for all facility assets
-
On State Changes:
- After each bid → updates auctioneer and period snapshots
- After auction results → updates auctioneer snapshots
- After deposits → updates facility asset snapshots
- After redemptions → updates facility asset snapshots (pending redemption, borrowed amount)
- After conversions → updates facility asset snapshots (total deposited)
The refreshAuctionState() function creates:
- Main
auctioneerSnapshotwith day state and auction parameters auctioneerDepositPeriodSnapshotfor all deposit periods (enabled or not)
- Batched Contract Calls: Multiple reads combined into single
multicall()requests - Decimal Caching: Asset decimals fetched once and reused
- Efficient Queries: Uses
context.db.sql.select()for complex queries instead of iterative lookups - Snapshot Updates: Direct updates to existing snapshots instead of creating new ones
This project is part of the Olympus Protocol ecosystem.