/* RM Circle — public direct-join engine (dormant fallback; served only when config.dappFallbackPublic is on). Talks only to window.ethereum (CSP-safe). Auto-assigns the current rotation sponsor, registers the member's own wallet, then sends them to their live position page /my/. Zero custody. */ (function(){ 'use strict'; var CONTRACT='0x33bdaeefd6d17d80ae53816c916dfb26c4fb2daf'; var POLYGON_HEX='0x89'; var SEL_REGISTER='0x30de37e4'; // register(uint48 sponsorId, uint8 tier) payable var SEL_GETMEMBER='0x2ada2596'; // getMember(address) var SEL_GETCOSTS='0x735f87b9'; // getAllCosts() var T_REGISTERED='0xe4a74887d749eb048f14bfef37b204477f3a5ff67055908b7c8cc62c202aef17'; var $=function(id){return document.getElementById(id);}; var eth=null; // resolved via the wallet picker on connect var account=null, costs=null, sponsorId=null; function log(msg,kind){var b=$('log');if(!b)return;var d=document.createElement('div');d.className='dj-log '+(kind||'');d.innerHTML=msg;b.appendChild(d);b.scrollTop=b.scrollHeight;} function word(n){return BigInt(n).toString(16).padStart(64,'0');} function addrWord(a){return a.toLowerCase().replace(/^0x/,'').padStart(64,'0');} function polStr(wei){return (Number(wei)/1e18).toFixed(2);} function hexWei(wei){return '0x'+BigInt(wei).toString(16);} async function req(method,params){ if(!eth) throw new Error('No wallet found'); return await eth.request({method:method,params:params||[]}); } async function ethCall(data){ return await req('eth_call',[{to:CONTRACT,data:data},'latest']); } async function loadCosts(){ var r=await ethCall(SEL_GETCOSTS); var d=r.slice(2), w=[]; for(var i=0;i -> place in that member's leg via the site's moving-link joinTarget var ref=new URLSearchParams(location.search).get('ref'); if(ref&&/^\d{1,15}$/.test(ref)){ try{ var rm=await (await fetch('/api/public/member?id='+ref)).json(); var t=rm&&rm.joinTarget; if(t&&t.id){ sponsorId=String(t.id); $('sponsorLine').innerHTML='You’ll join under #'+t.id+''+(String(t.id)!==String(ref)?' — the next open spot in #'+ref+'’s team':'')+'.'; return; } }catch(e){} } var j=await (await fetch('/api/public/current-sponsor')).json(); var sp=j&&j.sponsor; if(sp&&sp.id){ sponsorId=String(sp.id); $('sponsorLine').innerHTML='You’ll join the team under #'+sp.id+(sp.name?' ('+sp.name+')':'')+' — the current team placement.'; } else $('sponsorLine').textContent='Loading the current team placement…'; }catch(e){ $('sponsorLine').textContent='Could not load the current sponsor — please refresh.'; } } async function refreshBalance(){ try{ var b=await req('eth_getBalance',[account,'latest']); $('bal').textContent=polStr(BigInt(b))+' POL'; }catch(e){} } async function connect(){ try{ eth=await window.RMCWallet.pick(); if(!eth){ log('No wallet selected or detected. Install/unlock MetaMask, Trust, or SafePal and try again.','err'); return; } attachWalletEvents(); log('Requesting wallet connection…'); var accs=await req('eth_requestAccounts'); account=accs[0]; await ensurePolygon(); await loadCosts(); $('wallet').textContent=account.slice(0,6)+'…'+account.slice(-4); $('connected').style.display='block'; $('connectBtn').style.display='none'; await refreshBalance(); $('joinCost').textContent=polStr(regValue())+' POL'; var pos=await readPosition(account); if(pos){ $('already').style.display='block'; $('already').innerHTML='This wallet already holds position — open your dashboard. To create a new position, connect a different wallet.'; $('joinBtn').disabled=true; } $('joinBox').style.display='block'; log('Connected. Review the amount, then Join.','ok'); }catch(e){ log('Connect failed: '+(e.message||e),'err'); } } async function doJoin(){ try{ if(!sponsorId){ log('Still loading the current sponsor — one moment.','err'); await loadSponsor(); if(!sponsorId) return; } await ensurePolygon(); var val=regValue(); var data=SEL_REGISTER+word(sponsorId)+word(2); log('Joining under #'+sponsorId+', Premium — paying '+polStr(val)+' POL. Confirm in your wallet…'); $('joinBtn').disabled=true; var tx=await req('eth_sendTransaction',[{from:account,to:CONTRACT,value:hexWei(val),data:data}]); log('Submitted — waiting for confirmation… view tx','ok'); var rc=await waitReceipt(tx); if(!rc){ log('Still pending. Once it mines, open your dashboard at /my.',''); $('joinBtn').disabled=false; return; } if(rc.status==='0x0'){ log('⚠️ The transaction reverted — no position created, and your POL was returned (minus gas). Please try again.','err'); $('joinBtn').disabled=false; return; } var newId=receiptEventId(rc); if(newId){ reportJoin(newId); $('result').style.display='block'; $('resultId').textContent='#'+newId; $('resultSub').innerHTML='Welcome to the team! Taking you to your position page…'; $('resultLink').href='/my/'+newId; $('resultLink').style.display='inline-flex'; log('🎉 Confirmed — your position is #'+newId+'. Redirecting to your dashboard…','ok'); try{ el_scroll('result'); }catch(e){} setTimeout(function(){ location.href='/my/'+newId; }, 4500); } else { log('Confirmed on-chain. Open your dashboard at /my and enter your new ID (also shown on Polygonscan).','ok'); $('joinBtn').disabled=false; } }catch(e){ log('Join failed / rejected: '+(e.message||e),'err'); $('joinBtn').disabled=false; } } function el_scroll(id){ var e=$(id); if(e&&e.scrollIntoView) e.scrollIntoView({behavior:'smooth',block:'center'}); } // Auto-confirm the join server-side: records the submission (source + clickid // attribution), fires the purchase event + BeMob postback, enqueues rotation // joins, and alerts the team — same pipeline as the manual submit-ID form. function reportJoin(newId){ try{ var nameEl=$('memberName'); var name=(nameEl&&nameEl.value?nameEl.value.trim().slice(0,60):'')||('Self-enrolled #'+newId); var src=(window.ctbGetSource?window.ctbGetSource():'(direct)'); var cid=(window.ctbGetClickId?window.ctbGetClickId():''); fetch('/api/public/submit-id',{method:'POST',headers:{'Content-Type':'application/json'}, body:JSON.stringify({newId:String(newId),memberName:name,sponsorId:String(sponsorId||'?'),source:src,clickid:cid}) }).catch(function(){}); }catch(e){} } function attachWalletEvents(){ if(!eth||!eth.on||eth.__rmcBound) return; eth.__rmcBound=true; eth.on('accountsChanged',function(accs){ account=(accs&&accs[0])||null; if(!account){ $('connected').style.display='none'; $('connectBtn').style.display=''; } else { $('wallet').textContent=account.slice(0,6)+'…'+account.slice(-4); refreshBalance(); } }); eth.on('chainChanged',function(){ if(account) refreshBalance(); }); } document.addEventListener('DOMContentLoaded',function(){ loadSponsor(); $('connectBtn').addEventListener('click',connect); $('joinBtn').addEventListener('click',doJoin); }); })();