From 15a881a72b6f4e75286cd7581b552205e9348bf3 Mon Sep 17 00:00:00 2001 From: martbost Date: Thu, 10 Sep 2026 06:11:38 -0500 Subject: [PATCH] Positions: force the wallet's account picker (wallet_requestPermissions) when adding a position or when the Buy-from wallet doesn't match; sign/send with the wallet's active account Co-Authored-By: Claude Fable 5.1 --- public/assets/my.js | 15 +++++++-------- public/assets/wallet.js | 33 ++++++++++++++++++++++++++++++--- public/my.html | 4 ++-- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/public/assets/my.js b/public/assets/my.js index 1031d28..9937b57 100644 --- a/public/assets/my.js +++ b/public/assets/my.js @@ -1249,10 +1249,9 @@ const me = await (await fetch('/api/me')).json(); if (!me.address) throw new Error('Link your main wallet first (Wallet tab), then add positions under it.'); if (!me.memberId) throw new Error('Switch on payouts for your main wallet first (Wallet tab). Positions register under your member number.'); - if (!confirm('In your wallet app, switch to a DIFFERENT account than ' + short(me.address) + ' first (MetaMask: account menu, Add account. Trust or SafePal: switch wallet). The wallet picker will open again so you can connect that account.\n\nReady?')) return; - await IAPWallet.disconnect(); - IAP.status('Connect the new account in the picker, then sign once…'); - const r = await IAPWallet.signIn({ asPosition: true }); + if (!confirm('Your wallet will ask which account to connect. Tick ONLY the new account (not ' + short(me.address) + '), then sign once.\n\nIf you have not created the extra account yet: MetaMask, account menu, Add account. Trust or SafePal: switch wallet.\n\nReady?')) return; + IAP.status('Pick the new account in your wallet, then sign once…'); + const r = await IAPWallet.signIn({ asPosition: true, pick: true }); $('qsHint').textContent = 'Added ' + short(r.address) + '. Now choose it under "Buy from" and buy a $20 or larger package.'; IAP.status('Position added: ' + short(r.address) + '. Pick it under "Buy from" above and buy a $20+ package to count it.', 'ok'); await loadPositions(); @@ -1299,11 +1298,11 @@ } const wantAddr = fromPos || (meNow.address ? meNow.address.toLowerCase() : null); if (wantAddr) { // never buy from a wallet other than the one selected: a stray wallet would register a brand-new member - let cur = (IAPWallet.address() || await IAPWallet.connect() || '').toLowerCase(); + await IAPWallet.connect(); + let cur = String(await IAPWallet.activeAddress() || '').toLowerCase(); if (cur !== wantAddr) { - IAP.status('Switch your wallet app to ' + wantAddr.slice(0, 6) + '…' + wantAddr.slice(-4) + ', then pick it in the picker…'); - await IAPWallet.disconnect(); - cur = String(await IAPWallet.connect() || '').toLowerCase(); + IAP.status('Pick ' + wantAddr.slice(0, 6) + '…' + wantAddr.slice(-4) + ' in your wallet\'s account picker…'); + cur = String(await IAPWallet.pickAccount() || '').toLowerCase(); } if (cur !== wantAddr) throw new Error('Your wallet connected as ' + cur.slice(0, 6) + '…' + cur.slice(-4) + ' but you chose ' + wantAddr.slice(0, 6) + '…' + wantAddr.slice(-4) + '. Switch accounts in your wallet app and try again.'); } diff --git a/public/assets/wallet.js b/public/assets/wallet.js index a945e1b..b4fb3f6 100644 --- a/public/assets/wallet.js +++ b/public/assets/wallet.js @@ -93,6 +93,33 @@ window.IAPWallet = (function () { } function eth() { if (!provider) throw new Error('Connect your wallet first.'); return provider; } + const isInjected = () => !!(provider && window.ethereum && (provider === window.ethereum || provider.isMetaMask)); + // the account the wallet will actually sign with: for an injected wallet (MetaMask + // extension) that is its active account, which can differ from AppKit's cached one + async function activeAddress(fallback) { + if (isInjected()) { try { const a = await provider.request({ method: 'eth_accounts' }); if (a && a[0]) return a[0]; } catch (e) {} } + return fallback || currentAddress(); + } + // Force the wallet's own account picker. Injected wallets stay connected to the + // site, so a plain disconnect/reconnect never shows one: asking for permissions + // again makes MetaMask open its account-selection prompt, and whatever the user + // ticks becomes the active account. WalletConnect wallets fall back to a fresh + // session (the picker + the wallet app's own account choice). + async function pickAccount() { + const c = await IAP.getConfig(); + await initAppKit(c); + const inj = window.ethereum; + if (inj && inj.request) { + try { + await inj.request({ method: 'wallet_requestPermissions', params: [{ eth_accounts: {} }] }); + const accs = await inj.request({ method: 'eth_accounts' }); + if (accs && accs[0]) { provider = inj; await ensureChain(c).catch(() => {}); return accs[0]; } + } catch (e) { + if (e && (e.code === 4001 || /reject|denied/i.test(String(e.message || '')))) throw new Error('You closed the account picker. Pick the account you want and try again.'); + } + } + return freshConnect(c); + } // A WalletConnect session can die underneath AppKit's cached "connected" // state: the wallet app rejects or kills it (Trust does this after its own @@ -149,7 +176,7 @@ window.IAPWallet = (function () { // SIWE: challenge -> personal_sign -> verify (server sets the session cookie) async function signIn(opts) { - const addr = await connect(); + const addr = (opts && opts.pick) ? await pickAccount() : await activeAddress(await connect()); const ch = await (await fetch('/api/auth/challenge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address: addr }) })).json(); if (ch.error) throw new Error(ch.error); @@ -177,7 +204,7 @@ window.IAPWallet = (function () { if (!isNaN(chainNum(cur)) && chainNum(cur) !== wantNum) throw new Error('Your wallet is on the wrong network. Switch it to ' + (c.chainName || 'the correct network') + ', then try again.'); } - const tx = { from: addr, to: c.contract, data }; + const tx = { from: await activeAddress(addr), to: c.contract, data }; if (valueWei) tx.value = '0x' + BigInt(valueWei).toString(16); // Amoy's Bor nodes enforce a ~25 gwei minimum priority fee that MetaMask's // own estimate misses ("gas tip below minimum"). Pull the network's correct @@ -237,5 +264,5 @@ window.IAPWallet = (function () { return BigInt(h); } function walletName() { try { const w = modal && modal.getWalletInfo && modal.getWalletInfo(); return (w && w.name) || ''; } catch (e) { return ''; } } - return { connect, signIn, buy, activate, waitTx, disconnect, balance, address: currentAddress, walletName }; + return { connect, signIn, buy, activate, waitTx, disconnect, balance, address: currentAddress, activeAddress, pickAccount, walletName }; })(); diff --git a/public/my.html b/public/my.html index caab69e..f62fcd9 100644 --- a/public/my.html +++ b/public/my.html @@ -793,9 +793,9 @@ - + - +