@@ -196,7 +196,9 @@ ${functionNames}
196196 } ,
197197
198198 /** Wallet connection and network switching patterns */
199- wallet : ( ) : string => `
199+ wallet : ( isLocalVM : boolean = false ) : string => {
200+ if ( isLocalVM ) {
201+ return `
200202**WALLET CONNECTION RULES:**
2012031. **Connect Wallet** button must be visible when disconnected.
2022042. Check \`window.ethereum\` existence before any wallet operations.
@@ -222,7 +224,70 @@ const switchNetwork = async (targetChainHex) => {
222224 }
223225};
224226\`\`\`
225- ` ,
227+ `
228+ }
229+
230+ // Real network: full wallet selection rules with disconnect/switch/localStorage
231+ return `
232+ **WALLET CONNECTION RULES:**
233+ 1. **Connect Wallet** button must be visible in the header/navbar when disconnected.
234+ 2. **Disconnect Wallet** button must be visible in the header/navbar when connected (next to the account address).
235+ 3. **Switch Network** button must appear **only when** the connected wallet's chain ID differs from the DApp's target chain ID. Hide it when on the correct network.
236+ 4. Check \`window.ethereum\` existence before any wallet operations.
237+ 5. Show "Wrong Network" warning with a **Switch Network** button if chain ID mismatches.
238+ 6. Use loading spinners for async actions.
239+ 7. Handle "User rejected request" errors gracefully.
240+ 8. Show truncated wallet address (e.g. \`0x1234...5678\`) when connected.
241+
242+ **🚨 CHAIN ID COMPARISON (CRITICAL — prevents wrong-network false positive):**
243+ - ethers.js v6 returns \`network.chainId\` as a **BigInt** (e.g. \`11155111n\`).
244+ - **NEVER compare hex strings directly** (e.g. \`"0xaa36a7" !== "aa36a7"\` — prefix mismatch!).
245+ - **ALWAYS compare as decimal numbers:**
246+ \`\`\`javascript
247+ const TARGET_CHAIN_ID = 11155111; // Sepolia — use DECIMAL number
248+ // After connecting:
249+ const network = await provider.getNetwork();
250+ const currentChainId = Number(network.chainId);
251+ setChainId(currentChainId);
252+ // Wrong network check:
253+ const isWrongNetwork = account && chainId !== null && chainId !== TARGET_CHAIN_ID;
254+ \`\`\`
255+ - For \`wallet_switchEthereumChain\`, convert to hex: \`'0x' + TARGET_CHAIN_ID.toString(16)\`
256+
257+ **Disconnect Pattern (mandatory — MUST implement this):**
258+ \`\`\`javascript
259+ const disconnectWallet = () => {
260+ setAccount(null);
261+ setProvider(null);
262+ setSigner(null);
263+ // Clear saved wallet preference for wallet selection
264+ try { localStorage.removeItem('__qdapp_wallet_rdns'); } catch(e) {}
265+ };
266+ \`\`\`
267+ The Disconnect button should be placed in the navbar/header, visible when connected.
268+ When disconnected, the DApp should return to the initial "Connect Wallet" state.
269+
270+ **Network Switch Pattern (mandatory — MUST be a visible button):**
271+ \`\`\`javascript
272+ const switchNetwork = async (targetChainHex) => {
273+ try {
274+ await window.ethereum.request({
275+ method: 'wallet_switchEthereumChain',
276+ params: [{ chainId: targetChainHex }],
277+ });
278+ } catch (switchError) {
279+ if (switchError.code === 4902) {
280+ // Chain not added — prompt to add it
281+ await window.ethereum.request({ method: 'wallet_addEthereumChain', params: [...] });
282+ } else {
283+ throw switchError;
284+ }
285+ }
286+ };
287+ \`\`\`
288+ Show a **"Switch to [Network Name]"** button when the user is on the wrong chain.
289+ `
290+ } ,
226291
227292 /** Ethers.js v6 specific rules */
228293 ethersRules : ( ) : string => `
@@ -533,7 +598,7 @@ export const buildSystemPrompt = (ctx: PromptContext): string => {
533598 invariants . truncationPrevention ( ) ,
534599 // Layer 1
535600 blockchain . ethersRules ( ) ,
536- blockchain . wallet ( ) ,
601+ blockchain . wallet ( ! ! ctx . isLocalVM ) ,
537602 blockchain . networkContext ( ctx . contract . chainId , ! ! ctx . isLocalVM ) ,
538603 // Layer 2
539604 ctx . isBaseMiniApp ? platform . baseMiniApp ( ) : '' ,
0 commit comments