Lp share token tracking#156
Conversation
…nvironment settings
…hema and indexer utilities
|
@githoboman Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Miracle656
left a comment
There was a problem hiding this comment.
The schema, indexing wiring, reorg-aware rollbackToLedger cleanup, and idempotent eventId-keyed upsert here are all well done, and modelling deposits as a mint-to-provider / withdrawals as a burn-from-provider mirrors decoder.ts nicely. One correctness issue needs fixing before merge:
mint/burn are treated as LP-share events for every contract, so the table over-captures.
const DEPOSIT_EVENTS = new Set(["deposit", "mint"]);
const WITHDRAW_EVENTS = new Set(["withdraw", "burn"]);pollOnce feeds the full event batch to parseLpShareEvents(events), and there's no filter narrowing to pool contracts. deposit/withdraw are pool-specific and safe — but mint/burn are generic SEP-41 events emitted by every token (stablecoin issuers, NFT-ish tokens, ordinary SACs). As written, every USDC mint, every token burn, etc. gets written into LpShareTransfer with poolId = contractId and double-stored alongside its normal token-transfer row. That defeats the point of a table meant to surface liquidity-pool shares specifically.
Options to fix:
- Simplest & safe: drop
mint/burnfrom the recognized set and rely only on the explicitdeposit/withdrawdialect (Soroswap/Phoenix/etc.). You lose pools that only emit bare share-token mint/burn, but you stop mislabelling all token mint/burn as LP shares. - Keep
mint/burnbut gate them behind a positive pool signal — e.g. only accept them from contracts already known to be pools (a pool registry / a priordeposit/withdrawseen from thatcontractId), not unconditionally.
A test asserting that a plain SEP-41 mint from a non-pool token is not recorded would lock this down. Everything else looks ready — happy to merge once the over-capture is closed.
Implemented LP-share transfer tracking, surfacing liquidity-pool deposits/withdrawals as first-class LP-share transfers tagged with the pool ID.
Closes #142
New module — src/indexer/lp-shares.ts
A self-contained, pure decoder mirroring the existing NFT ingester / decoder style:
isLpShareEvent / parseLpShareEvent / parseLpShareEvents — detect and decode LP events, never throwing (a bad event yields null so it can't stall ingest).
Handles both event dialects seen in Soroban AMMs:
Explicit deposit/withdraw (Soroswap, Phoenix, …) — provider in topics[1].
Bare SEP-41 mint/burn of the pool's own share token (native liquidity_pool SAC) — recipient in topics[2] for mint, holder in topics[1] for burn.
A deposit is modelled as shares minted to the provider (toAddress, no from); a withdrawal as shares burned from the provider (fromAddress, no to) — consistent with how decoder.ts treats mint/burn.
extractShares reads the share amount from a bare i128 or a map-wrapped value (share_amount/shares/amount/…), since AMMs bundle several figures into the value; returns the absolute value as a decimal string, preserving full i128 precision.
upsertLpShareTransfers — idempotent bulk insert (skipDuplicates on eventId).
Schema & migration
New LpShareTransfer model in prisma/schema.prisma with a poolId column (the emitting pool contract — always available on the row), action, from/to, shares, ledger fields, and a unique eventId. Indexed on poolId, poolId+action, addresses, ledger, txHash.
Migration 20260629130000_add_lp_share_transfers, following the tombstones migration's exact SQL conventions.
Wiring
src/indexer.ts — added an LP-share path to pollOnce, best-effort and additive (deposit/withdraw aren't touched by the fungible path; a pool's own share mint/burn is recorded here in addition to its token row), plus an updated processed-events log line.
src/db.ts — rollbackToLedger now also prunes LP-share rows above the target ledger during reorgs.
Verification
tsc --noEmit passes clean across the whole project.
New suite src/tests/lpShares.test.ts — 25 tests, all passing (both dialects, share extraction from bare/map/large/negative values, pool-ID tagging, batch filtering, idempotent insert).
Full jest run: my change adds zero new failures. The 3 failing suites are pre-existing tests/integration/ vitest files (run via the se