diff --git a/private/direct-join.html b/private/direct-join.html new file mode 100644 index 0000000..dc035d7 --- /dev/null +++ b/private/direct-join.html @@ -0,0 +1,74 @@ +Direct On-Chain Enrollment · RM Circle + + + + +
+
Contingency tool — for use only if the official RM Circle dApp is ever unavailable. This page builds the enrollment/upgrade transaction that your own wallet signs and pays, directly to the smart contract. It holds no funds and has no authority. If an amount is ever wrong the contract simply rejects it and you keep your POL. Normal joins should still go through the official dApp and the Getting Started page.
+ +
+

The contract

+
RM Circle (Polygon Mainnet, chain 137)
+
0x33BdAEEfd6d17D80aE53816c916dFb26c4fB2DAF
+ View verified code on Polygonscan ↗ +
+ +
+

1 · Connect your wallet

+ +
+
Wallet—
+
NetworkPolygon Mainnet ✓
+
Balance—
+
+
+ +
+

Your existing position

+

+
+
Upgrade to—
+
Cost—
+ +
+

+
+ +
+

2 · Register a new position

+

A wallet can hold one position. To create a new one, connect a fresh wallet funded with enough POL, enter the sponsor ID to join under, and register.

+
+
+
You will pay (Premium)—
+ +
+ +
+

Advanced (optional)

+
+

Only use this if you need to send a specific amount. The auto amount is read live from the contract's own price table.

+
+ +
+

Activity

+
+
+ +

No income is guaranteed. Cryptocurrency and smart-contract participation carry risk. Only use funds you can afford to lose.

+
+ diff --git a/public/direct-join.js b/public/direct-join.js new file mode 100644 index 0000000..4ed3663 --- /dev/null +++ b/public/direct-join.js @@ -0,0 +1,135 @@ +/* RM Circle — direct on-chain enrollment (contingency tool). + Talks ONLY to the injected wallet provider (window.ethereum), so it needs no + external libraries and no network of its own — CSP-safe (connect-src 'self'). + It builds transactions to the RM Circle contract that the USER signs and pays; + this page has zero custody and zero authority. If msg.value is ever wrong the + contract simply reverts and the user keeps their POL (minus trivial gas). */ +(function(){ + 'use strict'; + var CONTRACT='0x33bdaeefd6d17d80ae53816c916dfb26c4fb2daf'; + var POLYGON_HEX='0x89'; // 137 + // function selectors (decoded from live transactions) + var SEL_REGISTER='0x30de37e4'; // register(uint48 sponsorId, uint8 tier) payable + var SEL_UPGRADE ='0xd55ec697'; // upgrade() payable + var SEL_GETMEMBER='0x2ada2596';// getMember(address) view + var SEL_GETCOSTS='0x735f87b9'; // getAllCosts() view -> 4x uint256[8] (reg std, reg prem, up std, up prem) + var LEVELS=['','Scintilla','Ascensus','Fabrica','Culmen','Apex','Fastigium','Vertex','Corona']; + + var $=function(id){return document.getElementById(id);}; + var eth=window.ethereum; + var account=null, costs=null; + + function log(msg,kind){var b=$('log');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');} // uint -> 32-byte hex word + function addrWord(a){return a.toLowerCase().replace(/^0x/,'').padStart(64,'0');} + function polStr(wei){return (Number(wei)/1e18).toFixed(4);} + 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']); } + + // ---- reads (via the wallet's own RPC) ---- + async function loadCosts(){ + var r=await ethCall(SEL_GETCOSTS); var d=r.slice(2), w=[]; + for(var i=0;i (level '+pos.level+'/8, '+(pos.tier===2?'Premium':'Standard')+' tier, '+pos.directs+'/2 directs).'; + if(pos.level<8){ $('upBox').style.display='block'; $('upNext').textContent=LEVELS[pos.level+1]; $('upCost').textContent=polStr(upValue(pos.tier,pos.level))+' POL'; $('upgradeBtn').dataset.tier=pos.tier; $('upgradeBtn').dataset.level=pos.level; } + else $('upInfo').innerHTML='Already at the top level (Corona).'; + log('Connected. This wallet is registered — Upgrade is available; Join a new position would need a fresh wallet.','ok'); + } else { + log('Connected. This wallet has no position yet — you can Register a new position below.','ok'); + } + }catch(e){ log('Connect failed: '+(e.message||e),'err'); } + } + + async function doRegister(){ + try{ + var sponsor=($('sponsorId').value||'').trim(); + if(!/^\d{1,15}$/.test(sponsor)){ log('Enter a valid numeric sponsor ID first.','err'); return; } + var tier=$('tier').value==='1'?1:2; + await ensurePolygon(); + var val=regValue(tier); + var override=($('customPol').value||'').trim(); + if(override){ if(!/^\d+(\.\d+)?$/.test(override)){log('Custom amount must be a number.','err');return;} val=BigInt(Math.round(parseFloat(override)*1e6))*(10n**12n); } + var data=SEL_REGISTER+word(sponsor)+word(tier); + log('Sending REGISTER — sponsor #'+sponsor+', '+(tier===2?'Premium':'Standard')+', paying '+polStr(val)+' POL. Confirm in your wallet…'); + var tx=await req('eth_sendTransaction',[{from:account,to:CONTRACT,value:hexWei(val),data:data}]); + log('✅ Submitted: '+tx.slice(0,18)+'… — wait for it to confirm, then your position is live.','ok'); + setTimeout(refreshBalance,4000); + }catch(e){ log('Register failed / rejected: '+(e.message||e),'err'); } + } + + async function doUpgrade(){ + try{ + await ensurePolygon(); + var tier=parseInt($('upgradeBtn').dataset.tier,10), level=parseInt($('upgradeBtn').dataset.level,10); + var val=upValue(tier,level); + var override=($('customPol').value||'').trim(); + if(override){ if(!/^\d+(\.\d+)?$/.test(override)){log('Custom amount must be a number.','err');return;} val=BigInt(Math.round(parseFloat(override)*1e6))*(10n**12n); } + var data=SEL_UPGRADE; + log('Sending UPGRADE '+LEVELS[level]+' → '+LEVELS[level+1]+', paying '+polStr(val)+' POL. Confirm in your wallet…'); + var tx=await req('eth_sendTransaction',[{from:account,to:CONTRACT,value:hexWei(val),data:data}]); + log('✅ Submitted: '+tx.slice(0,18)+'…','ok'); + setTimeout(refreshBalance,4000); + }catch(e){ log('Upgrade failed / rejected: '+(e.message||e),'err'); } + } + + document.addEventListener('DOMContentLoaded',function(){ + $('connectBtn').addEventListener('click',connect); + $('registerBtn').addEventListener('click',doRegister); + var ub=$('upgradeBtn'); if(ub) ub.addEventListener('click',doUpgrade); + if(eth && eth.on){ eth.on('accountsChanged',function(){location.reload();}); eth.on('chainChanged',function(){location.reload();}); } + if(!eth) log('No wallet detected in this browser. Use MetaMask (desktop extension) or the MetaMask in-app browser on mobile.','err'); + }); +})(); diff --git a/server.js b/server.js index 93ab59c..ff5b380 100644 --- a/server.js +++ b/server.js @@ -619,7 +619,7 @@ const server=http.createServer(async(req,res)=>{ if((mj=pathname.match(/^\/join\/(\d{1,15})$/)))return serveMemberPage(req,res,path.join(PUBLIC_DIR,'join.html'),'join',mj[1]); } let file; - if(pathname==='/')file=path.join(PUBLIC_DIR,'index.html');else if(pathname==='/start'||pathname==='/start/')file=path.join(PUBLIC_DIR,'start.html');else if(pathname==='/training'||pathname==='/training/')file=path.join(PUBLIC_DIR,'training.html');else if(pathname==='/admin'||pathname==='/admin/')file=path.join(PUBLIC_DIR,'admin.html');else if(pathname==='/my'||pathname==='/my/'||/^\/my\/\d{1,15}$/.test(pathname))file=path.join(PUBLIC_DIR,'my.html');else if(pathname==='/contract'||pathname==='/contract/')file=path.join(PUBLIC_DIR,'contract.html');else if(pathname==='/disclaimer'||pathname==='/disclaimer/')file=path.join(PUBLIC_DIR,'disclaimer.html');else if(pathname==='/how-pay-works'||pathname==='/how-pay-works/')file=path.join(PUBLIC_DIR,'how-pay-works.html');else if(pathname==='/tools'||pathname==='/tools/')file=path.join(PUBLIC_DIR,'tools.html');else if(/^\/join\/\d{1,15}$/.test(pathname))file=path.join(PUBLIC_DIR,'join.html');else if(pathname==='/join'||pathname==='/join/'){res.writeHead(302,{Location:'/start'});return res.end();}else{ + if(pathname==='/')file=path.join(PUBLIC_DIR,'index.html');else if(pathname==='/start'||pathname==='/start/')file=path.join(PUBLIC_DIR,'start.html');else if(pathname==='/training'||pathname==='/training/')file=path.join(PUBLIC_DIR,'training.html');else if(pathname==='/admin'||pathname==='/admin/')file=path.join(PUBLIC_DIR,'admin.html');else if(pathname==='/my'||pathname==='/my/'||/^\/my\/\d{1,15}$/.test(pathname))file=path.join(PUBLIC_DIR,'my.html');else if(pathname==='/contract'||pathname==='/contract/')file=path.join(PUBLIC_DIR,'contract.html');else if(pathname==='/disclaimer'||pathname==='/disclaimer/')file=path.join(PUBLIC_DIR,'disclaimer.html');else if(pathname==='/how-pay-works'||pathname==='/how-pay-works/')file=path.join(PUBLIC_DIR,'how-pay-works.html');else if(pathname==='/tools'||pathname==='/tools/')file=path.join(PUBLIC_DIR,'tools.html');else if(pathname==='/direct-join'||pathname==='/direct-join/'){if(!getSession(req)){res.writeHead(302,{Location:'/admin'});return res.end();}file=path.join(ROOT,'private','direct-join.html');}else if(/^\/join\/\d{1,15}$/.test(pathname))file=path.join(PUBLIC_DIR,'join.html');else if(pathname==='/join'||pathname==='/join/'){res.writeHead(302,{Location:'/start'});return res.end();}else{ const safe=path.normalize(pathname).replace(/^([.][.][/\\])+/, '').replace(/^[/\\]+/,'');file=path.join(PUBLIC_DIR,safe);if(!file.startsWith(PUBLIC_DIR))file=''; } if(file&&staticFile(req,res,file))return;return staticFile(req,res,path.join(PUBLIC_DIR,'404.html'),404);