Both /join-now and /direct-join now discover every injected wallet (Brave,
MetaMask, SafePal, Trust, Coinbase…) via EIP-6963 and, when more than one is
present, show a 'Choose your wallet' picker on connect — instead of blindly
grabbing window.ethereum (which silently hangs when e.g. Brave's built-in wallet
wins the slot). Shared public/rmc-wallet.js; provider resolved on connect and
wallet events bound once after. Falls back to window.ethereum for legacy
single-provider wallets. CSP-safe (6963 icons are data URIs).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/join-now: public self-enroll page gated behind config.dappFallbackPublic
(default off -> redirects to /start, invisible until needed). Auto-assigns the
current rotation sponsor via /api/public/current-sponsor, registers the user's
own wallet, then redirects to /my/<newId> so new members land on their live
position instead of guessing their ID. HTML lives in private/, served only via
the flagged route. Reuses the proven contract engine. Admin /direct-join now
also shows a 'View position ->' link to /my/<newId> after a successful join.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After submit, poll eth_getTransactionReceipt and read the member id from the
MemberRegistered event (topics[1]); surface it in a prominent success box plus
the activity log, instead of leaving the user to find it in Telegram. Same for
upgrades (shows the new level). Reverts are reported clearly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chainChanged handler called location.reload(); wallets that fire chainChanged
on page load turned that into an endless refresh that blocked Connect. Handle
accountsChanged/chainChanged in place instead of reloading.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contingency tool: if the official RM Circle dApp ever goes down, positions can
still be created/upgraded straight from the member's own wallet. Vanilla JS
talks only to window.ethereum (CSP-safe, no external libs), builds register()
[0x30de37e4, sponsorId+tier, value=base*1.05] and upgrade() [0xd55ec697,
value=next-level cost] calls decoded from live txs, reads prices via
getAllCosts() and position via getMember(address). Page lives outside public/
and is served only through a getSession-gated route, so it's admin-only. The
contract is public and immutable, so this cements a company-domain-independent
path to enroll.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>