99 lines
5.5 KiB
JavaScript
99 lines
5.5 KiB
JavaScript
/* RM Circle — EIP-6963 multi-wallet discovery + picker.
|
|
Lets the user choose among ALL injected wallets (Brave, MetaMask, SafePal,
|
|
Trust, Coinbase…) instead of whatever won the window.ethereum fight — which is
|
|
why "connect" silently hangs when e.g. Brave's built-in wallet grabs the slot.
|
|
CSP-safe: no external libs, and EIP-6963 wallet icons are data: URIs. */
|
|
(function(){
|
|
'use strict';
|
|
var found=[], seen={};
|
|
function onAnnounce(e){
|
|
var d=e&&e.detail; if(!d||!d.info||!d.provider) return;
|
|
var key=d.info.uuid||d.info.rdns||d.info.name; if(seen[key]) return; seen[key]=1;
|
|
found.push({info:d.info, provider:d.provider});
|
|
}
|
|
window.addEventListener('eip6963:announceProvider', onAnnounce);
|
|
function request(){ try{ window.dispatchEvent(new Event('eip6963:requestProvider')); }catch(e){} }
|
|
request();
|
|
|
|
// Some wallets don't fire EIP-6963 in every setup (notably Phantom, whose EVM
|
|
// provider lives at window.phantom.ethereum), and older multi-wallet setups
|
|
// expose window.ethereum.providers[]. Fold those in so nobody's wallet is
|
|
// invisible at Connect. Dedupe by provider identity AND name.
|
|
function probeInjected(list){
|
|
function add(provider,name,rdns){
|
|
if(!provider) return;
|
|
if(list.some(function(w){return w.provider===provider||(w.info&&w.info.name===name);})) return;
|
|
list.push({info:{name:name,rdns:rdns||'',uuid:'',icon:''},provider:provider});
|
|
}
|
|
try{
|
|
var eth=window.ethereum;
|
|
if(eth&&Array.isArray(eth.providers)){
|
|
eth.providers.forEach(function(p){
|
|
if(!p)return;
|
|
var nm=p.isPhantom?'Phantom':p.isMetaMask?'MetaMask':p.isCoinbaseWallet?'Coinbase Wallet':p.isTrust||p.isTrustWallet?'Trust Wallet':p.isRabby?'Rabby':'Injected wallet';
|
|
add(p,nm);
|
|
});
|
|
}
|
|
if(window.phantom&&window.phantom.ethereum) add(window.phantom.ethereum,'Phantom','app.phantom');
|
|
if(eth&ð.isPhantom&&!(window.phantom&&window.phantom.ethereum)) add(eth,'Phantom','app.phantom');
|
|
}catch(e){}
|
|
return list;
|
|
}
|
|
|
|
function showPicker(list){
|
|
return new Promise(function(resolve){
|
|
var ov=document.createElement('div');
|
|
ov.style.cssText='position:fixed;inset:0;background:rgba(3,10,18,.74);display:flex;align-items:center;justify-content:center;z-index:99999;padding:20px';
|
|
var box=document.createElement('div');
|
|
box.style.cssText='background:#0b1e30;border:1px solid #27445e;border-radius:16px;padding:20px;max-width:360px;width:100%;box-shadow:0 20px 60px rgba(0,0,0,.55)';
|
|
var h=document.createElement('div'); h.style.cssText='font-weight:800;font-size:17px;color:#f7f9fc;margin-bottom:4px'; h.textContent='Choose your wallet'; box.appendChild(h);
|
|
var s=document.createElement('div'); s.style.cssText='color:#aebdca;font-size:13px;margin-bottom:14px'; s.textContent='Pick the wallet you want to connect with.'; box.appendChild(s);
|
|
list.forEach(function(w){
|
|
var b=document.createElement('button');
|
|
b.style.cssText='display:flex;align-items:center;gap:12px;width:100%;text-align:left;background:#132c43;border:1px solid #294a65;border-radius:11px;padding:12px 14px;margin-bottom:8px;cursor:pointer;color:#f7f9fc;font-weight:700;font-size:15px';
|
|
if(w.info.icon){ var img=document.createElement('img'); img.src=w.info.icon; img.alt=''; img.style.cssText='width:26px;height:26px;border-radius:6px;flex:none'; b.appendChild(img); }
|
|
var sp=document.createElement('span'); sp.textContent=w.info.name||'Wallet'; b.appendChild(sp);
|
|
b.addEventListener('click',function(){ try{document.body.removeChild(ov);}catch(e){} resolve(w.provider); });
|
|
box.appendChild(b);
|
|
});
|
|
var c=document.createElement('button'); c.textContent='Cancel';
|
|
c.style.cssText='width:100%;background:transparent;border:0;color:#8498aa;padding:8px;margin-top:2px;cursor:pointer;font-size:13px';
|
|
c.addEventListener('click',function(){ try{document.body.removeChild(ov);}catch(e){} resolve(null); });
|
|
box.appendChild(c);
|
|
ov.appendChild(box); document.body.appendChild(ov);
|
|
});
|
|
}
|
|
|
|
window.RMCWallet={
|
|
// Resolve a provider: 0 discovered -> legacy window.ethereum; 1 -> use it; 2+ -> ask.
|
|
pick:function(){
|
|
return new Promise(function(resolve){
|
|
request();
|
|
setTimeout(function(){
|
|
var list=probeInjected(found.slice());
|
|
if(list.length===0){ resolve(window.ethereum||null); return; }
|
|
if(list.length===1){ resolve(list[0].provider); return; }
|
|
showPicker(list).then(resolve);
|
|
}, 220);
|
|
});
|
|
},
|
|
list:function(){ return probeInjected(found.slice()); },
|
|
// Switch the wallet to Polygon (137) so a SIWE sign-in whose message says
|
|
// "Chain ID: 137" matches the wallet's active chain — otherwise wallets
|
|
// refuse the signature with a "chain ID does not match" error. Adds Polygon
|
|
// if the wallet doesn't have it. Returns a Promise.
|
|
ensureChain:function(eth){
|
|
var POLY='0x89';
|
|
return eth.request({method:'eth_chainId'}).then(function(cid){
|
|
if(cid===POLY) return true;
|
|
return eth.request({method:'wallet_switchEthereumChain',params:[{chainId:POLY}]}).then(function(){return true;}).catch(function(e){
|
|
if(e&&e.code===4902){
|
|
return eth.request({method:'wallet_addEthereumChain',params:[{chainId:POLY,chainName:'Polygon Mainnet',nativeCurrency:{name:'POL',symbol:'POL',decimals:18},rpcUrls:['https://polygon-rpc.com'],blockExplorerUrls:['https://polygonscan.com']}]}).then(function(){return true;});
|
|
}
|
|
throw e;
|
|
});
|
|
});
|
|
}
|
|
};
|
|
})();
|