4. LP Tracking
Purpose
Track user LP token balances via Transfer
events. This emits balanceDelta
events that feed into the time-weighted balance (TWB) calculation system, enabling rewards based on liquidity provision duration.
Implementation
if (params.trackLP && log.topics[0] === transferTopic) {
const { from, to, value } = univ2Abi.events.Transfer.decode(log);
// Handle token outflow (decrease balance)
await emit.balanceDelta({
user: from,
asset: params.poolAddress,
amount: new Big(value.toString()).neg(),
activity: 'hold',
});
// Handle token inflow (increase balance)
await emit.balanceDelta({
user: to,
asset: params.poolAddress,
amount: new Big(value.toString()),
activity: 'hold',
});
}
How Transfer Events Work
ERC-20 Transfer Event
event Transfer(address indexed from, address indexed to, uint256 value);
topic0
: Event signature hashtopic1
:from
address (indexed)topic2
:to
address (indexed)topic3
:value
(not indexed, in data)
Automatic Null Address Handling
The Absinthe engine automatically ignores balance delta events for the null address (0x0000000000000000000000000000000000000000
). This means you don't need to filter out mint and burn events - the engine handles this automatically.
Balance Delta Logic
Why Two Events?
// When Alice sends 100 tokens to Bob:
// Event 1: Alice's balance decreases by 100
// Event 2: Bob's balance increases by 100
// Alice: -100 LP tokens
await emit.balanceDelta({
user: from,
asset: params.poolAddress,
amount: new Big(value.toString()).neg(),
activity: 'hold',
});
// Bob: +100 LP tokens
await emit.balanceDelta({
user: to,
asset: params.poolAddress,
amount: new Big(value.toString()),
activity: 'hold',
});
Big Number Handling
Why Big Numbers?
// ❌ Wrong: JavaScript numbers lose precision
const amount = value.toString(); // "1000000000000000000000000"
const balance = Number(amount); // 1e+24 (precision lost!)
// ✅ Correct: Use Big.js for precision
import Big from 'big.js';
const amount = new Big(value.toString()); // Exact precision maintained
- Uniswap V2 LP tokens use 18 decimals
1000000000000000000
= 1 LP token500000000000000000
= 0.5 LP tokens
Common Pitfalls
// ❌ Incorrect: Direct number conversion
amount: Number(value.toString()) // Precision loss for large numbers
// ❌ Incorrect: No negative for outflows
amount: new Big(value.toString()) // Missing .neg() for transfers
// ✅ Correct: Proper big number handling
amount: new Big(value.toString()).neg() // For outflows
amount: new Big(value.toString()) // For inflows
Activity Types
activity: 'hold'
Why 'hold'?
- LP tokens represent ownership in the pool
- Holding LP tokens = providing liquidity
- This enables time-weighted rewards for liquidity provision
'swap'
- For trading activities'stake'
- For staking tokens'lend'
- For lending activities
Event Deduplication
Why Deterministic Keys Matter
// Each Transfer event should emit unique balance deltas
// The system will aggregate these over time for TWB calculations
- User starts with: 0 LP tokens
- Transfer event 1: +1.0 LP tokens → Balance: 1.0
- Transfer event 2: -0.5 LP tokens → Balance: 0.5
- Transfer event 3: +2.0 LP tokens → Balance: 2.5
Real-World Example
Complete Transfer Flow
// User adds liquidity (mint)
{
user: '0x123...abc',
asset: '0xpool...address',
amount: new Big('1000000000000000000'), // +1.0 LP
activity: 'hold'
}
// User removes liquidity (burn)
{
user: '0x123...abc',
asset: '0xpool...address',
amount: new Big('500000000000000000').neg(), // -0.5 LP
activity: 'hold'
}