Skip to content

feat(subgraph): index per-trove operation history#41

Open
bayological wants to merge 3 commits into
mainfrom
feat/subgraph-trove-history
Open

feat(subgraph): index per-trove operation history#41
bayological wants to merge 3 commits into
mainfrom
feat/subgraph-trove-history

Conversation

@bayological
Copy link
Copy Markdown
Member

@bayological bayological commented May 14, 2026

Summary

Phase 1 of the trove-history feature.

https://thegraph.com/studio/subgraph/mento-troves-celo-sepolia/

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 history row carries:

  • operation kind (mirrors ITroveEvents.Operation)
  • signed collateralDelta / debtDelta
  • post-op snapshot (newCollateral, newDebt, newInterestRate)
  • redistribution gains, upfront fee, redemptionPrice / liquidationPrice enriched via same-tx 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.

Mento V3 adaptations worth a closer look

Listening at TroveManager addresses, not AddressesRegistry

Mento V3 deploys via CREATE2, so AddressesRegistry.setAddresses() fires CollateralRegistryAddressChanged with 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.constructor emits the same CollateralRegistryAddressChanged(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 by collIndex alone — without a fix, all three branches would clobber each other at Collateral(id="0"), Trove(id="0:<troveId>"), etc. addCollateral now namespaces collId by the branch's TroveManager address; that prefix flows through DataSourceContext into every downstream entity ID. Verified empirically that several troveIds exist concurrently on multiple branches without collision.

Pre-existing dedupe bug fixed

The bootstrap loop checked Collateral.load(tokenAddress) but stored entities under collIndex — the check was always false. Now correctly checks the namespaced collId.

Configuration

networks.json gets celo + celo-sepolia profiles, each with all three branches' TroveManager addresses + exact deploy blocks:

Branch Network TroveManager Block
GBPm celo 0xb38aEf2bF4e34B997330D626EBCd7629De3885C9 60668167
CHFm celo 0x4e105fEf015dB26320c077427bd605acEad9262E 65390610
JPYm celo 0xd2E65aF47d927d5E84f384AE6BAC4F97c3da65df 65390636
GBPm celo-sepolia 0x3ADFF16949513480E051F451748Ee8B801731c46 19187423
CHFm celo-sepolia 0xc21dc07eC1707d315A7eff31A6556e628E43A3f8 23671277
JPYm celo-sepolia 0xeD54259166EbbFe72083FC37eE2a00e922fbA84D 23671448

Governance is 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 + a test script, plus 4 unit tests in subgraph/tests/trove-operation-history.test.ts covering:

  • adjustTroveInterestRate post-state snapshot
  • applyPendingDebt redistribution gains without zeroing user state
  • redeemCollateral redemption-price extraction from a synthetic Redemption log
  • liquidate liquidation-price extraction from a synthetic Liquidation log

Note: use corepack pnpm (not system pnpm) — the project pins pnpm@8.15.8 and a system 9.x will rewrite the lockfile to v9.0 format.

Test plan

  • corepack pnpm codegen passes
  • corepack pnpm build passes (against --network celo + --network celo-sepolia)
  • corepack pnpm test passes (4/4 matchstick tests)
  • Live Celo Sepolia smoke test via local graph-node: indexed past all three branch deploy blocks; 86 TroveOperation rows captured (GBPm 81, CHFm 2, JPYm 3) including 36 with redemptionPrice populated from receipt-walked Redemption logs; three distinct Collateral entities under namespaced IDs
  • Reviewer to spot-check one indexed trove's history against Celoscan once Studio staging is up (Phase 2)

What's next (out of scope for this PR)

  • Phase 2: Subgraph Studio staging deploy against Celo mainnet → cross-check against Celoscan → publish to The Graph decentralized network as mento-troves-celo (gated on wallet funding).
  • Phase 3: Trove Activity panel in frontend-monorepo per mento-trove-history-wireframe.jsx.

Known follow-ups

  • Remove the Governance data source entirely once we confirm no consumer reads its entities on Celo.
  • The data source name BoldToken (retained for GBPm to preserve generated/BoldToken/... import paths) is misleading — rename to AddressesRegistry* or *BoldToken per branch in a cleanup pass.

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new immutable TroveOperation entity and receipt-parsing logic in handleTroveOperation, 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 TroveOperation entity (and Trove.operations relationship) and records one row per TroveOperation event 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-sepolia TroveManager-based data sources (GBPm/CHFm/JPYm), and the Governance indexing code/entities are removed.

Adds subgraph testing support. Adds matchstick-as/assemblyscript, new build/test scripts, .gitignore entries 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.

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>
Copilot AI review requested due to automatic review settings May 14, 2026 18:45
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TroveOperation entity + TroveOperationKind enum in schema.graphql, written from handleTroveOperation after the post-state has been applied; redemption/liquidation prices are decoded from same-tx logs by walking event.receipt.
  • Multi-branch subgraph manifest reworked to listen at per-branch TroveManager addresses (instead of AddressesRegistry) and to namespace collId by the emitting TroveManager address so GBPm / CHFm / JPYm don't collide; networks.json gains celo + celo-sepolia profiles.
  • 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.

Comment thread subgraph/networks.json
Comment thread subgraph/subgraph.yaml Outdated
Comment on lines +98 to +103
- kind: ethereum/contract
name: Governance
source:
abi: Governance
address: "0x807def5e7d057df05c796f4bc75c3fe82bd6eee1"
startBlock: 22496547
address: "0x0000000000000000000000000000000000000000"
startBlock: 19187423
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread subgraph/src/TroveManager.mapping.ts
bayological and others added 2 commits May 15, 2026 11:12
…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>
Copilot AI review requested due to automatic review settings May 15, 2026 16:26
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants