From c1c01d6fe682e25a3b333e44c4984bcf8d40177d Mon Sep 17 00:00:00 2001 From: dcccrypto Date: Thu, 27 Aug 2026 12:19:44 +0100 Subject: [PATCH] fix(create-market): v17 detection must fail closed, not silently downgrade to v12 The probe initialised isV17Slab to false and swallowed read errors, so ANY failure to confirm v17-ness routed a v17 market down the legacy v12 branch. That branch calls sdk-compat stubs which THROW by design -- the instructions were removed on-chain in beta.29 -- so the user saw an opaque "[sdk-compat] ... removed in beta.29" describing an SDK migration rather than what actually went wrong. The slab is created moments earlier in the same flow, so getAccountInfo returning null is an ordinary propagation race, not an exceptional case. That made the bad branch reachable in normal operation whenever admin-oracle mode was selected. Now: tri-state (boolean | null), up to 5 reads at "confirmed" with backoff, and if v17-ness still cannot be established the flow stops with a message naming the account and stating that nothing further was sent. Failing closed is correct here because the alternative branch is known to throw. Fixes dcccrypto/percolator-launch#2523. app tsc: 0 errors. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NgoNgagkvw7i5SSRC3FJ8D --- app/hooks/useCreateMarket.ts | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/app/hooks/useCreateMarket.ts b/app/hooks/useCreateMarket.ts index 30ab9327..bdc39f53 100644 --- a/app/hooks/useCreateMarket.ts +++ b/app/hooks/useCreateMarket.ts @@ -2045,13 +2045,38 @@ export function useCreateMarket() { const instructions: TransactionInstruction[] = []; // Detect if this is a v17 slab (v17 magic at bytes 0-7). - let isV17Slab = false; - try { - const newSlabInfo = await connection.getAccountInfo(slabPk); - if (newSlabInfo?.data) { - isV17Slab = isV17Account(new Uint8Array(newSlabInfo.data)); + // + // This must FAIL CLOSED. It used to default to false and swallow the error, so any + // hiccup reading a slab we had just created routed a v17 market down the legacy v12 + // branch below — which calls sdk-compat stubs that THROW by design (the instructions + // were removed on-chain in beta.29). The user then saw an opaque + // "[sdk-compat] ... removed in beta.29" instead of the real problem. + // + // The slab was created moments ago in this same flow, so a null read is an ordinary + // propagation race, not an exceptional case. Retry, and if v17-ness still cannot be + // established, say so plainly rather than guessing "v12". + let isV17Slab: boolean | null = null; + let lastDetectErr: unknown = null; + for (let attempt = 0; attempt < 5 && isV17Slab === null; attempt++) { + try { + const newSlabInfo = await connection.getAccountInfo(slabPk, "confirmed"); + if (newSlabInfo?.data) { + isV17Slab = isV17Account(new Uint8Array(newSlabInfo.data)); + break; + } + } catch (e) { + lastDetectErr = e; } - } catch { /* fall through — conservative: assume v12 */ } + await new Promise((r) => setTimeout(r, 250 * (attempt + 1))); + } + if (isV17Slab === null) { + throw new Error( + `Could not read the market account ${slabPk.toBase58()} just after creating it, ` + + `so its program version could not be determined. Nothing further was sent. ` + + `Retry the oracle setup step.` + + (lastDetectErr ? ` (last read error: ${String(lastDetectErr)})` : ""), + ); + } if (!isV17Slab && isAdminOracle) { // v12 admin oracle setup (removed in v17):