feat(subgraph): index per-trove operation history#41
Conversation
Adds an immutable `TroveOperation` entity that records every state-changing event on a trove (opens, adjustments, rate changes, applyPendingDebt, liquidations, redemptions, batch join/leave). Each row carries: - operation kind (enum mirroring ITroveEvents.Operation) - signed collateral / debt deltas from the op - post-op snapshot (newCollateral, newDebt, newInterestRate) - redistribution gains, upfront fee - redemptionPrice / liquidationPrice — pulled from same-tx Redemption / Liquidation logs via receipt walking - initiator (tx.from), block, timestamp, tx hash Rows are written from the existing `handleTroveOperation` handler, which fires after `TroveUpdated` / `BatchedTroveUpdated`, so the in-memory Trove entity already reflects the post-op snapshot. ## Listening at TroveManager addresses (not AddressesRegistry) Mento V3 deploys via CREATE2, so `AddressesRegistry.setAddresses()` is called with predicted addresses for contracts that don't yet have code (TroveManager is deployed ~12 blocks later in a follow-up tx). Listening to AddressesRegistry's `CollateralRegistryAddressChanged` (the upstream pattern) causes the bootstrap handler to query a phantom TroveManager and revert fatally. TroveManager's constructor emits the same `CollateralRegistryAddressChanged(address)` from its own address at a block where all contracts are real. The three data sources are repointed to per-branch TroveManager addresses with their actual deploy blocks: GBPm Celo 0xb38aEf...3885C9 block 60668167 CHFm Celo 0x4e105f...9262E block 65390610 JPYm Celo 0xd2E65a...3da65df block 65390636 GBPm Sepolia 0x3ADFF1...731c46 block 19187423 CHFm Sepolia 0xc21dc0...43A3f8 block 23671277 JPYm Sepolia 0xeD5425...fba84d block 23671448 ## Multi-branch entity-ID namespacing Each Mento V3 branch has its own CollateralRegistry; each numbers its single collateral at `collIndex=0`. The upstream subgraph keys entities by collIndex alone, so without a fix all three branches would write to the same Collateral/Trove/InterestBatch IDs and clobber each other. `addCollateral` now namespaces `collId` by the branch's TroveManager address (the `event.address` of the bootstrap event), and that prefix flows through DataSourceContext into every downstream entity ID: Collateral.id = "<troveManager>:<collIndex>" Trove.id = "<troveManager>:<collIndex>:<troveIdHex>" CollateralAddresses.id = "<troveManager>:<collIndex>" InterestRateBracket.id = "<troveManager>:<collIndex>:<rateFloored>" InterestBatch.id = "<troveManager>:<collIndex>:<batchManager>" Also fixes a pre-existing dedupe bug: the bootstrap loop checked `Collateral.load(tokenAddress)` but stored entities under `collIndex` — the check was always false. Now correctly checks the namespaced collId. Validated against Celo Sepolia via local graph-node — all three live branches (GBPm/CHFm/JPYm) indexed independently with distinct Collateral entities and 86 total TroveOperation rows including 36 with receipt-walked redemptionPrice (GBPm 81, CHFm 2, JPYm 3). Several troveIds appear on multiple branches with different debts confirming the namespace prevents collision. ## Other changes - networks.json gains `celo` + `celo-sepolia` profiles with exact TroveManager deploy blocks per branch. Governance is zero-addressed since Mento doesn't use Liquity's gov system (planned for removal in a follow-up). - matchstick test scaffold added: `matchstick-as`, `assemblyscript`, a `test` script, and 4 history-row tests (adjustTroveInterestRate, applyPendingDebt, redeemCollateral with price extraction, liquidate with price extraction). Phase 1 of the trove-history feature spec (AGENT_SPEC.md). Phase 2 (Studio deploy) and Phase 3 (frontend Activity panel) follow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a Phase 1 trove operation history feature to the subgraph (an immutable TroveOperation entity capturing every state-changing event per trove, with redemption/liquidation prices enriched via same-tx receipt walking) and adapts the subgraph to Mento V3's multi-branch deployment on Celo / Celo Sepolia (per-branch TroveManager data sources + namespaced entity IDs). Also wires up Matchstick tests for the new handler logic.
Changes:
- New
TroveOperationentity +TroveOperationKindenum inschema.graphql, written fromhandleTroveOperationafter the post-state has been applied; redemption/liquidation prices are decoded from same-tx logs by walkingevent.receipt. - Multi-branch subgraph manifest reworked to listen at per-branch TroveManager addresses (instead of
AddressesRegistry) and to namespacecollIdby the emitting TroveManager address so GBPm / CHFm / JPYm don't collide;networks.jsongainscelo+celo-sepoliaprofiles. - Matchstick test harness added (
matchstick-as,assemblyscript) with 4 unit tests covering interest-rate adjustment, applyPendingDebt, redeemCollateral and liquidate paths.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| subgraph/schema.graphql | Adds TroveOperation entity, TroveOperationKind enum, and a operations derived field on Trove. |
| subgraph/src/TroveManager.mapping.ts | Adds recordTroveOperation plus Redemption / Liquidation receipt walkers and a string mapping for the operation kind. |
| subgraph/src/BoldToken.mapping.ts | Namespaces every collId by the branch's TroveManager address; fixes pre-existing dedupe check. |
| subgraph/subgraph.yaml | Adds BoldTokenCHFm / BoldTokenJPYm data sources, points BoldToken at the GBPm TroveManager, switches all networks to celo-sepolia, zero-addresses Governance. |
| subgraph/networks.json | Adds full celo and celo-sepolia profiles (each with three branch TroveManager addresses + deploy blocks). |
| subgraph/package.json | Adds build/test scripts and matchstick-as + pinned assemblyscript dev deps. |
| subgraph/tests/trove-operation-history.test.ts | New Matchstick suite for the four key TroveOperation paths, including synthetic receipt construction. |
| subgraph/.gitignore | Ignores Matchstick-generated .bin/ and .latest.json. |
| pnpm-lock.yaml | Lockfile updates for the new dev deps and transitive wabt / supports-color consolidation. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - kind: ethereum/contract | ||
| name: Governance | ||
| source: | ||
| abi: Governance | ||
| address: "0x807def5e7d057df05c796f4bc75c3fe82bd6eee1" | ||
| startBlock: 22496547 | ||
| address: "0x0000000000000000000000000000000000000000" | ||
| startBlock: 19187423 |
There was a problem hiding this comment.
Acknowledged — tracking as a follow-up. The Governance data source, mapping (Governance.mapping.ts), and the related schema entities (GovernanceAllocation, GovernanceInitiative, GovernanceUser, GovernanceVotingPower, GovernanceAllocationIndex) will all be removed together in a separate PR to keep this one scoped to the trove-history feature. Zero-addressing is a known-fragile placeholder but is sufficient until that cleanup lands; graph-node simply finds no logs at address(0).
There was a problem hiding this comment.
Updated decision — went ahead and removed Governance entirely in 3a9cafb2 instead of deferring. Dropped the data source from subgraph.yaml, removed entries from all three network profiles, deleted Governance.mapping.ts, and removed GovernanceVotingPower / GovernanceAllocationIndex / GovernanceAllocation / GovernanceInitiative from the schema. Verified no remaining references via grep across scripts / cli / tests / src. codegen + build (celo + celo-sepolia) + matchstick (5/5) all green.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5b697ef. Configure here.
…eum profiles Addresses two of three PR review items from Cursor Bugbot and Copilot: 1. **Receipt-walking now filters by log.address** (Bugbot, Medium) `extractRedemptionPrice` / `extractLiquidationPrice` previously matched receipt logs only by topic hash, so a transaction touching multiple Mento V3 branches (each with its own TroveManager emitting its own branch-priced Redemption / Liquidation events) could attach the wrong branch's price to a trove. Now both functions additionally require `log.address.equals( event.address)` — i.e. the price event must come from the same TroveManager that emitted this TroveOperation. New matchstick test `redeemCollateral ignores Redemption logs emitted by a different branch's TroveManager` plants a foreign-branch Redemption log in the receipt and asserts that `redemptionPrice` remains null. 5/5 tests passing. 2. **networks.json drops the upstream `mainnet` and `sepolia` profiles** (Copilot) Those were carry-overs from upstream Liquity's Ethereum deployment. Mento V3 doesn't deploy to Ethereum, so the profiles weren't reachable and were now stale (no entries for the new `BoldTokenCHFm` / `BoldTokenJPYm` data sources). Removing them keeps the manifest consistent. The `local` profile is kept for anvil dev and gets zero-addressed stubs for CHFm / JPYm so `graph build --network mainnet ./networks.json` from the upstream `--local` deploy preset still works. The third review item (Governance data source zero-addressed on Celo) is deferred per prior discussion; will be removed in a follow-up that also strips the related entities and `Governance.mapping.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mento V3 doesn't use Liquity's voting/initiative system. Previously zero- addressed as a placeholder, but the Copilot review pointed out that's fragile for Studio. Removing the whole thing now rather than carrying dead code: - subgraph.yaml: drop the Governance data source - networks.json: drop the Governance entry from celo / celo-sepolia / local - schema.graphql: drop GovernanceVotingPower, GovernanceAllocationIndex, GovernanceAllocation, GovernanceInitiative - src/Governance.mapping.ts: deleted Nothing else in the subgraph references these — confirmed by grep across scripts/, cli/, tests/, and src/. The `contracts/out/Governance.sol/Governance.json` ABI artifact stays (it's a foundry build output from upstream and we don't modify contracts/). codegen + build (celo + celo-sepolia) + matchstick (5/5) all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Summary
Phase 1 of the trove-history feature.
https://thegraph.com/studio/subgraph/mento-troves-celo-sepolia/
Adds an immutable
TroveOperationentity that records every state-changing event on a trove — opens, adjustments, rate changes,applyPendingDebt, liquidations, redemptions, batch join/leave. Each history row carries:ITroveEvents.Operation)collateralDelta/debtDeltanewCollateral,newDebt,newInterestRate)redemptionPrice/liquidationPriceenriched via same-tx receipt walkinginitiator(tx.from), block, timestamp, tx hashRows are written from the existing
handleTroveOperationhandler, which fires afterTroveUpdated/BatchedTroveUpdated, so the in-memoryTroveentity already reflects the post-op snapshot.Mento V3 adaptations worth a closer look
Listening at TroveManager addresses, not AddressesRegistry
Mento V3 deploys via CREATE2, so
AddressesRegistry.setAddresses()firesCollateralRegistryAddressChangedwith predicted addresses for contracts that don't yet have code (TroveManager is deployed ~12 blocks later). The upstream subgraph would call the phantom TroveManager and abort fatally. Instead, the three data sources listen at the per-branch TroveManager addresses —TroveManager.constructoremits the sameCollateralRegistryAddressChanged(address)event from its own address at a block where everything is real.Multi-branch entity-ID namespacing
Each branch (GBPm/CHFm/JPYm) has its own CollateralRegistry, each numbering its single collateral at
collIndex=0. The upstream subgraph keys entities bycollIndexalone — without a fix, all three branches would clobber each other atCollateral(id="0"),Trove(id="0:<troveId>"), etc.addCollateralnow namespacescollIdby the branch's TroveManager address; that prefix flows throughDataSourceContextinto every downstream entity ID. Verified empirically that severaltroveIds exist concurrently on multiple branches without collision.Pre-existing dedupe bug fixed
The bootstrap loop checked
Collateral.load(tokenAddress)but stored entities undercollIndex— the check was always false. Now correctly checks the namespacedcollId.Configuration
networks.jsongetscelo+celo-sepoliaprofiles, each with all three branches' TroveManager addresses + exact deploy blocks:0xb38aEf2bF4e34B997330D626EBCd7629De3885C90x4e105fEf015dB26320c077427bd605acEad9262E0xd2E65aF47d927d5E84f384AE6BAC4F97c3da65df0x3ADFF16949513480E051F451748Ee8B801731c460xc21dc07eC1707d315A7eff31A6556e628E43A3f80xeD54259166EbbFe72083FC37eE2a00e922fbA84DGovernanceis zero-addressed on Celo since Mento doesn't use Liquity's voting/initiative system — flagged for removal in a follow-up.Tests
Matchstick was not previously configured in the repo. Added
matchstick-as+assemblyscript+ atestscript, plus 4 unit tests insubgraph/tests/trove-operation-history.test.tscovering:adjustTroveInterestRatepost-state snapshotapplyPendingDebtredistribution gains without zeroing user stateredeemCollateralredemption-price extraction from a synthetic Redemption logliquidateliquidation-price extraction from a synthetic Liquidation logTest plan
corepack pnpm codegenpassescorepack pnpm buildpasses (against--network celo+--network celo-sepolia)corepack pnpm testpasses (4/4 matchstick tests)TroveOperationrows captured (GBPm 81, CHFm 2, JPYm 3) including 36 withredemptionPricepopulated from receipt-walked Redemption logs; three distinctCollateralentities under namespaced IDsWhat's next (out of scope for this PR)
mento-troves-celo(gated on wallet funding).frontend-monorepopermento-trove-history-wireframe.jsx.Known follow-ups
Governancedata source entirely once we confirm no consumer reads its entities on Celo.BoldToken(retained for GBPm to preservegenerated/BoldToken/...import paths) is misleading — rename toAddressesRegistry*or*BoldTokenper branch in a cleanup pass.🤖 Generated with Claude Code
Note
Medium Risk
Adds a new immutable
TroveOperationentity and receipt-parsing logic inhandleTroveOperation, plus rekeys collateral/trove IDs for multi-branch deployments; mistakes could cause indexing failures or data inconsistencies across networks.Overview
Adds per-trove operation history indexing. Introduces an immutable
TroveOperationentity (andTrove.operationsrelationship) and records one row perTroveOperationevent with deltas, post-state snapshots, redistribution/upfront-fee fields, and optional redemption/liquidation prices extracted from same-transaction receipt logs.Adapts the subgraph for Mento V3 multi-branch deployments. Collateral (and downstream) entity IDs are now namespaced by the emitting TroveManager address to avoid cross-branch collisions, and bootstrap deduping is updated accordingly. The manifest/network config is switched to
celo/celo-sepoliaTroveManager-based data sources (GBPm/CHFm/JPYm), and the Governance indexing code/entities are removed.Adds subgraph testing support. Adds
matchstick-as/assemblyscript, newbuild/testscripts,.gitignoreentries for test artifacts, and a unit test suite covering history-row creation and receipt-walked price extraction.Reviewed by Cursor Bugbot for commit 3a9cafb. Bugbot is set up for automated code reviews on this repo. Configure here.