From 5eeae712490e399d08b5421639c2edbb6f8a9c59 Mon Sep 17 00:00:00 2001 From: mikeminer <4n0nym0u5.cyber@gmail.com> Date: Tue, 1 Sep 2026 06:06:28 +0200 Subject: [PATCH] Harden DevFridge trust signals for Phantom --- app/public/.well-known/security.txt | 5 ++ app/public/robots.txt | 2 + app/src/hooks/usePhantom.ts | 75 +++++++++++++++++++++++++--- scan/public/.well-known/security.txt | 5 ++ team/index.html | 12 +++++ team/public/.well-known/security.txt | 5 ++ team/public/robots.txt | 2 + 7 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 app/public/.well-known/security.txt create mode 100644 app/public/robots.txt create mode 100644 scan/public/.well-known/security.txt create mode 100644 team/public/.well-known/security.txt create mode 100644 team/public/robots.txt diff --git a/app/public/.well-known/security.txt b/app/public/.well-known/security.txt new file mode 100644 index 0000000..ea75ef1 --- /dev/null +++ b/app/public/.well-known/security.txt @@ -0,0 +1,5 @@ +Contact: https://connect.devfridge.cool +Expires: 2027-09-01T00:00:00.000Z +Preferred-Languages: en, it +Policy: https://docs.devfridge.cool/security +Canonical: https://devfridge.cool/.well-known/security.txt diff --git a/app/public/robots.txt b/app/public/robots.txt new file mode 100644 index 0000000..c2a49f4 --- /dev/null +++ b/app/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / diff --git a/app/src/hooks/usePhantom.ts b/app/src/hooks/usePhantom.ts index 442488e..a769051 100644 --- a/app/src/hooks/usePhantom.ts +++ b/app/src/hooks/usePhantom.ts @@ -26,6 +26,62 @@ type PhantomProvider = { ) => Promise<{ signature: string }>; }; +type SimulationResponse = { + error?: { message?: string }; + result?: { value?: { err?: unknown; logs?: string[] | null } }; +}; + +function bytesToBase64(bytes: Uint8Array): string { + let binary = ""; + const chunkSize = 0x8000; + for (let i = 0; i < bytes.length; i += chunkSize) { + binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize)); + } + return btoa(binary); +} + +async function simulateBeforeSigning( + connection: Connection, + tx: Transaction | VersionedTransaction +): Promise { + const wire = + tx instanceof Transaction + ? tx.serialize({ requireAllSignatures: false, verifySignatures: false }) + : tx.serialize(); + const response = await fetch(connection.rpcEndpoint, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: "devfridge-preflight", + method: "simulateTransaction", + params: [ + bytesToBase64(wire), + { + encoding: "base64", + commitment: "confirmed", + sigVerify: false, + replaceRecentBlockhash: false, + }, + ], + }), + }); + if (!response.ok) { + throw new Error(`Transaction simulation request failed (${response.status})`); + } + const body = (await response.json()) as SimulationResponse; + if (body.error) { + throw new Error(body.error.message || "Transaction simulation failed"); + } + const simulationError = body.result?.value?.err; + if (simulationError) { + const logs = body.result?.value?.logs?.slice(-3).join(" | "); + throw new Error( + `Transaction would fail on-chain: ${JSON.stringify(simulationError)}${logs ? ` (${logs})` : ""}` + ); + } +} + function getProvider(): PhantomProvider | null { const w = window as Window & { phantom?: { solana?: PhantomProvider }; @@ -80,27 +136,34 @@ export function usePhantom(cluster: ClusterName, fallbackEndpoint?: string) { tx.recentBlockhash = latest.blockhash; tx.lastValidBlockHeight = latest.lastValidBlockHeight; } - if (extraSigners.length) tx.partialSign(...extraSigners); } + // Phantom recommends simulating with sigVerify=false before requesting a + // signature. This catches deterministic failures before the wallet dialog. + await simulateBeforeSigning(connection, tx); + // Always submit on DevFridge's selected cluster RPC. Phantom's // signAndSendTransaction uses the wallet's network, which is often // still Mainnet when the site is on Devnet/Testnet. if (p.signTransaction) { const signed = await p.signTransaction(tx); if (signed instanceof Transaction) { - const out = Transaction.from( - signed.serialize({ requireAllSignatures: false, verifySignatures: false }) - ); - if (extraSigners.length) out.partialSign(...extraSigners); - return connection.sendRawTransaction(out.serialize(), sendOpts); + // Phantom signs first; any local co-signers are added afterwards. + // This ordering follows Phantom's transaction-warning guidance. + if (extraSigners.length) signed.partialSign(...extraSigners); + return connection.sendRawTransaction(signed.serialize(), sendOpts); } + if (extraSigners.length) signed.sign(extraSigners); return connection.sendRawTransaction( (signed as VersionedTransaction).serialize(), sendOpts ); } + if (extraSigners.length) { + throw new Error("This transaction requires a wallet that supports signTransaction"); + } + const { signature } = await p.signAndSendTransaction(tx); return signature; }, [connection, publicKey]); diff --git a/scan/public/.well-known/security.txt b/scan/public/.well-known/security.txt new file mode 100644 index 0000000..ca1188c --- /dev/null +++ b/scan/public/.well-known/security.txt @@ -0,0 +1,5 @@ +Contact: https://connect.devfridge.cool +Expires: 2027-09-01T00:00:00.000Z +Preferred-Languages: en, it +Policy: https://docs.devfridge.cool/security +Canonical: https://scan.devfridge.cool/.well-known/security.txt diff --git a/team/index.html b/team/index.html index ce9069e..c096c67 100644 --- a/team/index.html +++ b/team/index.html @@ -4,6 +4,18 @@ DevFridge — Team + + + + + +