Skip to content

feat: migrate to Tangle evm#3090

Draft
drewstone wants to merge 105 commits intodevelopfrom
v2
Draft

feat: migrate to Tangle evm#3090
drewstone wants to merge 105 commits intodevelopfrom
v2

Conversation

@drewstone
Copy link
Contributor

@drewstone drewstone commented Dec 23, 2025

No description provided.

drewstone and others added 30 commits December 10, 2025 01:32
- Add --disable-code-size-limit flag to Anvil for oversized contracts
- Add --private-key and --non-interactive flags to forge deploy
- Clear forge broadcast cache before deployment
- Verify deployment by checking deployer nonce and contract code
- Update GraphQL queries to use Envio/Hasura syntax (PascalCase entities,
  limit/offset, order_by, where with operators)
- Update indexing progress to use chain_metadata table
- Rename block-based queries to timestamp-based

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update useOperatorBlueprints to accept EVM Address type instead of SubstrateAddress
- Fix SelectBlueprintsModal to use viem Address type
- Update operator page to use isAddress from viem for validation
- Convert deprecated tx hook stubs to return no-op functions instead of null
  to avoid TypeScript "never" type narrowing issues
- Add proper return type annotations to prevent type narrowing
- Fix DepositForm setValue type to match react-hook-form type

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Create stub implementations for service approve/reject/terminate/register
hooks until proper Tangle contract ABI integration is implemented.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add comments to empty reset functions to satisfy eslint no-empty-function rule.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update OperatorSelectionStep to use GraphQL hooks (useOperatorMap, useRestakeAssets)
- Fix operator mapping to use EVM types (Address instead of SubstrateAddress)
- Update AssetConfigurationStep to use useRestakeAssets with proper typing
- Fix instance tables to use correct Blueprint type and property names
- Update confirmation modals for EVM Blueprint type compatibility
- Fix TotalValueLocked to use assetPositions instead of deposits
- Add useOperatorBatchRegisterTx stub hook for batch registration
- Change BigInt literals from 0n to BigInt(0) for ES2019 compatibility
- Fix property name mismatches (logo -> imgUrl, serviceCount -> instancesCount)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add wagmi connectors (injected, coinbaseWallet, safe, walletConnect)
- Enable EIP-6963 multi-injected provider discovery
- Add new wallets: Coinbase, Safe, Talisman, Trust Wallet, Keplr
- Create wallet icons for new wallets
- Improve connector matching logic with multiple fallback strategies
- Update local RPC endpoint to anvil port (8545)
- Fix VaultsHightlightCard React key warning
- Add debug logging for available connectors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update wallet connection system to use RainbowKit while preserving
existing UI components. This provides:
- Better wallet detection via EIP-6963
- Automatic support for MetaMask, Coinbase, WalletConnect, etc.
- Simplified provider setup with RainbowKitProvider

Changes:
- Update wagmi config to use RainbowKit's getDefaultConfig
- Add RainbowKitProvider to tangle-dapp and tangle-cloud providers
- Update ConnectWalletButton to use wagmi hooks (useAccount, useDisconnect)
- Update WalletDropdown to use wagmi for disconnect
- Remove unused ConnectWalletModal (replaced by RainbowKit modal)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update WalletDropdown to match original UI design with wallet icons
- Add walletIcons helper to map wagmi connectors to wallet icons
- Update NetworkSelectorDropdown to use wagmi hooks (useChainId, useSwitchChain)
- Remove dependency on deprecated useWebContext for network switching
- Simplify TopNavigationPanel by removing unused props

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…in switcher

- Remove Tangle Mainnet/Testnet from wagmiChains (they use Substrate)
- Add Anvil Local, Base, and Base Sepolia as primary chains
- Create ChainSelectorButton using RainbowKit's useChainModal
- Replace custom NetworkSelectorDropdown with RainbowKit chain modal

This aligns with the pure EVM architecture using RainbowKit for
wallet and chain management.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove custom bridge components (BridgeContainer, BridgeConfirmationModal, etc.)
- Remove bridge contexts (BridgeHyperlaneContext, BridgeTxQueueContext)
- Remove bridge hooks (useHyperlaneQuote, useHyperlaneTransfer, etc.)
- Remove bridge utilities
- Add Li.Fi widget for cross-chain bridging
- Update dependencies: wagmi@2.19.5, viem@2.37.0, @tanstack/react-query@5.90.12
- Simplify providers by removing bridge-specific providers
- Remove BridgeTxQueueDropdown from Layout

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add NetworkGuard component to show wrong network message on Dashboard,
  Restake, and Liquid Staking pages when not on Base/Base Sepolia/Local
- Fix LiFi widget styling: apply Satoshi font family to all MUI variants,
  add component overrides, and force font via CSS
- Fix dropdown z-index (z-50) to appear above LiFi widget
- Fix ClaimRewardsDropdown button height to match adjacent buttons
- Fix NativeRestakeContainer to hide page title when showing wrong network
- Fix Switcher import in create-vault form

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Scripts were moved to scripts/local-env/ for shared use across apps.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Native Restaking:
- Add ValidatorPod and ValidatorPodManager ABIs
- Create native restaking UI components (CreatePodCard, DelegationCard, etc.)
- Add hooks for validator pod management
- Add wrong network alert for unsupported chains

Migration Claims:
- Add TangleMigration contract integration
- Create claim eligibility and proof generation hooks
- Add SubstrateKeyInput component for SS58 address entry
- Include merkle tree utilities and snapshot data
- Add deploy-migration.sh script for local testing

Data Layer Improvements:
- Add on-chain fallback for restake assets when indexer unavailable
- Add Envio health check utility
- Add LOCAL_MOCK_TOKENS config for local development
- Add unified restake assets hook with automatic fallback

EVM Support:
- Add Ethereum mainnet and Holesky testnet chains
- Add contract addresses for new chains

Liquid Staking:
- Add vault tables and user positions components
- Add liquid delegation hooks and data fetching

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add comprehensive README with deployment and proof generation guide
- Fix curve25519-dalek patch branch name (patch-curve25519-v4.1.3)
- Pin serde to <1.0.218 to avoid alloy-consensus compatibility issue
- Add HashableKey import for vkey.bytes32() method
- Fix test assertions to use PublicValues.pubkey field

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add claim-relayer service for gasless claims via gas sponsorship
- Add deployment scripts (deploy.sh, setup-local-test.sh)
- Add extractEvmClaims.ts for EVM snapshot processing
- Update generateMerkleTree.ts with improved structure
- Add SubstrateWalletSelector component for Polkadot.js integration
- Add gitignore entries for large generated files (migration-proofs.json)
- Add gitignore for Foundry and Rust build artifacts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove Substrate-specific staking components (ValidatorTable,
  NominationsTable, NominationValidatorTables)
- Remove Substrate balance hooks and contexts (BalancesContext,
  useTransferTx, usePendingEvmBalance, etc.)
- Remove legacy restaking containers (JoinOperatorsModal,
  SelectOperatorModal, WithdrawModal, etc.)
- Update types from BN/SubstrateAddress to bigint/Address (viem)
- Migrate data hooks to use GraphQL/Envio indexer instead of
  Substrate RxJS hooks
- Rename Evm-suffixed components to remove suffix:
  - UserRestakingOverviewEvm → UserRestakingOverview
  - ProtocolStatisticCardEvm → ProtocolStatisticCard
  - OperatorsTableEvm → OperatorsTable
- Update imports across tangle-dapp and tangle-cloud apps
- Remove unused Substrate utility functions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The activity generator was hardcoded to use chain ID 84532 (Base Sepolia)
while Anvil runs on 31337 (default). This caused the script to hang as
viem would reject transactions with mismatched chain IDs.

Now uses a configurable CHAIN_ID that defaults to 31337 to match the
Anvil configuration in start-local-env.sh and config.local.yaml.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Removed `forge clean` which was forcing full recompilation on every run.
Now only the broadcast cache is cleared (needed for deterministic contract
addresses), while compiled artifacts in `out/` are reused.

This significantly speeds up subsequent runs of the local environment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
… and blueprint features

- Add operator management page with registration/unregister/slashing UI
- Add job submission form and job history table for service details
- Add rewards claiming page for operators
- Add developer earnings dashboard for blueprint owners
- Add blueprint creation wizard and management page
- Add advanced deployment options (approval model config)
- Create GraphQL hooks for jobs, rewards, operator management, slashing,
  blueprint management, and developer earnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add Anvil state caching to speed up local environment startup
- Add Hasura health check before db-setup to fix race condition
- Fix dynamic env var syntax for Envio RPC URL
- Add cache CLI commands (cache clear/save/status, reset)
- Fix lint errors in tangle-cloud and tangle-dapp (unused imports/vars)
- Update migration deployment scripts with improved logging
- Various code quality improvements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix operators query enum type (String -> restakingoperatorstatus)
- Add useDelegatorCount hook for accurate restaker count
- Update ProtocolStatisticCard to use unique restaker count
- Improve claim page error display with user-friendly messages
- Fix UserRestakingOverview to show "0" instead of "--" for zero values
- Change suffix from "ETH" to "TNT" in restaking overview

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add chain check in useProtocolConfig to skip contract calls on chains
  where restaking contracts aren't deployed
- Return sensible default values for protocol config on non-restaking chains
- Fix icon import in migration claim page (AlertLine -> Alert)
- Add asset list display on dashboard with token icons
- Improve useDynamicSVGImport to always fall back to default icons
- Remove debug console.log statements from restake asset hooks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix empty object type in useDelegatorCount to use Record<string, never>
- Prefix unused hasBalance variable with underscore in Actions.tsx
- Remove unused RestakeAsset import and chainId in TransferModal.tsx
- Move handleClose before handleTransfer to fix hook dependency
- Reorder imports in migration claim page to fix import/first rule

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Use useOptionalRestakeContext instead of useRestakeContext to prevent
errors during hot module reload when the context may temporarily be
unavailable. Shows a loading skeleton instead of throwing an error.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace skeleton loader with an animated spinner component matching
the Claim Migration page design. Features a gradient background,
spinning border animation, and descriptive text.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add token icons to RestakingAssetsTable with symbol lookup
- Add token icons to DepositForm asset selector and list modal
- Add token icons to DelegateForm asset selector and list modal
- Add token icons to UnstakeForm delegation selector and list modal
- Add token icons to WithdrawForm asset selector and list modal
- Use local token config to resolve symbol from token address

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Include native token (zero address) in potential tokens list
- Fetch native balance using wagmi useBalance hook
- Add native ETH to asset map with hardcoded metadata
- Handle case where only native token is enabled (no ERC20 tokens)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@netlify
Copy link

netlify bot commented Dec 23, 2025

Deploy Preview for tangle-dapp failed. Why did it fail? →

Name Link
🔨 Latest commit 4358797
🔍 Latest deploy log https://app.netlify.com/projects/tangle-dapp/deploys/698618e443445c00081cf2ec

@netlify
Copy link

netlify bot commented Dec 23, 2025

Deploy Preview for tangle-leaderboard failed. Why did it fail? →

Name Link
🔨 Latest commit 4358797
🔍 Latest deploy log https://app.netlify.com/projects/tangle-leaderboard/deploys/698618e41103ea0008541fc0

@netlify
Copy link

netlify bot commented Dec 23, 2025

Deploy Preview for tangle-cloud ready!

Name Link
🔨 Latest commit 4358797
🔍 Latest deploy log https://app.netlify.com/projects/tangle-cloud/deploys/698618e4cd183300094530c6
😎 Deploy Preview https://deploy-preview-3090--tangle-cloud.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@drewstone
Copy link
Contributor Author

drewstone commented Dec 23, 2025

Latest work summary @vutuanlinh2k2. Basically everything that's not dApp UI related should ideally be in tnt-core v2 branch not in dApp. You'll either want a local checkout or we can improve if it pulls the repo remotely to set things up. The local checkout should be at same directory level /path/to/tnt-core and /path/to/dapp.

  • Switched ABI sourcing to tnt-core exports and refreshed generated ABIs so facet methods are typed correctly in the dapp. (scripts/sync-tnt-core-
    assets.mjs, libs/tangle-shared-ui/src/abi/tangle.ts, libs/tangle-shared-ui/src/abi/multiAssetDelegation.ts, libs/tangle-shared-ui/src/abi/
    operatorStatusRegistry.ts, libs/tangle-shared-ui/src/abi/blueprintServiceManager.ts)
  • Removed the local claim relayer app from this repo and forced local/infra scripts to use ../tnt-core/apps/claim-relayer only. (scripts/local-env/
    start-local-env.sh, scripts/infra/start-infra.sh)
  • Earlier fixes already pushed: UI/UX improvements on restake tables/dropdown, token metadata/logo mapping, caching of restake assets, infra runner
    scripts, and typecheck cleanup across restake flows.

}

// Pick random account (skip account 0 which is the deployer)
const accountIndex = 1 + Math.floor(Math.random() * (ANVIL_ACCOUNTS.length - 1));

Check failure

Code scanning / CodeQL

Insecure randomness High

This uses a cryptographically insecure random number generated at
Math.random()
in a security context.

Copilot Autofix

AI about 1 month ago

In general, the fix is to stop using Math.random() and instead use a cryptographically secure source of randomness, such as Node.js’s crypto.randomInt or randomness derived from crypto.randomBytes, especially when generating values that affect security‑sensitive behavior. For random integers or weighted selections, crypto.randomInt avoids bias and is easy to substitute.

For this file, the best approach is:

  • Import Node’s built‑in crypto module.
  • Replace all usages of Math.random() that determine accounts or activity selection with calls to crypto.randomInt, preserving the same ranges and behavior.
    • Line 670: replace let random = Math.random() * totalWeight; with a secure random integer in [0, totalWeight). To minimize behavioral change, use crypto.randomInt(totalWeight) when totalWeight is a positive integer (the weights appear to be integer constants).
    • Line 682: replace 1 + Math.floor(Math.random() * (ANVIL_ACCOUNTS.length - 1)) with 1 + crypto.randomInt(ANVIL_ACCOUNTS.length - 1).
    • Lines 722–723: similarly replace the account selection for rawAccounts.
    • Line 729: replace Math.random() > 0.5 with a secure random bit via crypto.randomInt(2) === 1.

Concretely:

  • At the top of scripts/local-env/activity-generator.mjs, add import crypto from 'crypto'; (Node core module, no external dependency).
  • Update the specific lines called out above with equivalent crypto.randomInt logic, keeping all surrounding logic intact.
Suggested changeset 1
scripts/local-env/activity-generator.mjs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/scripts/local-env/activity-generator.mjs b/scripts/local-env/activity-generator.mjs
--- a/scripts/local-env/activity-generator.mjs
+++ b/scripts/local-env/activity-generator.mjs
@@ -17,6 +17,7 @@
 import { createPublicClient, createWalletClient, http, parseEther, parseUnits, encodeFunctionData } from 'viem';
 import { privateKeyToAccount } from 'viem/accounts';
 import { anvil } from 'viem/chains';
+import crypto from 'crypto';
 
 // Configuration
 const RPC_URL = process.env.RPC_URL || 'http://localhost:8545';
@@ -667,7 +668,7 @@
   });
 
   const totalWeight = availableActivities.reduce((sum, a) => sum + a.weight, 0);
-  let random = Math.random() * totalWeight;
+  let random = totalWeight > 0 ? crypto.randomInt(totalWeight) : 0;
   let selectedActivity = availableActivities[0].name;
 
   for (const activity of availableActivities) {
@@ -679,7 +680,7 @@
   }
 
   // Pick random account (skip account 0 which is the deployer)
-  const accountIndex = 1 + Math.floor(Math.random() * (ANVIL_ACCOUNTS.length - 1));
+  const accountIndex = 1 + crypto.randomInt(ANVIL_ACCOUNTS.length - 1);
 
   switch (selectedActivity) {
     case 'depositETH':
@@ -720,13 +721,13 @@
     case 'multiDeposit': {
       // Generate 3 random accounts, then deduplicate to prevent nonce collisions
       const rawAccounts = [1, 2, 3].map(() =>
-        1 + Math.floor(Math.random() * (ANVIL_ACCOUNTS.length - 1))
+        1 + crypto.randomInt(ANVIL_ACCOUNTS.length - 1)
       );
       const uniqueAccounts = [...new Set(rawAccounts)];
 
       // Mix of ETH and ERC20 deposits - each unique account processes sequentially
       await Promise.all(uniqueAccounts.map(async (idx) => {
-        if (Math.random() > 0.5 && state.tokensAvailable.length > 0) {
+        if (crypto.randomInt(2) === 1 && state.tokensAvailable.length > 0) {
           const tokenKey = randomChoice(state.tokensAvailable);
           return depositERC20Token(idx, tokenKey);
         } else {
EOF
@@ -17,6 +17,7 @@
import { createPublicClient, createWalletClient, http, parseEther, parseUnits, encodeFunctionData } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { anvil } from 'viem/chains';
import crypto from 'crypto';

// Configuration
const RPC_URL = process.env.RPC_URL || 'http://localhost:8545';
@@ -667,7 +668,7 @@
});

const totalWeight = availableActivities.reduce((sum, a) => sum + a.weight, 0);
let random = Math.random() * totalWeight;
let random = totalWeight > 0 ? crypto.randomInt(totalWeight) : 0;
let selectedActivity = availableActivities[0].name;

for (const activity of availableActivities) {
@@ -679,7 +680,7 @@
}

// Pick random account (skip account 0 which is the deployer)
const accountIndex = 1 + Math.floor(Math.random() * (ANVIL_ACCOUNTS.length - 1));
const accountIndex = 1 + crypto.randomInt(ANVIL_ACCOUNTS.length - 1);

switch (selectedActivity) {
case 'depositETH':
@@ -720,13 +721,13 @@
case 'multiDeposit': {
// Generate 3 random accounts, then deduplicate to prevent nonce collisions
const rawAccounts = [1, 2, 3].map(() =>
1 + Math.floor(Math.random() * (ANVIL_ACCOUNTS.length - 1))
1 + crypto.randomInt(ANVIL_ACCOUNTS.length - 1)
);
const uniqueAccounts = [...new Set(rawAccounts)];

// Mix of ETH and ERC20 deposits - each unique account processes sequentially
await Promise.all(uniqueAccounts.map(async (idx) => {
if (Math.random() > 0.5 && state.tokensAvailable.length > 0) {
if (crypto.randomInt(2) === 1 && state.tokensAvailable.length > 0) {
const tokenKey = randomChoice(state.tokensAvailable);
return depositERC20Token(idx, tokenKey);
} else {
Copilot is powered by AI and may make mistakes. Always verify output.
drewstone and others added 13 commits December 23, 2025 12:11
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
#3091)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…ation display (#3094)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
#3095)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@drewstone
Copy link
Contributor Author

🔍 PR Audit: Tangle EVM Migration (v2)

Auditor: CI Bot | Date: 2026-02-06 | PR Age: 45 days (draft)


📊 Overall Rating: 4/10

The migration scope is well-documented and architecturally sound, but execution gaps prevent this from being merge-ready.


📈 Migration Scope Analysis

Metric Value Assessment
Files Changed 725 ⚠️ Too large for single PR
Additions ~800k lines 🔴 Includes generated files (merkle proofs, ABIs)
Deletions ~34k lines ✅ Appropriate cleanup
Files Removed 289 ✅ Substrate provider removal
Test Files ~2 🔴 Critical gap

Key Changes:

  • ✅ New EVM ABIs: MultiAssetDelegation, BlueprintServiceManager, OperatorStatusRegistry, RewardVaults
  • ✅ SP1 ZK-based migration claim system documented
  • ✅ Local dev environment with Anvil + Envio indexer
  • ✅ Comprehensive parity docs (V2_MIGRATION_PARITY.md, TANGLE_CLOUD_CONTRACT_PARITY.md)
  • ⚠️ Many hooks are stubs (blocking user flows per contract parity doc)
  • 🔴 Substrate providers removed but not all consumers updated

🔴 Why This PR Is Stale (45 Days)

  1. CI Failures (Feb 4): Build 🏗️ ❌ | Test 🧪 ❌ | Clean 🧹 ❌
  2. Netlify Deploy Failures: tangle-dapp ❌ | tangle-leaderboard ❌ | tangle-cloud ✅
  3. External Dependency: Requires tnt-core repo at sibling directory (undocumented in PR)
  4. Incomplete Implementation: Per TANGLE_CLOUD_CONTRACT_PARITY.md, 5 features are broken stubs
  5. Scope Creep: Single PR attempting full Substrate→EVM migration

🚨 Critical Gaps

Gap Severity Impact
No test coverage 🔴 Critical 725 files with ~2 test files. Zero confidence in regression safety
Broken CI 🔴 Critical Cannot verify changes compile or pass existing tests
Stub hooks 🔴 Critical useOperatorBatchRegisterTx, useServicesApproveTx, etc. block user flows
No rollback strategy 🟡 High No documented plan if migration fails in production
No CHANGELOG 🟡 High Breaking changes not documented for consumers
Missing contract addresses 🟡 High dapp-config missing Base Sepolia MBSM, credits addresses

✅ What's Working Well

  1. Documentation: MIGRATION_CLAIM_PLAN.md (571 lines) and parity docs are excellent
  2. Architecture: ZK-based claim system using SP1 is well-designed
  3. Local Dev: scripts/local-env/ provides good DX for testing
  4. ABI Sync: Scripts to sync ABIs from tnt-core prevent drift
  5. Tangle Cloud: Only app with passing Netlify deploy

🗺️ Roadmap to 10/10 (Merge-Ready)

Phase 1: Stabilize (1-2 days)

  • Fix CI build failures - Get green builds first
  • Update tnt-core dependency docs - Add to PR description and README
  • Fix Netlify deploys - Resolve tangle-dapp and leaderboard failures

Phase 2: Split the PR (1 week)

  • PR 1: ABI + contract config changes only (~50 files)
  • PR 2: Remove Substrate providers + update consumers (~100 files)
  • PR 3: Migration claim UI + hooks (~50 files)
  • PR 4: Local dev scripts + generated artifacts (~500 files)
  • PR 5: Final integration + parity verification

Phase 3: Test Coverage (1-2 weeks)

  • Unit tests for new hooks: useContractWrite, useResilientReadContract, etc.
  • Integration tests for claim flow: Mock SP1 proofs, verify merkle validation
  • E2E tests: Operator registration → Service deployment → Job submission

Phase 4: Production Readiness (3-5 days)

  • Write CHANGELOG with breaking changes
  • Document rollback strategy - What if migration fails mid-deploy?
  • Complete stub hooks - Per TANGLE_CLOUD_CONTRACT_PARITY.md Flow 2-6
  • Verify contract addresses for testnet and mainnet
  • Add feature flags for gradual rollout

Phase 5: Review & Merge

  • Code review of split PRs
  • QA sign-off on tangle-cloud testnet
  • Merge to develop → staging verification → mainnet

💡 Recommendations

  1. Don't force-merge this PR - It's too large and too broken
  2. Cherry-pick working components into smaller PRs
  3. Prioritize operator registration flow - It blocks all downstream features
  4. Add CI job for tnt-core integration - Catch ABI drift early
  5. Consider feature branch strategy - developv2-phase-X ← feature PRs

📋 Checklist for Author

Before marking ready for review:
[ ] CI passing (Build, Test, Clean)
[ ] All 3 Netlify deploys passing
[ ] Test coverage >80% for new hooks
[ ] No stub implementations in critical paths
[ ] CHANGELOG updated
[ ] Rollback strategy documented
[ ] Contract addresses verified for all environments

This audit was generated by analyzing the PR diff, documentation, CI status, and contract parity docs. For questions, see the linked documentation files.

Drew Stone added 2 commits February 6, 2026 09:26
Removed ~580 lines of duplicate code from tangle-dapp and tangle-cloud
that already exist in tangle-shared-ui:

Components removed:
- ErrorMessage.tsx (from both apps)
- InputWrapper.tsx (from tangle-dapp)
- InputAction.tsx (from tangle-dapp)

Hooks removed:
- useInputAmount.ts (from tangle-dapp)

Utils removed:
- parseChainUnits.ts (from tangle-dapp)
- cleanNumericInputString.ts (from tangle-dapp)

Context removed:
- ErrorsContext/ directory (from tangle-dapp)

All imports updated to use @tangle-network/tangle-shared-ui/* paths.

Ref: PR #3090 Phase 2
@drewstone
Copy link
Contributor Author

🏗️ Phase 4: Architecture Review Assessment

Executive Summary

The v2 branch shows substantial progress in migrating from Substrate to EVM. The architecture is generally well-organized with a clear monorepo structure using Nx. However, there are some areas that need attention for cleanup and consistency.


✅ What's Good

1. Clean Monorepo Structure

  • Clear separation: apps/ (3 apps: tangle-dapp, tangle-cloud, leaderboard) and libs/ (10 shared libraries)
  • Nx build system is properly configured with caching and dependency tracking
  • Workspace structure follows best practices

2. EVM Migration Progress

  • libs/dapp-config/src/contracts.ts - Well-organized contract addresses with clear chain-specific configurations
  • libs/dapp-config/src/chains/evm/ - Comprehensive EVM chain configs using viem/wagmi patterns
  • libs/web3-api-provider/ - Clean EVM-only web3 provider implementation
  • Migration-specific code (Substrate→EVM) is properly isolated in apps/tangle-dapp/src/pages/claim/migration/

3. Configuration Management

  • Contract addresses properly organized by network (local, base-sepolia, base-mainnet, etc.)
  • Environment-based config system is in place
  • Chain configs use TypedChainId pattern for type safety

4. Shared Code Organization

  • libs/tangle-shared-ui/ contains well-organized hooks (40+ hooks)
  • libs/ui-components/ for reusable UI components
  • libs/dapp-types/ for shared TypeScript types

⚠️ What's Concerning

1. Leftover Substrate References (LOW RISK)

  • libs/dapp-config/src/chains/substrate/ still exists with Polkadot/Kusama/Phala configs
  • Substrate chain configs have env: ['development'] limiting exposure
  • Hooks like useSubstrateTx.ts, useSubstrateAddress.ts still in shared libs
  • Decision needed: Are these needed for migration flow only, or should they be removed?

2. Duplicate Hook Implementations (MEDIUM)

apps/tangle-dapp/src/data/evm/useContractWrite.ts (146 lines)
libs/tangle-shared-ui/src/hooks/useContractWrite.ts (561 lines)

Two different useContractWrite implementations exist. The app-level one may be redundant.

3. Missing GraphQL Schema (LOW)

  • codegen.ts references ./libs/tangle-shared-ui/src/graphql/envio.schema.graphql which doesn't exist
  • GraphQL codegen may be broken or using fallback endpoint

4. README Documentation Mismatch (LOW)

  • README mentions polkadot-api-provider library which no longer exists in libs/
  • Library list in README needs updating for v2

5. @PolkaDot Dependencies Still Present (LOW)

  • package.json still has @polkadot/api and related packages in resolutions
  • These may only be needed for migration features

📋 Recommended Improvements (Prioritized)

P1 - Critical (Should fix before merge)

  1. ✗ None identified - architecture is sound

P2 - High Priority (Should fix soon)

  1. Consolidate useContractWrite hooks - use shared lib version, remove app duplicate
  2. Fix or remove GraphQL schema reference in codegen.ts

P3 - Medium Priority (Can be follow-up)

  1. Update README.md to reflect v2 library structure (remove polkadot-api-provider mention)
  2. Add ARCHITECTURE.md documenting the EVM migration and remaining Substrate code purposes
  3. Consider adding deprecation notices to Substrate-related hooks if they're migration-only

P4 - Low Priority (Nice to have)

  1. Clean up Substrate chain configs once migration period ends
  2. Review and document @PolkaDot dependency usage for migration
  3. Add comments to libs/dapp-config/src/chains/substrate/ explaining they're dev/migration only

🤔 Decisions Needed (Human Review Required)

  1. Substrate code retention: Should Substrate hooks/configs be kept for migration support or can they be safely removed?

  2. GraphQL schema: Is the Envio indexer ready? Should codegen reference a live endpoint or local schema?

  3. Migration timeline: When can @PolkaDot dependencies and substrate chains be fully deprecated?


Implementation Notes

I'm implementing only the LOW-RISK README update as a safe improvement. Other changes require coordination with the team.


Generated by Phase 4 Architecture Review Subagent

- Remove reference to non-existent polkadot-api-provider
- Update library descriptions to reflect v2 EVM-focused architecture
- Remove tailwind-preset mention (not in libs/)
- Clarify web3-api-provider and tangle-shared-ui purposes
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