- Project Overview
- Architecture & Design
- Supported Protocols
- Core Components
- Data Flow & Processing
- Configuration & Setup
- API Integration
- Development Guide
- Contributing 10.Troubleshooting
Absinthe Adapter API is a comprehensive blockchain indexing platform built with the Subsquid framework that provides real-time tracking and analysis of multiple DeFi protocols across various blockchain networks. The platform specializes in tracking liquidity positions, token swaps, staking activities, and bonding curve interactions with sophisticated time-weighted balance calculations.
- Multi-Protocol Support: Indexes 10+ different DeFi protocols including DEXs, staking platforms, and bonding curves
- Cross-Chain Compatibility: Supports 4 major blockchain networks (Ethereum, Polygon, Base, Hemi)
- Real-Time Processing: Processes blockchain events in real-time with configurable batch processing
- Time-Weighted Analytics: Calculates time-weighted balances for accurate position tracking
- Price Integration: Integrates with CoinGecko API for real-time price data
- Scalable Architecture: Built with TypeScript and Subsquid for high-performance indexing
- LP Token Positions: Time-weighted balance tracking for liquidity providers
- Swap Events: Real-time monitoring of token swaps across all supported protocols
- Staking Activities: Deposit/withdrawal events and balance changes in staking protocols
- Bonding Curve Interactions: Token trades and liquidity deployments in bonding curve protocols
- Position Management: NFT position tracking for Uniswap V3 and similar protocols
- Price Calculations: USD value calculations for all tracked assets
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Blockchain β β Subsquid β β Absinthe β
β Networks βββββΆβ Processor βββββΆβ API β
β β β β β β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β PostgreSQL β
β OR
Redis β
βββββββββββββββββββ
- Modular Architecture: Each protocol has its own processor and data models
- Type Safety: Full TypeScript implementation with strict type checking
- Configuration-Driven: Protocol support configured via JSON files
- Event-Driven Processing: Real-time event processing with batch optimization
- Fault Tolerance: Robust error handling and retry mechanisms
- Framework: Subsquid (blockchain indexing framework)
- Language: TypeScript
- Database: PostgreSQL with TypeORM
- In-Memory-Store: Redis
- Package Manager: pnpm (monorepo structure)
- API Integration: RESTful API with rate limiting
- Price Data: CoinGecko API integration
- Containerization: Docker support
- Purpose: Track LP token transfers, swaps, and liquidity events
- Events Monitored:
Transfer,Sync,Swap - Features: Time-weighted balance tracking, price calculations
- Supported Chains: Ethereum, Base, Hemi, Polygon
- Purpose: Advanced position tracking with NFT positions
- Events Monitored:
Swap,IncreaseLiquidity,DecreaseLiquidity,Transfer - Features: Position management, fee tier tracking, concentrated liquidity
- Supported Chains: Ethereum, Hemi, Base, Polygon
- Purpose: Track staking deposits and withdrawals
- Events Monitored:
Deposit,Withdraw - Features: Time-weighted balance tracking, reward calculations
- Supported Chains: Hemi (43111)
- Purpose: Track bonding curve token trades
- Events Monitored:
CurveCreated,TokenTrade,LiquidityDeployed,Swap - Features: Curve parameter tracking, trade volume analysis
- Supported Chains: Base
- Purpose: Stablecoin minting protocol tracking
- Events Monitored: Minting events, price stabilization
- Supported Chains: Ethereum
- Purpose: Tracking userVerify function call
- Events Monitored: Custom demo events
- Supported Chains: Hemi
- Purpose: Voucher token system tracking
- Events Monitored: Voucher creation, redemption
- Supported Chains: Ethereum
- Purpose: Auction and bidding platform tracking
- Events Monitored:
Auction_BidPlaced, bidding events - Features: Auction tracking, bid analysis
- Supported Chains: Polygon, Base
Each protocol has its own processor that:
- Configures Event Listening: Sets up blockchain event subscriptions
- Handles Data Extraction: Decodes blockchain events into structured data
- Manages State: Maintains protocol-specific state and balances
- Processes Batches: Handles batch processing for efficiency
interface ProtocolState {
activeBalances: Map<string, Map<string, ActiveBalance>>;
balanceWindows: TimeWeightedBalanceEvent[];
transactions: TransactionEvent[];
}
interface ActiveBalance {
balance: bigint;
updatedBlockTs: number;
updatedBlockHeight: number;
}
interface TimeWeightedBalanceEvent {
userAddress: string;
deltaAmount: number;
trigger: TimeWindowTrigger;
startTs: number;
endTs: number;
windowDurationMs: number;
tokenPrice: number;
valueUsd: number;
}Each protocol extends the base models with protocol-specific fields:
- DEX Models: Pool states, swap events, liquidity positions
- Staking Models: Staking positions, reward tracking
- Txn Tracking Models: Curve parameters, trade history
- CoinGecko API: Primary price data source
- Rate Limiting: Bottleneck-based rate limiting
- Retry Logic: Exponential backoff for failed requests
- Batch Processing: Efficient batch data transmission
- Error Handling: Comprehensive error handling and logging
interface TimeWeightedBalanceEvent {
base: BaseEventFields;
eventType: MessageType;
tokenPrice: number;
tokenDecimals: number;
balanceBefore: string;
balanceAfter: string;
timeWindowTrigger: TimeWindowTrigger;
startUnixTimestampMs: number;
endUnixTimestampMs: number;
windowDurationMs: number;
startBlockNumber: number;
endBlockNumber: number;
txHash: string | null;
}interface TransactionEvent {
base: BaseEventFields;
eventType: MessageType;
rawAmount: string;
displayAmount: number;
unixTimestampMs: number;
txHash: string;
logIndex: number;
gasUsed: number;
gasFeeUsd: number;
blockNumber: number;
blockHash: string;
}Blockchain Event β Subsquid Processor β Event Decoder β State Manager β API Client
- Event Detection: Subsquid processor detects relevant blockchain events
- Event Decoding: Raw event data is decoded using protocol-specific ABIs
- State Update: Protocol state is updated based on event data
- Balance Calculation: Time-weighted balances are calculated
- Price Integration: USD values are calculated using price feeds
- Data Transmission: Processed data is sent to Absinthe API
- Configurable Windows: Balance snapshots taken at configurable intervals
- Trigger Events: Balance updates triggered by transfers or time windows
- Accurate Tracking: Maintains historical balance records with timestamps
// For a user holding 1000 LP tokens for 24 hours
const balanceWindow = {
userAddress: '0x...',
deltaAmount: 0, // No change in balance
startTs: 1640995200, // Start of day
endTs: 1641081600, // End of day
windowDurationMs: 86400000, // 24 hours
balanceBefore: '1000',
balanceAfter: '1000',
valueUsd: 5000, // Calculated USD value
};- Batch Size Control: Configurable batch sizes for optimal performance
- Parallel Processing: Multiple protocols processed in parallel
- Memory Management: Efficient memory usage with streaming processing
- Error Recovery: Graceful handling of processing errors
# Blockchain RPC Endpoints
RPC_URL_MAINNET=https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY
RPC_URL_POLYGON=https://polygon-mainnet.g.alchemy.com/v2/YOUR-API-KEY
RPC_URL_ARBITRUM=https://arb-mainnet.g.alchemy.com/v2/YOUR-API-KEY
RPC_URL_BASE=https://base-mainnet.g.alchemy.com/v2/YOUR-API-KEY
RPC_URL_OPTIMISM=https://opt-mainnet.g.alchemy.com/v2/YOUR-API-KEY
RPC_URL_HEMI=https://hemi-mainnet.g.alchemy.com/v2/YOUR-API-KEY
# API Configuration
ABSINTHE_API_URL=https://adapters.absinthe.network
ABSINTHE_API_KEY=your-absinthe-api-key
ABS_CONFIG='{"balanceFlushIntervalHours":6,"dexProtocols":[{"type":"uniswap-v2","chainId":1,"toBlock":0,"protocols":[{"name":"pepe-weth","contractAddress":"0xa43fe16908251ee70ef74718545e4fe6c5ccec9f","fromBlock":17046833,"pricingStrategy":"coingecko","token0":{"coingeckoId":"pepe","decimals":18},"token1":{"coingeckoId":"weth","decimals":18},"preferredTokenCoingeckoId":"token1"}]},{"type":"izumi","chainId":42161,"toBlock":0,"protocols":[{"name":"weth-hemitbtc","contractAddress":"0xa43fe16908251ee70ef74718545e4fe6c5ccec9f","fromBlock":1276815,"pricingStrategy":"coingecko","token0":{"coingeckoId":"weth","decimals":18},"token1":{"coingeckoId":"btc","decimals":8},"preferredTokenCoingeckoId":"token1"},{"name":"vusd-weth","contractAddress":"0xa43fe16908251ee70ef74718545e4fe6c5ccec9f","fromBlock":1274620,"pricingStrategy":"coingecko","token0":{"coingeckoId":"vesper-vdollar","decimals":18},"token1":{"coingeckoId":"weth","decimals":18},"preferredTokenCoingeckoId":"token1"}]}],"txnTrackingProtocols":[{"type":"printr","name":"printr-base","contractAddress":"0xbdc9a5b600e9a10609b0613b860b660342a6d4c0","factoryAddress":"0x33128a8fc17869897dce68ed026d694621f6fdfd","chainId":8453,"toBlock":0,"fromBlock":30000000},{"type":"vusd-mint","name":"vusd-mint","contractAddress":"0xFd22Bcf90d63748288913336Cd38BBC0e681e298","chainId":1,"toBlock":0,"fromBlock":22017054},{"type":"demos","name":"demos","contractAddress":"0x70468f06cf32b776130e2da4c0d7dd08983282ec","chainId":43111,"toBlock":0,"fromBlock":1993447},{"type":"voucher","name":"voucher","contractAddress":"0xa26b04b41162b0d7c2e1e2f9a33b752e28304a49","chainId":1,"toBlock":0,"fromBlock":21557766}],"stakingProtocols":[{"type":"hemi","name":"hemi-staking","contractAddress":"0x4f5e928763cbfaf5ffd8907ebbb0dabd5f78ba83","chainId":43111,"toBlock":0,"fromBlock":2025621},{"type":"vusd-bridge","name":"vusd-bridge","contractAddress":"0x5eaa10F99e7e6D177eF9F74E519E319aa49f191e","chainId":1,"toBlock":0,"fromBlock":22695105}],"univ3Protocols":[{"type":"uniswap-v3","chainId":1,"factoryAddress":"0x1f98431c8ad98523631ae4a59f267346ea31f984","factoryDeployedAt":12369621,"positionsAddress":"0xc36442b4a4522e871399cd717abdd847ab11fe88","toBlock":0,"poolDiscovery":true,"trackPositions":true,"trackSwaps":true,"pools":[{"name":"pepe-weth-0.3","contractAddress":"0x11950d141ecb863f01007add7d1a342041227b58","fromBlock":13609065,"feeTier":3000,"pricingStrategy":"internal-twap","token0":{"symbol":"PEPE","decimals":18},"token1":{"symbol":"WETH","decimals":18},"preferredTokenCoingeckoId":"token1"},{"name":"wepe-weth-0.3","contractAddress":"0xa3c2076eb97d573cc8842f1db1ecdf7b6f77ba27","fromBlock":12376729,"feeTier":3000,"pricingStrategy":"internal-twap","token0":{"symbol":"WEPE","decimals":18},"token1":{"symbol":"WETH","decimals":18},"preferredTokenCoingeckoId":"token1"},{"name":"usdc-weth-0.3","contractAddress":"0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640","fromBlock":1620250931,"feeTier":3000,"pricingStrategy":"internal-twap","token0":{"symbol":"USDC","decimals":6},"token1":{"symbol":"WETH","decimals":18},"preferredTokenCoingeckoId":"token1"}]}],"zebuProtocols":[{"type":"zebu","name":"zebu-new","toBlock":0,"clients":[{"name":"xyz-1","contractAddress":"0xD71954165a026924cA771C53164FB0a781c54C83","chainId":137,"fromBlock":61059459},{"name":"xyz-2","contractAddress":"0x3e4768dB375094b753929B7A540121d970fcb24e","chainId":137,"fromBlock":61059459},{"name":"xyz-3","contractAddress":"0x5859Ff44A3BDCD00c7047E68B94e93d34aF0fd71","chainId":8453,"fromBlock":15286409},{"name":"xyz-4","contractAddress":"0xE3EB2347bAE4E2C6905D7B832847E7848Ff6938c","chainId":137,"fromBlock":61695150},{"name":"xyz-5","contractAddress":"0x19633c8006236f6c016a34B9ca48e98AD10418B4","chainId":137,"fromBlock":64199277},{"name":"xyz-6","contractAddress":"0x0c18F35EcfF53b7c587bD754fc070b683cB9063B","chainId":8453,"fromBlock":20328800},{"name":"xyz-7","contractAddress":"0xDD4d9ae148b7c821b8157828806c78BD0FeCE8C4","chainId":137,"fromBlock":73490308}]},{"type":"zebu","name":"zebu-legacy","toBlock":0,"clients":[{"name":"xyz-1","contractAddress":"0xd7829F0EFC16086a91Cf211CFbb0E4Ef29D16BEE","chainId":8453,"fromBlock":27296063}]}]}'
# Price Data
COINGECKO_API_KEY=your-coingecko-api-key
# Database Configuration
DB_URL=postgresql://username:password@localhost:5432/database
REDIS_URL="redis://localhost:6379"
| Variable | Purpose | How to Obtain | | ----------------- | ----------------------- | --------------------------------------- | ------------ | | RPCURL* | Blockchain connectivity | Alchemy, Infura, or other RPC providers | | ABSINTHEAPI* | Data transmission | Contact Absinthe team | | COINGECKO_API_KEY | Price data | Sign up at coingecko.com/en/api | | DB_URL/REDIS_URL | Database connection | PostgreSQL setup (local or cloud) | Redis Server | | ABS_CONFIG | env config | Make sure to paste it as a string |
{
"balanceFlushIntervalHours": 6,
"dexProtocols": [...],
"txnTrackingProtocols": [...],
"stakingProtocols": [...],
"univ3Protocols": [...],
"zebuProtocols": [...]
}Uniswap V2 Configuration
{
"type": "uniswap-v2",
"chainId": 1,
"toBlock": 0,
"protocols": [
{
"name": "pepe-weth",
"contractAddress": "0xa43fe16908251ee70ef74718545e4fe6c5ccec9f",
"fromBlock": 17046833,
"pricingStrategy": "coingecko",
"token0": {
"coingeckoId": "pepe",
"decimals": 18
},
"token1": {
"coingeckoId": "weth",
"decimals": 18
},
"preferredTokenCoingeckoId": "token1"
}
]
}Staking Protocol Configuration
{
"type": "hemi",
"name": "hemi-staking",
"contractAddress": "0x4f5e928763cbfaf5ffd8907ebbb0dabd5f78ba83",
"chainId": 43111,
"toBlock": 0,
"fromBlock": 2025621
}- Node.js (v20+)
- pnpm package manager
- PostgreSQL database
- API keys for required services
- Clone and Install
git clone https://github.com/AbsintheLabs/absinthe-adapters.git
cd absinthe-adapters
pnpm install- Environment Setup
cp .env.example .env
# Edit .env with your configuration- Configuration Setup
ABS_CONFIG='{"balanceFlushIntervalHours":6,"dexProtocols":[{"type":"uniswap-v2","chainId":1,"toBlock":0,"protocols":[{"name":"pepe-weth","contractAddress":"0xa43fe16908251ee70ef74718545e4fe6c5ccec9f","fromBlock":17046833,"pricingStrategy":"coingecko","token0":{"coingeckoId":"pepe","decimals":18},"token1":{"coingeckoId":"weth","decimals":18},"preferredTokenCoingeckoId":"token1"}]},{"type":"izumi","chainId":42161,"toBlock":0,"protocols":[{"name":"weth-hemitbtc","contractAddress":"0xa43fe16908251ee70ef74718545e4fe6c5ccec9f","fromBlock":1276815,"pricingStrategy":"coingecko","token0":{"coingeckoId":"weth","decimals":18},"token1":{"coingeckoId":"btc","decimals":8},"preferredTokenCoingeckoId":"token1"},{"name":"vusd-weth","contractAddress":"0xa43fe16908251ee70ef74718545e4fe6c5ccec9f","fromBlock":1274620,"pricingStrategy":"coingecko","token0":{"coingeckoId":"vesper-vdollar","decimals":18},"token1":{"coingeckoId":"weth","decimals":18},"preferredTokenCoingeckoId":"token1"}]}],"txnTrackingProtocols":[{"type":"printr","name":"printr-base","contractAddress":"0xbdc9a5b600e9a10609b0613b860b660342a6d4c0","factoryAddress":"0x33128a8fc17869897dce68ed026d694621f6fdfd","chainId":8453,"toBlock":0,"fromBlock":30000000},{"type":"vusd-mint","name":"vusd-mint","contractAddress":"0xFd22Bcf90d63748288913336Cd38BBC0e681e298","chainId":1,"toBlock":0,"fromBlock":22017054},{"type":"demos","name":"demos","contractAddress":"0x70468f06cf32b776130e2da4c0d7dd08983282ec","chainId":43111,"toBlock":0,"fromBlock":1993447},{"type":"voucher","name":"voucher","contractAddress":"0xa26b04b41162b0d7c2e1e2f9a33b752e28304a49","chainId":1,"toBlock":0,"fromBlock":21557766}],"stakingProtocols":[{"type":"hemi","name":"hemi-staking","contractAddress":"0x4f5e928763cbfaf5ffd8907ebbb0dabd5f78ba83","chainId":43111,"toBlock":0,"fromBlock":2025621},{"type":"vusd-bridge","name":"vusd-bridge","contractAddress":"0x5eaa10F99e7e6D177eF9F74E519E319aa49f191e","chainId":1,"toBlock":0,"fromBlock":22695105}],"univ3Protocols":[{"type":"uniswap-v3","chainId":1,"factoryAddress":"0x1f98431c8ad98523631ae4a59f267346ea31f984","factoryDeployedAt":12369621,"positionsAddress":"0xc36442b4a4522e871399cd717abdd847ab11fe88","toBlock":0,"poolDiscovery":true,"trackPositions":true,"trackSwaps":true,"pools":[{"name":"pepe-weth-0.3","contractAddress":"0x11950d141ecb863f01007add7d1a342041227b58","fromBlock":13609065,"feeTier":3000,"pricingStrategy":"internal-twap","token0":{"symbol":"PEPE","decimals":18},"token1":{"symbol":"WETH","decimals":18},"preferredTokenCoingeckoId":"token1"},{"name":"wepe-weth-0.3","contractAddress":"0xa3c2076eb97d573cc8842f1db1ecdf7b6f77ba27","fromBlock":12376729,"feeTier":3000,"pricingStrategy":"internal-twap","token0":{"symbol":"WEPE","decimals":18},"token1":{"symbol":"WETH","decimals":18},"preferredTokenCoingeckoId":"token1"},{"name":"usdc-weth-0.3","contractAddress":"0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640","fromBlock":1620250931,"feeTier":3000,"pricingStrategy":"internal-twap","token0":{"symbol":"USDC","decimals":6},"token1":{"symbol":"WETH","decimals":18},"preferredTokenCoingeckoId":"token1"}]}],"zebuProtocols":[{"type":"zebu","name":"zebu-new","toBlock":0,"clients":[{"name":"xyz-1","contractAddress":"0xD71954165a026924cA771C53164FB0a781c54C83","chainId":137,"fromBlock":61059459},{"name":"xyz-2","contractAddress":"0x3e4768dB375094b753929B7A540121d970fcb24e","chainId":137,"fromBlock":61059459},{"name":"xyz-3","contractAddress":"0x5859Ff44A3BDCD00c7047E68B94e93d34aF0fd71","chainId":8453,"fromBlock":15286409},{"name":"xyz-4","contractAddress":"0xE3EB2347bAE4E2C6905D7B832847E7848Ff6938c","chainId":137,"fromBlock":61695150},{"name":"xyz-5","contractAddress":"0x19633c8006236f6c016a34B9ca48e98AD10418B4","chainId":137,"fromBlock":64199277},{"name":"xyz-6","contractAddress":"0x0c18F35EcfF53b7c587bD754fc070b683cB9063B","chainId":8453,"fromBlock":20328800},{"name":"xyz-7","contractAddress":"0xDD4d9ae148b7c821b8157828806c78BD0FeCE8C4","chainId":137,"fromBlock":73490308}]},{"type":"zebu","name":"zebu-legacy","toBlock":0,"clients":[{"name":"xyz-1","contractAddress":"0xd7829F0EFC16086a91Cf211CFbb0E4Ef29D16BEE","chainId":8453,"fromBlock":27296063}]}]}'
you can edit it
- Database/Redis Setup
# For local development
docker-compose up -d postgres
# Or configure external database- Code Generation
cd projects/uniswapv2
pnpm typegen
pnpm codegen
pnpm migration- Run Development
pnpm devconst apiClient = new AbsintheApiClient({
baseUrl: env.baseConfig.absintheApiUrl,
apiKey: env.baseConfig.absintheApiKey,
minTime: 90, // Rate limiting
});// Send transaction events
await apiClient.send(transactions);
// Send time-weighted balance events
await apiClient.send(balances);- Rate Limiting: 90ms minimum between requests
- Queue Management: Automatic request queuing
- Retry Logic: Exponential backoff for failed requests
try {
await apiClient.send(data);
} catch (error) {
console.error('API transmission failed:', error);
// Automatic retry with exponential backoff
}- Node.js (v20+)
- pnpm package manager
- PostgreSQL database
- API keys for required services
First, copy the environment template and configure your settings:
cp .env.example .envEdit the .env file with your specific values. See the Environment Configuration section above for detailed instructions on obtaining each required variable.
The indexer uses a JSON configuration file to define which protocols and pools to track.
you would need to pass the json as a string in and you can dynamically change the things in this schema, which would be dynamically picked something like this please add
chainId: The blockchain network ID (1 for Ethereum mainnet)gatewayUrl: Subsquid gateway URL for your target networkbalanceFlushIntervalHours: How often to create balance snapshots (in hours)toBlock: Optional ending block number for indexing
For each Uniswap V2 pool you want to track:
type: Can be any dex type, for now we are using"uniswap-v2"name: Descriptive name for the poolcontractAddress: The pool's contract addressfromBlock: Block number when the pool was createdpricingStrategy: Price data source (currently"coingecko")token0andtoken1: Token configurations with:coingeckoId: CoinGecko API identifier for the tokendecimals: Number of decimal places for the token
preferredTokenCoingeckoId: Which token to use for pricing ("token0"or"token1")
{
"chainId": 1,
"gatewayUrl": "https://v2.archive.subsquid.io/network/ethereum-mainnet",
"balanceFlushIntervalHours": 24,
"toBlock": 14981079,
"protocols": [
{
"type": "uniswap-v2",
"name": "USDC/WETH Pool",
"contractAddress": "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc",
"fromBlock": 10008355,
"pricingStrategy": "coingecko",
"token0": {
"coingeckoId": "usd-coin",
"decimals": 6
},
"token1": {
"coingeckoId": "weth",
"decimals": 18
},
"preferredTokenCoingeckoId": "token0"
}
]
}abs_config.json first. If not found, it will fall back to abs_config.example.json. For production deployments, always use abs_config.json.
abs_config.json file is included in .gitignore to prevent accidentally committing sensitive configuration data.
pnpm installThe indexer automatically validates both your environment variables and configuration file on startup. If there are any issues, you'll see detailed error messages indicating what needs to be fixed.
Choose the appropriate adapter based on the protocol you want to index:
Hemi-Staking Protocol Adapters:
- Configure Environment Variables: Set up your RPC URLs, API keys, and database connection
- Customize Configuration: Modify the protocol configuration to match your specific pools/contracts
- Monitor Logs: Check the deployment logs to ensure proper indexing
- Verify Data: Confirm that events are being processed and sent to the Absinthe API
Each adapter is pre-configured with the appropriate protocol settings and will automatically start indexing once deployed and configured.
Your .env file with all required environment variables
You can proceed with the development setup below.
- Node.js (v20+)
- pnpm
- Setup .env file
Option 1: If you want to run the indexer locally on container, you can use docker-compose to start the database and the indexer.
sudo docker-compose up -dOption 2: If you want to run the indexer locally on your machine, you can use the following command to start the indexer. Make sure you already have postgres instance running, and modify the .env file with the correct values.
pnpm install
cd packages/common
pnpm typegen
cd projects/uniswapv2
pnpm typegen
pnpm codegen
pnpm migration (would fail if nothing to migrate)
pnpm devβββ abi/ # Smart contract ABI files
βββ abs-app/ # Absinthe API application
β βββ src/
β β βββ index.ts
β βββ tsconfig.json
β βββ package.json
βββ packages/
β βββ common/
β βββ src/
β βββ types/
β β βββ interfaces.ts # Core type definitions
β β βββ protocols.ts # Protocol configurations
β β βββ schema.ts # Validation schemas
β β βββ tokens.ts
β βββ utils/
β βββ chains.ts # Chain configuration data
βββ projects/
β βββ compoundv2/ # Compound V2 indexer
β β βββ src/
β β β βββ model/
β β β βββ generated/
β β β βββ activeBalances.model.ts
β β β βββ poolConfig.model.ts
β β β βββ poolState.model.ts
β β β βββ token.model.ts
β β βββ schema.graphql
β βββ uniswapv2/ # Uniswap V2 indexer
β βββ src/
β βββ model/
β βββ generated/
β βββ activeBalances.model.ts
β βββ poolConfig.model.ts
β βββ token.model.ts
βββ db/ # Database migrations
β βββ migrations/
βββ logs/ # Application logs
βββ .env.example # Environment variables template
βββ abs_config.example.json # Protocol configuration template
βββ commands.json # Subsquid commands configuration
βββ docker-compose.yml # Docker services setup
βββ tsconfig.base.json # Base TypeScript configuration
βββ tsconfig.json # Main TypeScript configuration
βββ README.md
βββ LICENSE
βββ package.json
βββ pnpm-lock.yamlWe welcome contributions to the Absinthe Adapters project! This section outlines the development practices, tools, and guidelines for contributors.
Before contributing, ensure you have the proper development environment set up as described in the Development section above.
Our project uses several tools to maintain code quality and consistency:
We use ESLint with TypeScript support for code linting and catching potential issues.
- Configuration: See
eslint.config.jsfor the full configuration - Rules: Includes TypeScript-specific rules and Prettier integration
- Ignored files: Generated files, builds, and migrations are excluded
Commands:
# Run linting on all TypeScript files
pnpm lint
# Auto-fix linting issues where possible
pnpm lint:fix
# Lint only changed files (useful during development)
pnpm lint:changed
# Lint only staged files (used in pre-commit hooks)
pnpm lint:stagedCode formatting is handled automatically by Prettier to ensure consistent code style.
- Configuration: See
.prettierrcfor formatting rules - Settings: Single quotes, semicolons, trailing commas, 100 character line width
- Ignored files: See
.prettierignorefor excluded files
Commands:
# Format all files
pnpm format
# Format only changed files
pnpm format:changed
# Format only staged files
pnpm format:staged
# Check formatting without making changes
pnpm format:checkWe use Husky to run automated checks before commits to ensure code quality.
Pre-commit hooks (.husky/pre-commit):
- Prettier formatting: Automatically formats staged files
- ESLint checks: Runs linting on staged TypeScript files
- Validation: Ensures all checks pass before allowing commit
The hooks run automatically when you commit. If any check fails, the commit will be rejected.
Follow conventional commit format for clear commit history:
type(scope): description
feat(uniswap): add support for new pool types
fix(pricing): resolve decimal precision issues
docs(readme): update configuration instructions
chore(deps): update dependencies
Types:
feat: New featuresfix: Bug fixesdocs: Documentation changeschore: Maintenance tasksrefactor: Code refactoringtest: Test additions/changes
When adding support for a new protocol, you have two options:
For quick setup, choose the appropriate pre-built template based on your protocol type:
When to use: For protocols that require tracking user balances over time for yield calculations, staking rewards, or liquidity mining programs.
Best for:
- Staking protocols (deposits/withdrawals with yield tracking)
- Liquidity mining programs
- Time-based reward distribution systems
- Any protocol where "balance Γ time" matters for rewards
-
Copy the template:
cd projects cp -r template-twb your-staking-protocol-name -
Update package.json:
{ "name": "@absinthe/your-staking-protocol-name", "description": "Absinthe adapter for YourStakingProtocol" } -
Add your contract ABIs:
# Replace hemi.json with your actual staking contract ABIs # Ensure you have Deposit and Withdraw events (or equivalent)
-
Update configuration: Add to
abs_config.json:{ "stakingProtocols": [ { "type": "your-staking-protocol-name", "name": "your-protocol-instance", "contractAddress": "0x...", "chainId": 1, "fromBlock": 12345678, "toBlock": 0 } ] } -
Configure supported tokens: Update
src/utils/conts.tswith your supported tokens:const TOKEN_METADATA = [ { address: '0x...', // Token contract address decimals: 18, // Token decimals coingeckoId: 'token-name', // CoinGecko ID for price data }, // ... more tokens ];
-
Generate types and run:
pnpm typegen # Generate ABI types pnpm codegen # Generate TypeORM models pnpm migration # Run database migrations pnpm dev # Start development
-
Customize the logic:
- Update
src/BatchProcessor.tswith your protocol's event handling - Modify
src/processor.tswith correct events and contract addresses - Adjust time window duration in configuration (balanceFlushIntervalHours)
- Implement your specific staking/reward logic
- Update
When to use: For protocols that only need to track individual transactions without balance state.
Best for:
- DEX swap tracking
- Bonding curve protocols
- Auction platforms
- Simple transaction logging
-
Copy the template:
cd projects cp -r template-txn your-protocol-name -
Update package.json:
{ "name": "@absinthe/your-protocol-name", "description": "Absinthe adapter for YourProtocol" } -
Add your contract ABIs:
# Replace mint.json with your actual contract ABIs -
Update configuration: Add to
abs_config.json:{ "txnTrackingProtocols": [ // or appropriate protocol type { "type": "your-protocol-name", "name": "your-protocol-instance", "contractAddress": "0x...", "chainId": 1, "fromBlock": 12345678, "toBlock": 0 } ] } -
Generate types and run:
pnpm typegen # Generate ABI types pnpm codegen # Generate TypeORM models pnpm migration # Run database migrations pnpm dev # Start development
-
Customize the logic:
- Update
src/BatchProcessor.tswith your protocol's event handling - Modify
src/processor.tswith correct events and contract addresses - Implement your specific business logic
- Update
| Feature | Template-TWB | Template-TXN |
|---|---|---|
| Use Case | Time-weighted balance tracking | Simple transaction logging |
| Data Storage | Complex state with balance snapshots | Simple transaction records |
| Time Windows | β Configurable intervals | β No time-based processing |
| Balance Tracking | β Continuous user balances | β Event-based only |
| Yield Calculations | β Supports yield/reward tracking | β Not applicable |
| Complexity | Higher (state management) | Lower (stateless processing) |
| Memory Usage | Higher (active balances) | Lower (no persistent state) |
| Recovery | β Full state recovery | β Position-based recovery |
For complete control or complex protocols:
- Create project directory:
projects/{protocol-name}/ - Define schema: Create
schema.graphqlwith required entities - Generate models: Run
pnpm typegenandpnpm codegen - Implement processor: Create indexing logic in
src/ - Add configuration: Update protocol configurations
Template-TWB includes:
- β Time-weighted balance tracking with configurable windows
- β State persistence for crash recovery
- β Multi-token support with price integration
- β Periodic balance snapshots for yield calculations
- β Comprehensive comments explaining time-weighted logic
- β TypeScript types and interfaces pre-configured
- β Database setup with TypeORM models
- β Error handling and logging
- β API integration with Absinthe backend
- β Price fetching with CoinGecko integration
Template-TXN includes:
- β Simple transaction tracking for event-based protocols
- β Complete file structure with all required files
- β Comprehensive comments explaining every step
- β TypeScript types and interfaces pre-configured
- β Database setup with TypeORM models
- β Error handling and logging
- β API integration with Absinthe backend
- β Price fetching with CoinGecko integration
- β Gas fee calculations
- β USD value calculations
- Folder names:
protocol-name(kebab-case) - Package names:
@absinthe/protocol-name - Class names:
ProtocolNameProcessor(PascalCase) - File names:
main.ts,processor.ts,BatchProcessor.ts
For database schema changes:
- Modify GraphQL schema: Update
schema.graphqlfiles - Generate TypeORM models: Run
pnpm typegen - Create migration: Run
pnpm migration:generate - Test migration: Verify migration works correctly
- Document changes: Update relevant documentation
When contributing:
- Code comments: Document complex logic and algorithms
- README updates: Update documentation for new features
- Configuration examples: Provide clear configuration examples
- API documentation: Document any API changes
All contributions go through code review:
- Automated checks: CI runs linting, formatting, and tests
- Manual review: Team members review code for quality and correctness
- Feedback incorporation: Address review comments
- Approval and merge: Changes are merged after approval
If you need help while contributing:
- Issues: Check existing GitHub issues for similar problems
- Discussions: Use GitHub Discussions for questions
- Documentation: Reference this README and inline code documentation
- Community: Reach out to maintainers for guidance
If an adapter receives a 429 response, it will keep retrying to send the data indefinitely. This ensures that no data is dropped due to rate limiting.
Yes. The indexer maintains a "position" (like a file pointer) and can resume from the last saved place after a crash or restart. This ensures all data is eventually pushed, even if some requests are rate-limited or interrupted.
Yes, if the indexer crashes and restarts, some data may be sent more than once. However, any potential duplication is handled by primary keys on the database tables, ensuring at-least-once delivery semantics.
The indexer pushes data as fast as possible, but several factors can slow it down:
- Subsquid data fetching speed
- RPC call latency
- Internal processing logic
- External API call speed (if not batched)
- The adapters-api rate limiting (the main intentional slowdown)
The indexer will backfill as fast as it can, then switch to real-time indexing once caught up.
The blockchainβs data generation speed is always slower than our rate limit, so this edge case does not occur.
- No data is dropped due to rate limiting; retries ensure eventual delivery.
- At-least-once delivery is guaranteed, and deduplication is handled by primary keys.
- The indexer is designed to recover from crashes and resume from the last processed position.
This project is licensed under the MIT License - see the LICENSE file for details.