feat(subgraph): mirror FROST/Schnorr event surface from tBTC monorepo#4
feat(subgraph): mirror FROST/Schnorr event surface from tBTC monorepo#4mswilkison wants to merge 2 commits into
Conversation
Mirrors the tbtc-subgraph slice of `tlabs-xyz/tbtc:feat/frost-schnorr-migration` (PR #10) at frozen tag `frost-extraction-source-v1`. Adds FROST + scheme preference event handling to the subgraph schema and Bridge mapping. Changes - abis/Bridge.json (mirror): bundled Bridge ABI updated with FROST events (NewWalletRegisteredV2, NewFrostWalletRegistered, FrostWalletRegistrySet, P2TRFraudRouterSet, EcdsaFraudRouterSet, NewWalletSchemeSet, etc.) - schema.graphql (mirror): new entity definitions for FROST wallets + scheme preference events - src/mappingBridge.ts (mirror): handler logic for new event types - subgraph.yaml (mirror): event subscription manifest extended for FROST - scripts/verify-subgraph-abi-drift.mjs (allowlisted-divergence): drift-check tool ported from monorepo with placeholder for canonical-published ABI source Provenance - Source repository: tlabs-xyz/tbtc - Source branch: feat/frost-schnorr-migration - Source tag (frozen): frost-extraction-source-v1 - Source commit (H): 52389bd5cccb5daeef195671feb7ca46be6e2f37 - Source manifest: extraction/frost-extraction-source-manifest.json (manifestSha256: f7295fb738104501eb6c0c2447a42122ceb5f684c7a7c5dfb50ecb0bde3a0ea0) - Source PR(s): #440 (Phase C-1.1a — subgraph FROST + scheme preference event surface; full source-PR list per the source manifest's commit range) Known TBD (resolved pre-merge) - scripts/verify-subgraph-abi-drift.mjs currently expects monorepo paths (`packages/abi/dist/contracts/` AND `data/tbtc-subgraph/abis/` in same repo). Refactor needed to fetch canonical-published ABI from tbtc-v2 release artifact (or npm-published @threshold-network/tbtc-v2-abi). expectedTargetSha256 for this entry in the source manifest is <TBD> until the refactor lands. The refactor is parameterizable on the canonical ABI source URL; can land as a follow-up commit on this PR branch before merge. Verification (Gate E pre-merge per extraction plan v38 §7.2) - Mirror files: sha256 equality between `git show frost-extraction-source-v1:data/tbtc-subgraph/<sourcePath>` and this PR's content at the target path. - Allowlisted-divergence (verify-subgraph-abi-drift.mjs): sha256 equality against expectedTargetSha256 (recorded in manifest post-refactor) AND dual signoff (extraction lead + canonical repo maintainer) in this PR description per plan v38 §4.2. - PR-scoped rogue-file check: `git diff --name-only main...HEAD` only contains files declared in the manifest's fileMap with targetKey "subgraph". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR adds C-1 wallet scheme support (new schema entities, Bridge state singleton, and handlers for wallet registration, scheme flips, and governance setter events) and introduces a Node.js CLI that verifies subgraph ABI JSONs against canonical ABIs with allowlist support. ChangesWallet Scheme Support Implementation
ABI Drift Validation Tool
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/verify-subgraph-abi-drift.mjs`:
- Around line 54-61: Normalization currently omits ABI-significant fields so
different ABIs can collapse to the same signature: include event.anonymous in
the event signature generation inside the event branch (where normalizeType and
params are used) — e.g., append an ":anonymous" token or include a boolean flag
in the returned string — and likewise include mutability for
constructor/fallback branches (the code block handling constructor/fallback
around the other diff at 69-76) by incorporating entry?.stateMutability (or
payable/nonpayable equivalent) into their signature strings so
constructor/fallback signatures differ when mutability differs.
- Around line 10-12: The script hardcodes subgraph ABI path which fails when
ABIs are mirrored under abis/; update the resolution logic for subgraphAbiDir to
try the existing default ("data/tbtc-subgraph/abis") and fall back to "abis"
(both joined with repoRoot) and pick the first existing directory; update any
subsequent checks that reference subgraphAbiDir (the existence check and the
later comparison logic around the paths used in the drift validation) so they
use the resolved directory; keep allowlistPath and canonicalDir behavior the
same to preserve CI/default wiring.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 4a2e9e6c-2c91-4ef0-9668-03801e5910b2
📒 Files selected for processing (5)
abis/Bridge.jsonschema.graphqlscripts/verify-subgraph-abi-drift.mjssrc/mappingBridge.tssubgraph.yaml
| if (type === "event") { | ||
| const name = String(entry?.name ?? ""); | ||
| const inputs = Array.isArray(entry?.inputs) ? entry.inputs : []; | ||
| const params = inputs | ||
| .map((item) => `${normalizeType(item)}:${item?.indexed ? "indexed" : "plain"}`) | ||
| .join(","); | ||
| return `event:${name}(${params})`; | ||
| } |
There was a problem hiding this comment.
Normalization misses ABI-significant fields, causing false negatives.
event.anonymous and constructor/fallback mutability are not encoded in signatures. Two different ABIs can normalize identically, so drift can be missed.
Targeted fix
if (type === "event") {
const name = String(entry?.name ?? "");
const inputs = Array.isArray(entry?.inputs) ? entry.inputs : [];
+ const anonymous = entry?.anonymous ? "anonymous" : "nonanonymous";
const params = inputs
.map((item) => `${normalizeType(item)}:${item?.indexed ? "indexed" : "plain"}`)
.join(",");
- return `event:${name}(${params})`;
+ return `event:${name}(${params}):${anonymous}`;
}
if (type === "constructor") {
const inputs = Array.isArray(entry?.inputs) ? entry.inputs : [];
- return `constructor(${inputs.map(normalizeType).join(",")})`;
+ const stateMutability = String(entry?.stateMutability ?? "");
+ return `constructor(${inputs.map(normalizeType).join(",")}):${stateMutability}`;
}
if (type === "fallback" || type === "receive") {
- return type;
+ const stateMutability = String(entry?.stateMutability ?? "");
+ return `${type}:${stateMutability}`;
}Also applies to: 69-76
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/verify-subgraph-abi-drift.mjs` around lines 54 - 61, Normalization
currently omits ABI-significant fields so different ABIs can collapse to the
same signature: include event.anonymous in the event signature generation inside
the event branch (where normalizeType and params are used) — e.g., append an
":anonymous" token or include a boolean flag in the returned string — and
likewise include mutability for constructor/fallback branches (the code block
handling constructor/fallback around the other diff at 69-76) by incorporating
entry?.stateMutability (or payable/nonpayable equivalent) into their signature
strings so constructor/fallback signatures differ when mutability differs.
Per extraction plan v38 §4.4, allowlisted-divergence files require content normalization for canonical context. This commit applies the transformation declared in the source manifest for the subgraph drift check. Changes - subgraphAbiDir: was `<repoRoot>/data/tbtc-subgraph/abis` (monorepo prefix); now `<repoRoot>/abis` since the entire monorepo subtree IS the canonical subgraph repo root - canonicalDir: was `<repoRoot>/packages/abi/dist/contracts` (monorepo co-located ABI package); now configurable via TBTC_V2_ABI_DIR env var with default `<repoRoot>/node_modules/@threshold-network/tbtc-v2-abi/contracts` (canonical-published ABI npm package, post-Gate-E) - Updated error messages to document both supported configurations (env var override vs npm package install) Operational model - Post-Gate-E stable ABI release: subgraph CI `pnpm install @threshold-network/tbtc-v2-abi` as devDependency; script runs against installed package by default - During extraction window (canary ABI; pre-Gate-E stable swap): CI sets TBTC_V2_ABI_DIR to point at a checked-out canonical tbtc-v2's `solidity/packages/abi/dist/contracts/` - Local development: same — set env var or install npm package Recompute expectedTargetSha256 for this entry in the source manifest and collect dual signoff before merge per plan v38 §4.2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/verify-subgraph-abi-drift.mjs (1)
156-158:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winHandle malformed allowlist JSON explicitly.
A malformed
scripts/abi-drift-allowlist.jsoncurrently throws an uncaught parse exception. Wrap parsing intry/catchand emit a targeted error to keep CI output actionable.Suggested patch
-const allowlist = (await exists(allowlistPath)) - ? JSON.parse(await fs.readFile(allowlistPath, "utf8")) - : {}; +let allowlist = {}; +if (await exists(allowlistPath)) { + try { + allowlist = JSON.parse(await fs.readFile(allowlistPath, "utf8")); + } catch (error) { + console.error( + `Failed to parse allowlist JSON at ${allowlistPath}: ${error instanceof Error ? error.message : String(error)}`, + ); + process.exit(1); + } +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/verify-subgraph-abi-drift.mjs` around lines 156 - 158, The current allowlist JSON parse can throw an uncaught exception; update the block that computes `allowlist` (which uses `allowlistPath`, `exists`, `fs.readFile`, and `JSON.parse`) to read the file into a string, then wrap `JSON.parse` in a try/catch that logs a targeted error (including the path and the parse error message) and exits non‑zero (e.g., process.exit(1)) so CI shows a clear, actionable failure if `scripts/abi-drift-allowlist.json` is malformed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@scripts/verify-subgraph-abi-drift.mjs`:
- Around line 156-158: The current allowlist JSON parse can throw an uncaught
exception; update the block that computes `allowlist` (which uses
`allowlistPath`, `exists`, `fs.readFile`, and `JSON.parse`) to read the file
into a string, then wrap `JSON.parse` in a try/catch that logs a targeted error
(including the path and the parse error message) and exits non‑zero (e.g.,
process.exit(1)) so CI shows a clear, actionable failure if
`scripts/abi-drift-allowlist.json` is malformed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 0f10e1de-99ae-466e-8b89-cf078eb232ef
📒 Files selected for processing (1)
scripts/verify-subgraph-abi-drift.mjs
Summary
Mirrors the tbtc-subgraph slice of
tlabs-xyz/tbtc:feat/frost-schnorr-migration(PR #10) at frozen signed tagfrost-extraction-source-v1. Adds FROST + scheme-preference event handling to the subgraph schema and Bridge mapping.Source-PR provenance
feat/frost-schnorr-migrationfrost-extraction-source-v1(signed annotated tag by maclane)52389bd5cccb5daeef195671feb7ca46be6e2f37f7295fb738104501eb6c0c2447a42122ceb5f684c7a7c5dfb50ecb0bde3a0ea0)Files in this PR
abis/Bridge.jsondata/tbtc-subgraph/abis/Bridge.jsonschema.graphqldata/tbtc-subgraph/schema.graphqlsrc/mappingBridge.tsdata/tbtc-subgraph/src/mappingBridge.tssubgraph.yamldata/tbtc-subgraph/subgraph.yamlscripts/verify-subgraph-abi-drift.mjsscripts/verify-subgraph-abi-drift.mjsKnown TBD (resolved pre-merge)
scripts/verify-subgraph-abi-drift.mjscurrently expects monorepo paths (packages/abi/dist/contracts/ANDdata/tbtc-subgraph/abis/in the same repo). To work in this canonical repo, the script must be refactored to fetch the canonical-published ABI source fromthreshold-network/tbtc-v2(release artifact URL or npm-published@threshold-network/tbtc-v2-abi).The refactor is parameterizable on the canonical ABI source URL and can land as a follow-up commit on this PR branch before merge.
expectedTargetSha256for this entry in the source manifest is<TBD>until the refactor lands.This file requires dual signoff in this PR description before merge per plan v38 §4.2:
Verification (pre-merge per plan v38 §7.2)
sha256(git show frost-extraction-source-v1:data/tbtc-subgraph/<sourcePath>)==sha256(<file at this PR head>)sha256(scripts/verify-subgraph-abi-drift.mjs at this PR head)==expectedTargetSha256once recordedgit diff --name-only master...HEADonly contains files in the source manifest's fileMap with targetKeysubgraphContext
The tbtc-subgraph is confirmed in production use via tlabs-xyz/threshold-api proxy used by threshold-dapp (per Piotr in Slack 2026-05-26). The FROST extraction adds:
NewWalletRegisteredV2event (FROST wallet registration with canonical walletID)NewFrostWalletRegisteredevent (FROST-specific marker)FrostWalletRegistrySetevent (FROST registry installation)P2TRFraudRouterSet,EcdsaFraudRouterSetevents (fraud router sidecar wiring)NewWalletSchemeSetevent (scheme preference flip)Plan context
1 of 4 mergeable mirror PRs comprising the FROST extraction:
threshold-network/tbtc-v2(largest — contracts, SDK, watchtower, ABI tooling)threshold-network/tbtc-subgraph)threshold-network/keep-core(Rust signer atpkg/tbtc/signer/)tlabs-xyz/tbtc-v3-indexer(PR #12)Plus 2 DO-NOT-MERGE drafts in tbtc-v2 for D-2.2 drain-gated slices (slice 2 + slice 4).
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests