Skip to content

feat(contracts): tnt-core 0.18 compatibility behind a per-chain revision flag#3315

Merged
drewstone merged 1 commit into
developfrom
fix/tnt-core-018-compat
Jul 3, 2026
Merged

feat(contracts): tnt-core 0.18 compatibility behind a per-chain revision flag#3315
drewstone merged 1 commit into
developfrom
fix/tnt-core-018-compat

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Problem

tnt-core 0.18 chains (greenfield redeploys) break three dapp reads silently:

  1. getBlueprint dropped operatorCount from the struct (7→6 fields) — the fixed-ABI decode throws inside per-id try/catch, so the blueprint catalog's chain-fallback renders empty and the binary-upgrade owner gate disables itself.
  2. LockInfo.expiryBlock now carries a unix timestamp in the same slot/type — decode succeeds, and timestamp > blockNumber reads 'locked for decades': locked balances never become withdrawable in the UI.
  3. Display prose is event-sourced (BlueprintDefinitionRecorded → indexer; storage zeroed) — indexer side handled in feat(indexer): decode 0.18 event-sourced blueprint definitions + binary versions tnt-core#196.

Solution

getTntCoreRevisionByChainId(chainId): 'legacy' | 'v018' in dapp-config, keying exactly the divergent reads:

  • readBlueprintCore() — picks the right getBlueprint tuple ABI and sources the count from blueprintOperatorCount on v018 (already in the synced ABI). Used by both fetchBlueprintsOnChain sites + useBlueprintOwner.
  • Withdraw lock check compares against now-seconds on v018, block number on legacy.

Zero behavior change today: every wired chain returns 'legacy' (Base Sepolia probed live — blueprintDefinitionHash(0) reverts FunctionNotFound = pre-#193 deployment; local anvil is post-#193/pre-#194 which behaves 'legacy' on every branched surface). A 0.18 cutover flips the revision in the same change that updates the chain's addresses.

Verification

nx affected lint / typecheck / test (--uncommitted): all green
live probe: cast call blueprintDefinitionHash(0) on Base Sepolia 0x8299d6… → FunctionNotFound (pre-0.18 confirmed)

Not included (deliberate): full ABI resync — the legacy getBlueprint entry must keep decoding live chains; resync + revision flip + address bump land together at cutover.

…ion flag

0.18 chains are greenfield redeploys with three consumer-visible breaks
the dapp would hit silently:
- getBlueprint dropped operatorCount from the struct (7->6 fields): the
  fixed-ABI decode throws and the catalog chain-fallback renders empty.
- MultiAssetDelegation LockInfo's expiryBlock slot now carries a unix
  TIMESTAMP (same position/type, decode silently succeeds): comparing it
  against a block number reads 'locked for decades' and locked balances
  never become withdrawable in the UI.
- Display prose is event-sourced (indexer path; storage zeroed).

Add getTntCoreRevisionByChainId ('legacy' | 'v018') to dapp-config and
key the three divergent reads on it: readBlueprintCore() picks the
right getBlueprint tuple + blueprintOperatorCount view (both fetch
sites + the binary-upgrade owner gate), and the withdraw lock check
compares against now-seconds on v018. Every wired chain is 'legacy'
today (Base Sepolia probed live: blueprintDefinitionHash reverts =
pre-#193), so this is zero behavior change until a chain's addresses
are flipped to a 0.18 redeploy — flip the revision in that same change.
@drewstone drewstone requested a review from AtelyPham as a code owner July 3, 2026 04:01

@tangletools tangletools left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Auto-approved drewstone PR — 644b3449

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-03T04:01:30Z

@tangletools tangletools left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Value Audit — sound

Verdict sound
Concerns 2 (1 low, 1 weak-concern)
Heuristic 0.0s
Duplication 0.0s
Interrogation 135.1s (2 bridge agents)
Total 135.1s

💰 Value — sound

Adds a per-chain tnt-core revision flag that keys two divergent reads (getBlueprint ABI shape, LockInfo timestamp-vs-blocknumber) for the upcoming 0.18 cutover, consolidating three duplicated getBlueprint decodes into one revision-aware helper — zero behavior change today, correct grain, no better a

  • What it does: Introduces getTntCoreRevisionByChainId(chainId): 'legacy'|'v018' in dapp-config (contracts.ts:154) that currently returns 'legacy' for every chain. It keys two reads that diverge across the tnt-core 0.18 contract revision: (1) readBlueprintCore (new file) selects either the existing 7-field getBlueprint tuple (legacy, operatorCount in-struct) or a new 6-field tuple + the already-synced `blue
  • Goals it achieves: (1) Prevent silent breakage when a chain flips to tnt-core 0.18 greenfield redeploys: a dropped struct field throws inside per-id try/catch and empties the blueprint catalog, and a redefined LockInfo slot makes locked balances read as locked-for-decades. (2) De-risk the cutover by making it a single config flip tied to the address bump, not an ABI resync that would break live chains. (3) Collatera
  • Assessment: Good change on its merits. The revision flag follows the exact get*ByChainId switch grain that contracts.ts already uses for addresses — no new mechanism introduced. The readBlueprintCore consolidation is a real DRY improvement independent of the revision work: verified via grep that every functionName: 'getBlueprint' call site (libs + apps) now routes through it, leaving zero orphaned direc
  • Better / existing approach: none — this is the right approach. Searched: (a) dapp-config for any existing contract-version/revision/ABI-variant concept — found none, so getTntCoreRevisionByChainId is not duplicative; (b) the full repo for other functionName: 'getBlueprint' decodes — all three former sites are migrated to the helper, none missed; (c) runtime auto-detection as an alternative — viable for the blueprint stru
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 2
  • Bridge warning: opencode/kimi-for-coding/k2p7: bridge stream ended without value-audit content

🎯 Usefulness — sound

A well-targeted, consolidation-positive fix that routes three duplicated getBlueprint decode sites through one revision-aware helper and branches the withdraw lock comparison by chain revision; every new surface is reachable today via the active chain-read fallback path.

  • Integration: Fully wired and reachable. readBlueprintCore is consumed by all three places that previously inlined the getBlueprint decode: fetchBlueprintsOnChain (fetchBlueprintsOnChain.ts:65), fetchBlueprintByIdOnChain (:143), and useBlueprintOwner (BlueprintVersionsPanel.tsx:99). Those are reached by useBlueprints.ts:420/478 and :587/730 — the indexer-down fallback that is the ACTIVELY-USED path on testnet t
  • Fit with existing patterns: Fits the codebase grain and improves on what it replaces. getTntCoreRevisionByChainId sits directly beside the established getContractsByChainId switch in the same file (contracts.ts:167), using the identical chain-keyed-dispatch pattern. readBlueprintCore consolidates what was triplicated getBlueprint decode logic (two copies in fetchBlueprintsOnChain + one in BlueprintVersionsPanel) into one typ
  • Real-world viability: Holds up on the paths that fire today and the ones that will fire post-cutover. The legacy branch is byte-identical to prior behavior (same TANGLE_ABI, same fields). The v018 two-read Promise.all is fully covered by the existing per-id try/catch in all three consumers (fetchBlueprintsOnChain.ts:107, fetchBlueprintByIdOnChain.ts:179, BlueprintVersionsPanel.tsx:106), so a revert on either sub-read d
  • Model: opencode/zai-coding-plan/glm-5.2
  • Bridge attempts: 1

🔎 Heuristic Signals

🟡 Cruft: magic number added apps/tangle-dapp/src/pages/staking/withdraw/index.tsx

  •  const nowSeconds = BigInt(Math.floor(Date.now() / 1000));
    

💰 Value Audit

🟡 Inlined v018 ABI fragment will need reconciliation with synced ABI at cutover [maintenance] ``

GET_BLUEPRINT_V018_ABI is defined locally in readBlueprintCore.ts:15 rather than in the synced libs/tangle-shared-ui/src/abi/tangle.ts. This is deliberate and correctly documented (the legacy 7-field entry at tangle.ts:1336 must keep decoding live chains, so a full resync now would break them), and the PR body explicitly scopes the resync+flip+address-bump as one future change. Reviewer note only: when that cutover lands, the local fragment should be removed in favor of the resynced entry so two


What this audit checks

It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.

Pass What it asks
Heuristic Vague title? Whitespace-only or cruft-bearing diff? (content signals only)
Duplication Do added function/class names already exist elsewhere in the repo?
Value Audit What does it do? What goal does it achieve? Is it good? Better architecture or already-exists?
Usefulness Audit Does it integrate and fit? Will it hold up in real use and actually get used?

Findings are concerns, not blocks — the human reviewer decides what to do with them.

value-audit · 20260703T042637Z

@drewstone drewstone merged commit 1466534 into develop Jul 3, 2026
10 checks passed
@drewstone drewstone deleted the fix/tnt-core-018-compat branch July 3, 2026 04:27
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