Skip to content

feat(0.19): support tnt-core 0.19 behind the per-chain revision flag (staggered)#3316

Merged
drewstone merged 2 commits into
developfrom
feat/tnt-core-0.19
Jul 8, 2026
Merged

feat(0.19): support tnt-core 0.19 behind the per-chain revision flag (staggered)#3316
drewstone merged 2 commits into
developfrom
feat/tnt-core-0.19

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Adds tnt-core 0.19 support behind the existing per-chain revision flag — staggered, not a hard cutover: 0.18 and 0.19 both work per-chain, and no chain is flipped to v019 (getTntCoreRevisionByChainId still returns legacy everywhere). Flip a chain when its 0.19 contracts deploy.

Changes

  • abi/: re-synced from canonical tnt-core 0.19 (removed blueprintSources/canScheduleExit; reportUri/disputeReason gone from structs; shorter getSlashProposal tuple).
  • dapp-config: 'v019' added to TntCoreRevision.
  • useBinaryVersions / trustScore: binaryUri/reportUristring | null; v019 read ABIs drop the URI slots; URI null on v019 (comes from the indexer — see tnt-core indexer PR that adds attestation reportUri).
  • useSlashing: v018 vs v019 getSlashProposal tuple ABIs + revision branch; proposedAt/disputeReason/disputedAt default on v019 (indexer-sourced); buildSlashTimeline tolerates proposedAt=0.
  • useCanScheduleExit: legacy direct-read kept; v019 fail-closed default.
  • readBlueprintCore: dedicated legacy getBlueprint ABI (0.19 dropped operatorCount from it).
  • UI: null binaryUri/reportUri render a graceful "unavailable on this chain".
  • Write paths (publishBinaryVersion/attestBinaryVersion still pass the URIs as params) unchanged.

Verified

nx typecheck tangle-dapp + tangle-cloud pass; trustScore unit tests 11/11.

…(staggered)

Re-synced ABIs to tnt-core 0.19 and made every 0.19 break site revision-aware so 0.18
and 0.19 both work per-chain — NO hard cutover, no chain flipped to v019 yet.

- dapp-config: add 'v019' to TntCoreRevision (getTntCoreRevisionByChainId stays 'legacy').
- abi/*: re-synced from canonical tnt-core 0.19 (blueprintSources/canScheduleExit/reportUri/
  disputeReason gone; shorter getSlashProposal tuple).
- useBinaryVersions: BinaryVersion.binaryUri / Attestation.reportUri -> string | null;
  v019 read ABIs (URI slots dropped); URI null on v019 (indexer-sourced follow-up).
- useSlashing: v018 vs v019 getSlashProposal tuple ABIs + revision branch in the normalizer
  (proposedAt/disputeReason/disputedAt default to 0/null on v019); buildSlashTimeline tolerates
  proposedAt=0.
- useCanScheduleExit: legacy direct-read kept; v019 fail-closed default.
- readBlueprintCore: dedicated legacy getBlueprint ABI (0.19 dropped operatorCount from it).
- UI: null binaryUri/reportUri render a graceful "unavailable on this chain".
Write paths (publishBinaryVersion/attestBinaryVersion still take the URIs as params) unchanged.

Verified: nx typecheck tangle-dapp + tangle-cloud pass; trustScore tests 11/11.
@drewstone drewstone requested a review from AtelyPham as a code owner July 7, 2026 20:53

@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 — 0aff75e9

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-07T20:53:26Z

@tangletools

Copy link
Copy Markdown
Contributor

❌ Needs Work — 0aff75e9

Review health 100/100 · Reviewer score 9/100 · Confidence 75/100 · 10 findings (1 critical, 1 high, 5 medium, 3 low)

deepseek kimi-code aggregate
Readiness 9 62 9
Confidence 75 75 75
Correctness 9 62 9
Security 9 62 9
Testing 9 62 9
Architecture 9 62 9

Reviewer score is advisory once the run is complete and the verdict has no blockers.

Full multi-shot audit completed 3/3 planned shots over 12 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 3/3 planned shots over 12 changed files. Global verifier still owns final merge decision.

Blocking

🟣 CRITICAL expiryBlockexpiryTimestamp ABI rename silently breaks all staking withdrawal lock displays — libs/tangle-shared-ui/src/abi/multiAssetDelegation.ts

The getLocks output struct renamed field 2 from name: 'expiryBlock' to name: 'expiryTimestamp'. Viem decodes return data using ABI component names as keys, so the decoded result will contain expiryTimestamp not expiryBlock. Consumer apps/tangle-dapp/src/pages/staking/withdraw/index.tsx:299 destructures expiryBlock, which becomes undefined. The lock-is-active checks at lines 310-311 (lock.expiryBlock > nowSeconds / lock.expiryBlock > currentBlockNumber) both evaluate undefined > number = false, making all locks appear unlocked. On v019 this misleads users; on legacy/v018 it also breaks because the ABI is shared across a

🔴 HIGH ABI type mismatch risk for operatorCount on legacy chains — libs/tangle-shared-ui/src/data/blueprints/readBlueprintCore.ts

The new GET_BLUEPRINT_LEGACY_ABI fragment types operatorCount as uint64 at line 28. The old synced TANGLE_ABI (pre-PR, file line ~1289) typed the same struct field as uint32. If the actual legacy Solidity struct uses uint32, decoding with the uint64 ABI will mis-read the subsequent fields (membership, pricing, active). The PR's README for this section states the fragment replaces the synced ABI for legacy reads but does not explain the type size change. Verify against the actual legacy contract Types.Blueprint struct definition in tnt-core.

Other

🟠 MEDIUM v019 documentation omits LockInfo/expiryTimestamp semantics — libs/dapp-config/src/contracts.ts

The v018 bullet (lines 146-147) documents that MultiAssetDelegation LockInfo.expiryBlock is a unix TIMESTAMP, but the v019 bullet (lines 148-157) does not state whether v019 preserves that semantics or what the field is named. In the same PR, libs/tangle-shared-ui/src/abi/multiAssetDelegation.ts renames the ABI output from expiryBlock to expiryTimestamp. apps/tangle-dapp/src/pages/staking/withdraw/index.tsx still reads lock.expiryBlock and only treats 'v018' as timestamp semantics, so a future chain flip to v019 will compa

🟠 MEDIUM multiAssetDelegation ABI field rename breaks existing locked-balance caller — libs/tangle-shared-ui/src/abi/multiAssetDelegation.ts

The LockInfo component was renamed from expiryBlock to expiryTimestamp. The consumer in apps/tangle-dapp/src/pages/staking/withdraw/index.tsx:299-311 still accesses lock.expiryBlock. Because viem attaches named tuple properties from ABI component names, lock.expiryBlock becomes undefined after this ABI change, making undefined > bigint always false and summing locked balances to zero for all users. Unlike readBlueprintCore/useSlashing/useBinaryVersions, this read was not given a revision-aware ABI fragment, so legacy/v018 callers break too. Fix: either update the withdraw page to use expiryTimestamp (with revision-aware comparison) or provide a LockInfo ABI fragment selected by TntCoreRevision and keep the synced ABI for v019 only.

🟠 MEDIUM blueprintSources function renamed to blueprintSourcesHash with structural output change — libs/tangle-shared-ui/src/abi/tangle.ts

The blueprintSources(uint64) view was renamed to blueprintSourcesHash(uint64) and its output changed from a complex BlueprintSource[] struct tuple to a single bytes32. No in-repo consumers found for either name. External consumers (scripts, indexers, other UIs importing TANGLE_ABI) that call blueprintSources will hit a 'function not found in ABI' error post-update. The setBlueprintSources write function at the new entries (lines 3627-3780) uses the same BlueprintSource[] struct as a calldata param, so the write path is unchanged — only the read path changed. Document the breaking rename in the PR body.

🟠 MEDIUM canScheduleExit view removed from synced ABI — v019 fallback blocks all exit scheduling UI — libs/tangle-shared-ui/src/abi/tangle.ts

The canScheduleExit(uint64,address) view was removed from the synced ABI and replaced by the unrelated cancelBlueprintTransfer(uint64) nonpayable. useCanScheduleExit.ts handles this correctly with a local CAN_SCHEDULE_EXIT_ABI fragment for legacy/v018 and a fail-closed { canExit: false, reason: 'Exit eligibility unavailable on this chain' } default for v019. The consumer OperatorExitPanel.tsx:318 shows the reason text as a yellow warning, blocking the 'Schedule Exit' button. This is safe (fail-closed) but means ALL operators on v019 cannot schedule exits through the UI until the follow-up getExitStatus-based eligibility check is wired. Add a TODO linking to the tracking issue.

🟠 MEDIUM No tests for new revision-aware read branches — libs/tangle-shared-ui/src/data/blueprints/useBinaryVersions.ts

The PR adds revision-gated ABI selection in readBlueprintCore, useSlashing (getSlashProposal), and useBinaryVersions, plus a v019-only degradation in useCanScheduleExit. None of these branches have unit or integration tests. The only test in the changed set remains trustScore.spec.ts, which does not exercise ABI selection or tuple normalization. Add tests that verify decoding and normalization for both the legacy/v018 full tuples and the v019 short tuples, and that binaryUri/reportUri are nulled correctly on v019.

🟡 LOW Default BinaryUpgradeABI export still carries URI-bearing read tuples — footgun for v019 reads — libs/tangle-shared-ui/src/abi/tangleBinaryUpgrade.ts

The default export default ABI at line 470 still has binaryUri (string) and reportUri (string) in the getBinaryVersion, effectiveBinaryVersion, getAttestation, and listAttestations output tuples (lines 40-44, 67-73, 186-199, 215-231). The PR guards against misuse via binaryReadAbiFor() in useBinaryVersions.ts:75-78, but any direct consumer of BinaryUpgradeABI that calls these views on v019 will decode misaligned data. Consider adding a runtime guard or removing the URI slots from

🟡 LOW No test coverage for revision-aware ABI selection and tuple normalization — libs/tangle-shared-ui/src/data/graphql/useSlashing.ts

The PR adds complex branching logic across 4 files (readBlueprintCore.ts, useSlashing.ts, useBinaryVersions.ts, useCanScheduleExit.ts) that selects ABIs and normalizes tuples based on TntCoreRevision. normalizeOnChainSlashProposal alone has 15 positional fallback branches gated by isV019. None of this logic has test coverage. A positional index error (e.g., proposal?.[6] vs proposal?.[7] for executeAfter) would silently produce wrong data in the UI. The existing test files (useCancelExitTx.spec.tsx, useScheduleExitTx.spec.tsx) only mock useCanScheduleExit — they don't test the revision-aware path.

🟡 LOW v019 canScheduleExit always fails closed, disabling UI exit scheduling — libs/tangle-shared-ui/src/data/services/useCanScheduleExit.ts

For TntCoreRevision === 'v019' the hook returns { canExit: false, reason: 'Exit eligibility unavailable on this chain' }. This is an intentional fail-closed degradation noted as a follow-up, but it means operators on any v019 chain cannot schedule an exit through the UI even when eligible. Ensure the follow-up to derive eligibility from getExitStatus + exit config is tracked before v019 chains are enabled.


tangletools · 2026-07-07T21:13:32Z · trace

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

❌ 2 Blocking Findings — 0aff75e9

Full multi-shot audit completed 3/3 planned shots over 12 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 3/3 planned shots over 12 changed files. Global verifier still owns final merge decision.

Full immutable report for this review: trace

Summary comment for this run: full summary


tangletools · 2026-07-07T21:13:32Z · immutable trace

@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 0 (none)
Heuristic 0.0s
Duplication 0.0s
Interrogation 986.0s (2 bridge agents)
Total 986.0s

💰 Value — sound

Adds tnt-core 0.19 read-path support behind the existing per-chain revision flag (no chain flipped), extending the exact pattern landed one commit earlier by the 0.18 PR — coherent, safe, well-documented.

  • What it does: Re-syncs the canonical ABIs to the tnt-core 0.19 shape (which dropped several on-chain storage fields — binaryUri/reportUri from structs, proposedAt/disputeReason/disputedAt from the slash tuple, operatorCount already gone in 0.18, and removed the canScheduleExit view entirely — all now event-sourced), then adds local legacy ABI fragments + revision-keyed read-ABI selectors so legacy/v018 chains k
  • Goals it achieves: (1) Forward-compat with tnt-core 0.19 contracts before any chain deploys them, so a flip is a one-line config change later. (2) Keep 0.18 and 0.19 both working per-chain — staggered, not a hard cutover. (3) Avoid the decode-misalignment footgun where a 0.18 (URI-bearing) tuple decoded against 0.19 returndata silently shifts every field after the dropped slot. (4) Degrade gracefully where 0.19 remo
  • Assessment: This is a textbook extension of the revision-flag pattern established by PR #3315 (0.18) — same TntCoreRevision type, same getTntCoreRevisionByChainId selector, same co-located 'canonical ABI = latest + local legacy fragment + xxxReadAbiFor(chainId) ternary' shape, now in 3 consumer modules (readBlueprintCore:46-67, useBinaryVersions:75-78, useSlashing:812-815) plus the canScheduleExit:85 branch.
  • Better / existing approach: none — this is the right approach. Searched for a centralized ABI-selection abstraction (rg 'revisionAbi|abiFor|abiByRevision|versionedAbi|selectAbi') and found only the same per-module xxxReadAbiFor pattern this PR uses — there is no existing registry to extend. A shared selectAbiByRevision(chainId, v019Abi, legacyAbi) helper would wrap a 3-line ternary without removing the contract-specific frag
  • 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

Clean v019 compatibility behind the existing per-chain revision flag — extends the v018 pattern consistently, all integrations are reachable once a chain flips, error paths degrade gracefully, and no chain is flipped yet.

  • Integration: All surface is reachable. The getTntCoreRevisionByChainId returns 'legacy' everywhere now, but every consumer — useBinaryVersions (binaryReadAbiFor at useBinaryVersions.ts:75), useSlashProposalDetails (slashProposalReadAbiFor at useSlashing.ts:812), useCanScheduleExit (useCanScheduleExit.ts:85), readBlueprintCore (readBlueprintCore.ts:89), and the expiryTimestamp rename in multiA
  • Fit with existing patterns: Extends the established per-chain revision pattern introduced in #3315 (v018) without deviating. Same mechanism: TntCoreRevision union + getTntCoreRevisionByChainId switch + local ABI fragments selected by revision + safe defaults on the v019 path. The staking/withdraw page (apps/tangle-dapp/src/pages/staking/withdraw/index.tsx:309) already branches on v018 the same way, and `readBlueprintCo
  • Real-world viability: Robust. Every chain read is wrapped in try/catch with safe defaults (e.g., useCanScheduleExit returns canExit: false on v019; fetchBinaryVersions catches per-version failures). normalizeBinaryVersion / normalizeAttestation use ?? (not ||) to correctly handle empty-string URIs. buildSlashTimeline tolerates proposedAt = 0 on v019 with a clear message. The ABI selection prevents the
  • Model: opencode/deepseek/deepseek-v4-pro
  • Bridge attempts: 3
  • Bridge warning: opencode/zai-coding-plan/glm-5.2: bridge stream ended without value-audit content; opencode/kimi-for-coding/k2p7: bridge stream ended without value-audit content

No concerns — sound change, no better or existing approach found. ✅


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 · 20260707T212013Z

…n-read tests

Addresses the blocking review findings on the 0.19 adoption:

- CRITICAL: withdrawal lock displays were reading LockInfo.expiryBlock, which the synced
  0.19 ABI renamed to expiryTimestamp — viem keys decoded tuples by ABI component name, so
  every read was undefined and all locks showed as unlocked. Read expiryTimestamp on every
  revision; branch the comparison basis: legacy stores a BLOCK NUMBER (vs currentBlockNumber),
  0.18/0.19 store a unix TIMESTAMP (vs wall-clock seconds). Same uint64 slot, only the value
  semantics differ.
- HIGH: legacy getBlueprint ABI had operatorCount as uint64; the real pre-#194 struct is
  uint32. Wrong width mis-aligned the following membership/pricing/active fields on legacy.
- MED: blueprintSources -> blueprintSourcesHash — grep confirms zero dapp callers of either;
  documented the rename (the setBlueprintSources WRITE is unchanged).
- MED: canScheduleExit view removed -> fail-closed on v019; added TODO(v019) markers at the
  fallback and the exit-button block to track the getExitStatus follow-up.
- MED: extracted the pure decode/ABI-select logic out of the wagmi-importing hooks into
  wagmi-free modules (slashProposal.ts, binaryVersion.ts, re-exported, zero consumer change)
  and added 11 revision tests: v019 nulls binaryUri/reportUri without throwing; v018 decodes
  the full 14-field slash tuple; v019 short-tuple positional decode has no index slip.
- MED: documented the v019 LockInfo/expiryTimestamp semantics in getTntCoreRevisionByChainId.

Verified: nx typecheck (tangle-dapp + tangle-cloud) pass; nx test tangle-shared-ui 85 pass
(11 new + 74 existing); prettier clean; no .expiryBlock runtime reads remain.

@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 — 115a2b61

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-08T02: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 1 (1 low)
Heuristic 0.0s
Duplication 0.0s
Interrogation 284.3s (2 bridge agents)
Total 284.3s

💰 Value — sound

Extends the existing per-chain TntCoreRevision flag (legacy/v018 from PR #3315) with v019, adding revision-aware ABI selectors and tuple normalizers for the read surfaces 0.19 slimmed — clean, well-tested, ships inert behind the flag.

  • What it does: Adds 'v019' to the TntCoreRevision union (contracts.ts:170) so the dapp can read tnt-core 0.19 contracts. 0.19 moved several on-chain storage fields off-chain (event-sourced): getSlashProposal dropped proposedAt/disputeReason/disputedAt; getBinaryVersion/effectiveBinaryVersion dropped binaryUri; listAttestations/getAttestation dropped reportUri; blueprintSources(uint64)->BlueprintSource[] became b
  • Goals it achieves: (1) Let the dapp decode tnt-core 0.19 contract reads correctly once a chain is flipped — without that, every slash-proposal, binary-version, attestation, and blueprint-source read would either throw (tuple-length mismatch) or silently mis-align fields. (2) Keep legacy + v018 chains byte-identical (staggered rollout — flip a chain only when its 0.19 contracts deploy). (3) Degrade gracefully where 0
  • Assessment: A textbook extension of the pattern PR #3315 established. That PR created TntCoreRevision + getTntCoreRevisionByChainId, readBlueprintCore.ts with revision-branching ABI selection, and the withdraw lock-basis branch. This PR applies the identical pattern to the remaining read surfaces 0.19 touched — same shape (pure wagmi-free decode module split out for unit testing, local legacy-tuple ABI fragme
  • Better / existing approach: none — this is the right approach. Searched for an existing revision→ABI registry to reuse: the codebase has four revision-branching sites (readBlueprintCore.ts:93, binaryVersion.ts:48, slashProposal.ts:129, useCanScheduleExit.ts:88) and each branches for a DIFFERENT tuple shape, so a central registry would just replace four ternaries with four Map<revision,abi> lookups — no real win, and it would
  • 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 — error

usefulness agent produced no parseable value-audit JSON.

  • Model: opencode/deepseek/deepseek-v4-pro
  • Bridge attempts: 3
  • Bridge error: opencode/zai-coding-plan/glm-5.2: bridge stream ended without value-audit content; opencode/kimi-for-coding/k2p7: bridge stream ended without value-audit content; opencode/deepseek/deepseek-v4-pro: bridge stream ended without value-audit content

🔎 Heuristic Signals

🟡 Cruft: todo added apps/tangle-cloud/src/pages/services/[id]/OperatorExitPanel.tsx

  •      TODO(v019): `useCanScheduleExit` fail-closes to `{ canExit: false }` on
    

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 · 20260708T020807Z

@drewstone drewstone merged commit 97b9806 into develop Jul 8, 2026
10 checks passed
@drewstone drewstone deleted the feat/tnt-core-0.19 branch July 8, 2026 02:15
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