Switch RPC and Subsquid endpoints to mainnet - #647
Conversation
rpc1/rpc2-mainnet.quantus.com and subsquid-mainnet-app-1/2.quantus.com replace the Planck nodes and sub2. Drops the unused stillOnTestnet flag, points polkadart codegen and test_qr_payload.sh at the mainnet node, and adds the mainnet genesis hash to knownNetworks so signers name it.
The home screen no longer offers testnet tokens on a zero balance; there is no faucet. The Mining Rewards settings item goes away with its screens, redeem flow, provider, service, bundled testnet miner lists, and l10n. The only Supabase consumer was that service, so its init and dependency go too, along with the SDK miner-stats helpers nothing else called.
n13
left a comment
There was a problem hiding this comment.
Reviewer model: GPT 5.6 Sol
Verdict (advisory): Request changes
Blocking findings:
-
[High] Invalidate or namespace persisted Wormhole state when changing genesis —
quantus_sdk/lib/src/constants/app_constants.dart:14-17. The released wallet already writeswormhole_nullifiers_v2_<address>.json, and both that cache and the current transfer cache are keyed only by address, not by genesis/network (wormhole_utxo_service.dart:175-187). After this update the same files are read against mainnet. Because a nullifier is recomputed only from the wallet secret andtransferCount(wormhole_utxo_service.dart:648-659), a transfer number that was spent on Planck is immediately classified as spent on mainnet without querying mainnet. Current-main builds can also merge Planck transfer rows or skip the mainnet history range using Planck's much largercachedUpToBlock. Namespace both caches by genesis hash, or at minimum bump/clear both cache generations as part of this switch, and add an upgrade regression covering an existing Planck cache. -
[High] Keep the miner's UTXO discovery on the chain it is mining —
quantus_sdk/lib/src/constants/app_constants.dart:14-17.miner-appstill defaults to Planck (miner_config.dart:139-145), but its balance and withdrawal screens instantiateWormholeUtxoService, which now gets mainnet RPC and GraphQL from these shared constants. The claim flow discovers those mainnet UTXOs first and only later uses the explicitly supplied PlanckrpcUrlfor proofs/submission. A Planck miner therefore shows the wrong reward balance and cannot reliably withdraw it; if the same address has mainnet UTXOs, the flow attempts to prove them against Planck. Pass the selected chain's RPC/indexer into UTXO discovery, or move the miner to mainnet atomically rather than redirecting only its shared SDK services. -
[Medium] Make the second GraphQL endpoint handle HTTP 5xx from the first —
quantus_sdk/lib/src/constants/app_constants.dart:15-17. During this review both new origins returned Cloudflare 502 and later recovered independently.RedundantEndpointService.post()currently treats every HTTP response as success and stops iterating;GraphQlEndpointService.query()checks the status only afterpost()has returned. Thus an app-1 502 never reaches app-2 even if app-2 is healthy. Treat retryable 5xx responses as endpoint failures inside the failover boundary and cover first-502/second-200 behavior.
Validation:
- Reviewed full diff
0ad19057...f0edaf04;git diff --check, changed-Dart formatting, shell syntax, localization-member consistency, and removed-symbol searches passed. - Full mobile suite passed (426 tests); full non-native SDK and cold-wallet suites passed.
- Both RPC endpoints answered over HTTPS and WSS with mainnet genesis
0xfb5487...626fbaand runtime 152/transaction 6. Both GraphQL origins currently pass/healthzand the SDK's transfer query. - GitHub Analyze is red only for the unchanged base import in
mobile-app/test/unit/regular_send_strategy_test.dart:2; all other package analyzers passed before that failure.
…over - Wormhole caches: one generation constant for both the transfer and the nullifier file, bumped to v3 for the mainnet switch. The files are keyed by address only, so Planck-era transfers and nullifiers would otherwise be read against mainnet and a transfer count spent on Planck would count as spent here. Earlier generations of an address are deleted on first read. - Miner app: balance, withdrawal and claim discovery use the selected chain's RPC and indexer through MinerSettingsService.utxoService(), replacing the stub that never configured anything. Mainnet is added as the default chain and Planck points at its real indexer. - Endpoint failover: an HTTP 5xx is an endpoint failure inside RedundantEndpointService, so the next endpoint is tried; a 4xx is returned unchanged. The HTTP client is injectable for tests.
n13
left a comment
There was a problem hiding this comment.
Reviewer model: GPT 5.6 Sol
Verdict (advisory): Request changes
Blocking finding:
- [High] Namespace or clear the current-generation Wormhole caches when the miner switches chains —
quantus_sdk/lib/src/services/wormhole_utxo_service.dart:181-200,miner-app/lib/src/services/miner_settings_service.dart:45-47.utxoService()now correctly binds discovery to the selected chain's endpoints, but both cache filenames still contain only the generation and address prefix.saveChainId()changes only the preference. After the miner creates v3 transfer/nullifier caches on Planck and the user selects Mainnet (or vice versa),deleteStaleCaches()deliberately preserves those same v3 files and the new chain reads them. The cached height can suppress part or all of the new chain's transfer scan, while transfers and spent nullifiers from the old chain are evaluated as if they belonged to the new one, yielding a wrong rewards balance and unreliable claim discovery. Include a stable network/genesis namespace in both cache keys, or clear the Wormhole caches atomically whenever the selected chain changes, and add a regression that switches between two networks within cache generation v3.
Validation:
- Reviewed the complete
0ad19057...2ad6f018diff and the incremental fixes since the prior review; no other blocking findings found. git diff --check, changed-Dart formatting (27 files), shell syntax, localization consistency, removed-symbol searches, and the 9 focused endpoint/cache/pagination tests passed.- Full mobile, non-native SDK, and cold-wallet suites passed. The miner's 12 runnable tests passed; its overall suite remains red because the pre-existing
test/widget_test.dartplaceholder has nomain. - Local Melos analysis completed cleanly for cold wallet, miner, and SDK before the strict 10-second cap stopped the mobile package; the current-head GitHub Analyze and dependency-cooldown checks both passed.
- Both mainnet RPCs returned genesis
0xfb5487...626fbaand runtime 152 / transaction 6; both GraphQL origins passed health and the SDK transfer query. Live runtime metadata retains the bundled spec-147 pallet/call indices and top-level call field signatures.
Both cache files were keyed by generation and address only, so a miner that scanned on Planck and then selected Mainnet read Planck's scan height and spent nullifiers as if they were mainnet's. The files now carry the first 16 hex chars of the chain's genesis hash, fetched once per service instance over the same RPC discovery already uses. Stale cleanup keeps current-generation files of every network, so switching back does not rescan. RpcEndpointService.forUrls takes a client for tests.
n13
left a comment
There was a problem hiding this comment.
Reviewer model: GPT 5.6 Sol
Verdict (advisory): Approve
No blocking findings at 43d7cee01568ef6eab50981279b841b9cf6d9817.
The latest commit resolves the remaining cross-chain cache issue: transfer and spent-nullifier files are now namespaced by a genesis-derived network ID, pre-v3 files are removed, and current-generation caches for other networks are preserved without being read on the selected chain. Miner balance discovery, claim discovery, proof RPC, and submission are consequently bound to the same selected chain. The mainnet endpoint switch, 5xx failover, testnet reward/Supabase removal, generated localization changes, and signer-visible genesis naming are otherwise consistent across their callers.
Validation:
- Reviewed the complete
0ad19057...43d7cee0diff, prior review thread, and the incremental2ad6f018...43d7cee0fix;git diff --check, shell syntax, removed-symbol searches, and formatting of all 27 changed Dart files passed. - Full suites passed: quantus_sdk non-native (480), mobile app (426), cold wallet (285), and all 12 runnable miner tests. The focused cache/failover/pagination set passed 10/10.
- Local strict analysis passed for cold wallet, miner, and SDK before the required 10-second cap stopped the mobile package; GitHub
Analyzepassed at this exact head. Directflutter gen-l10nis unavailable because this repository does not enableflutter.generate; the committed generated localizations compiled in the full mobile suite, and the locale-key mismatch is unchanged from base. - Both mainnet RPCs returned genesis
0xfb5487c0be6ae4ade2d41d16e50465129861636c2b8d61fa94d7a19631626fbaand runtime 152 / transaction 6. Both GraphQL origins accepted the SDK transfer query and were caught up exactly to the RPC head at validation time.
Endpoint changes
https://rpc1-mainnet.quantus.com,https://rpc2-mainnet.quantus.com(replaces a1/a2-planck.quantus.cat and matcha-latte).https://subsquid-mainnet-app-1.quantus.com/v1/graphql,https://subsquid-mainnet-app-2.quantus.com/v1/graphql(replaces sub2). The/v1/graphqlpath comes from the iac nginx edge config, whose root location proxies straight to Hasura, same shape as sub2.test_qr_payload.shdefaults to rpc1-mainnet.stillOnTestnet; nothing referenced it.0xfb5487…626fba, from the GM release plan) toknownNetworksso the cold wallet and Keystone review name mainnet payloads instead of showing Unknown.Testnet UI removed
faucetUrland the X-post launcher are gone.Supabase.initialize, the env getters, and thesupabase_flutterdependency are removed.SUPABASE_URL/SUPABASE_ANON_KEYare no longer read; they can be dropped from.envand CI secrets.QuersiService.getMinerStats,getMiningAccountId,getOldMiningAccountId, theMinerStatsmodel,SubstrateService.nonHDdilithiumKeypairFromMnemonic,telemetryUrl, andminingSetupGuideUrlhad no remaining callers and are removed.homeGetTestnetTokens,swapDepositTestnetBanner, everysettingsMining*/settingsTestnet*, and theredeem*keys exceptredeemCancel/redeemClose, which the encrypted send progress screen still uses). Generated localizations regenerated.Test suites pass: quantus_sdk (non-native), mobile-app, cold-wallet-app.
Wormhole caches bound to the chain
The transfer and nullifier caches were keyed by generation and address only, so a miner that scanned on Planck and then selected Mainnet read Planck's scan height and spent nullifiers as mainnet's. Both files now carry the first 16 hex chars of the chain's genesis hash (
wormhole_cache_v3_<genesis16>_<address16>.json), fetched once perWormholeUtxoServiceinstance over the RPC it already uses for the chain head. Stale cleanup keeps current-generation files of every network, so switching back does not rescan; address-only v3 files from this branch are dropped like any earlier generation. A regression test switches between two networks within v3.Out of scope
miner-appchain list still has dev/dirac/planck with planck as default. A mainnet entry needs the node--chainid, so it is not a plain URL swap.scripts/debug_subsquid.shtargets the oldsubsquid.quantus.com/graphqlsquid API with non-Hasura query syntax; it was already stale.