Skip to content

feat(subgraph): mirror FROST/Schnorr event surface from tBTC monorepo#4

Open
mswilkison wants to merge 2 commits into
masterfrom
extraction/frost-mirror-2026-05-26
Open

feat(subgraph): mirror FROST/Schnorr event surface from tBTC monorepo#4
mswilkison wants to merge 2 commits into
masterfrom
extraction/frost-mirror-2026-05-26

Conversation

@mswilkison

@mswilkison mswilkison commented May 26, 2026

Copy link
Copy Markdown

Summary

Mirrors the tbtc-subgraph slice of tlabs-xyz/tbtc:feat/frost-schnorr-migration (PR #10) at frozen signed tag frost-extraction-source-v1. Adds FROST + scheme-preference event handling to the subgraph schema and Bridge mapping.

Source-PR provenance

Files in this PR

File Status Source path
abis/Bridge.json mirror data/tbtc-subgraph/abis/Bridge.json
schema.graphql mirror data/tbtc-subgraph/schema.graphql
src/mappingBridge.ts mirror data/tbtc-subgraph/src/mappingBridge.ts
subgraph.yaml mirror data/tbtc-subgraph/subgraph.yaml
scripts/verify-subgraph-abi-drift.mjs allowlisted-divergence scripts/verify-subgraph-abi-drift.mjs

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 the same repo). To work in this canonical repo, the script must be refactored to fetch the canonical-published ABI source from threshold-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. expectedTargetSha256 for 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:

  • Extraction lead (Maclane): pending
  • Canonical repo maintainer (Piotr): pending

Verification (pre-merge per plan v38 §7.2)

  • Mirror files (4): sha256(git show frost-extraction-source-v1:data/tbtc-subgraph/<sourcePath>) == sha256(<file at this PR head>)
  • Allowlisted-divergence (1): sha256(scripts/verify-subgraph-abi-drift.mjs at this PR head) == expectedTargetSha256 once recorded
  • PR-scoped rogue-file check: git diff --name-only master...HEAD only contains files in the source manifest's fileMap with targetKey subgraph

Context

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:

  • NewWalletRegisteredV2 event (FROST wallet registration with canonical walletID)
  • NewFrostWalletRegistered event (FROST-specific marker)
  • FrostWalletRegistrySet event (FROST registry installation)
  • P2TRFraudRouterSet, EcdsaFraudRouterSet events (fraud router sidecar wiring)
  • NewWalletSchemeSet event (scheme preference flip)
  • Updated entity definitions and mapping logic to index the new events

Plan context

1 of 4 mergeable mirror PRs comprising the FROST extraction:

  • threshold-network/tbtc-v2 (largest — contracts, SDK, watchtower, ABI tooling)
  • This PR (threshold-network/tbtc-subgraph)
  • threshold-network/keep-core (Rust signer at pkg/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

    • Added support for tracking ECDSA and FROST wallet schemes.
    • Introduced wallet registration and scheme-change audit trail with timestamps, block/tx details.
    • Enhanced bridge state to record selected wallet scheme and related governance addresses.
    • Added handlers to capture new wallet registrations, scheme flips, registry/router setters, and legacy migration events.
  • Tests

    • Added ABI drift verification script to detect interface mismatches and report warnings/errors.

Review Change Stack

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>
@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

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

Changes

Wallet Scheme Support Implementation

Layer / File(s) Summary
Wallet scheme schema entities
schema.graphql
Adds WalletScheme enum (ECDSA, FROST), Wallet entity with scheme classification and FROST-only xOnlyOutputKey, BridgeState singleton for governance-selected scheme and router/registry addresses, and WalletSchemeChange audit entity for scheme flips.
Event handler mapping
subgraph.yaml
Wires new Bridge events (NewWalletRegisteredV2, NewFrostWalletRegistered, FrostWalletRegistrySet, EcdsaFraudRouterSet, P2TRFraudRouterSet, LifecycleRouterSet, NewWalletSchemeSet, LegacyFraudChallengeMigrated) to corresponding handler functions.
Wallet registration and state persistence
src/mappingBridge.ts
Adds imports for new Bridge events and schema entities, implements legacy wallet ID derivation, scheme constants, BridgeState singleton initialization, saveWalletRegistration, and handlers for wallet registration (V1/V2/FROST), registry/router/lifecycle setters, scheme flips (writes BridgeState and WalletSchemeChange), and legacy fraud migration logging.

ABI Drift Validation Tool

Layer / File(s) Summary
Script setup and file discovery
scripts/verify-subgraph-abi-drift.mjs
Resolves canonical and subgraph ABI directories and allowlist path, provides path existence helper, validates directory presence, and enumerates subgraph ABI JSON files.
ABI normalization utilities
scripts/verify-subgraph-abi-drift.mjs
Implements normalizeType and normalizeEntry to produce deterministic signature strings for functions, events, errors, constructors, fallback/receive and tuple types; converts ABIs to signature sets and computes set differences.
ABI drift detection and reporting
scripts/verify-subgraph-abi-drift.mjs
For each subgraph ABI, loads/parses canonical ABI (if present), normalizes both, computes missing signatures both ways, applies allowlist to convert some mismatches to warnings, logs detailed drift (capped), and exits non-zero for any non-allowlisted mismatch; prints a success summary when passing.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit nibbles through the code,
ECDSA and FROST now showed,
Wallets register, schemes take flight,
Audits log each flip at night,
ABIs checked — the garden's bright 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding FROST/Schnorr event handling to the subgraph by mirroring event surface from the tBTC monorepo.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch extraction/frost-mirror-2026-05-26

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between e5606a6 and b4920bc.

📒 Files selected for processing (5)
  • abis/Bridge.json
  • schema.graphql
  • scripts/verify-subgraph-abi-drift.mjs
  • src/mappingBridge.ts
  • subgraph.yaml

Comment thread scripts/verify-subgraph-abi-drift.mjs Outdated
Comment on lines +54 to +61
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})`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Handle malformed allowlist JSON explicitly.

A malformed scripts/abi-drift-allowlist.json currently throws an uncaught parse exception. Wrap parsing in try/catch and 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

📥 Commits

Reviewing files that changed from the base of the PR and between b4920bc and 090443d.

📒 Files selected for processing (1)
  • scripts/verify-subgraph-abi-drift.mjs

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.

1 participant