diff --git a/chain.js b/chain.js index d52b2e1..8d6bb7d 100644 --- a/chain.js +++ b/chain.js @@ -173,6 +173,7 @@ function decodeLog(log) { function loadState() { try { state = JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')); } catch (e) { state = null; } if (!state || state.v !== 1) state = { v: 1, lastBlock: getConfig().deployBlock - 1, events: [] }; + if (!state.totals) { for (const ev of state.events) tally(ev); saveState(); } // one-time backfill } function saveState() { try { @@ -194,6 +195,7 @@ async function tail() { const ev = decodeLog(lg); if (!ev) continue; state.events.push(ev); + tally(ev); if (onEvent) { try { onEvent(ev); } catch (e) { console.error('chain onEvent', e.message); } } } if (state.events.length > KEEP_EVENTS) state.events = state.events.slice(-KEEP_EVENTS); @@ -205,6 +207,19 @@ async function tail() { } function recentEvents(n) { return state ? state.events.slice(-(n || 100)).reverse() : []; } +// running totals for the public counters — every number provable on-chain +function tally(ev) { + if (!state.totals) state.totals = { purchases: 0, paidInWei: '0', payouts: 0, payoutWei: '0', activations: 0 }; + const t = state.totals; + if (ev.type === 'Purchase') { t.purchases += 1; t.paidInWei = (BigInt(t.paidInWei) + BigInt(ev.paidWei)).toString(); } + if (ev.type === 'TierPaid') { t.payouts += 1; t.payoutWei = (BigInt(t.payoutWei) + BigInt(ev.amountWei)).toString(); } + if (ev.type === 'AwardPaid') { t.payouts += 1; t.payoutWei = (BigInt(t.payoutWei) + BigInt(ev.amountWei)).toString(); } + if (ev.type === 'MemberActivated') t.activations += 1; +} +function totals() { + return state && state.totals ? state.totals : { purchases: 0, paidInWei: '0', payouts: 0, payoutWei: '0', activations: 0 }; +} + function init(opts) { if (opts && opts.onEvent) onEvent = opts.onEvent; getConfig(); loadState(); @@ -213,4 +228,4 @@ function init(opts) { } module.exports = { init, getConfig, reloadConfig, memberIdByAccount, memberCount, member, - product, productCount, quoteWei, creditBalance, catalog, recentEvents, rpc }; + product, productCount, quoteWei, creditBalance, catalog, recentEvents, totals, rpc }; diff --git a/public/assets/home.js b/public/assets/home.js index e8e99d5..ad65e59 100644 --- a/public/assets/home.js +++ b/public/assets/home.js @@ -46,5 +46,26 @@ IAP.status('Purchase failed: ' + (e.message || e), 'bad'); } finally { btn.disabled = false; } } + async function loadStats() { + try { + const s = await (await fetch('/api/stats')).json(); + IAP.$('stMembers').textContent = (s.onchainMembers || 0).toLocaleString(); + IAP.$('stPurchases').textContent = (s.purchases || 0).toLocaleString(); + IAP.$('stPaid').textContent = IAP.fmtPol(s.paidInWei || '0'); + IAP.$('stPayouts').textContent = (s.payouts || 0).toLocaleString(); + } catch (e) {} + } + async function loadTicker() { + try { + const { events } = await (await fetch('/api/feed?n=30')).json(); + if (!events.length) return; + const inner = IAP.$('tickerInner'); + inner.innerHTML = events.map(ev => '' + IAP.describeEvent(ev, c) + '').join(''); + IAP.$('ticker').hidden = false; + } catch (e) {} + } loadLadder(); + loadStats(); + loadTicker(); + setInterval(loadStats, 60000); })(); diff --git a/public/assets/site.css b/public/assets/site.css index 53ee7e8..f41eb59 100644 --- a/public/assets/site.css +++ b/public/assets/site.css @@ -1,82 +1,131 @@ -/* InstantAdPay — site-wide styles. - Identity: "wire-transfer receipt meets neon ledger" — dark bank-slate ground, - electric mint for money-in-motion, warm amber for calls to action. */ +/* InstantAdPay visual system v2 — "commercial ad-network polish x DeFi flash". + Refs: Adsterra/PropellerAds (dual-audience hero, real-number counters, + format showcase) x DeFi landing patterns (aurora glows, glass cards, + bento grids, ticker heartbeat). Dark luxe ground; mint = money in motion; + amber gradient = action. Display face: Sora. */ :root{ - --ground:#0d1420; --panel:#141d2e; --panel2:#1a2538; --line:#26344d; - --ink:#e8eef7; --muted:#8fa1bb; --mint:#3ee6a8; --mint-soft:#10362b; - --amber:#f5b83d; --amber-ink:#0d1420; --bad:#ff8f7d; --mono:"Consolas","JetBrains Mono",monospace; + --ground:#070b16; --panel:rgba(19,28,48,.66); --panel-solid:#131c30; + --line:rgba(126,144,186,.16); --line-strong:rgba(126,144,186,.32); + --ink:#eef2fb; --muted:#8b9ab5; + --mint:#3de8a8; --mint-dim:#1c5b44; --mint-soft:rgba(61,232,168,.1); + --amber:#ffb238; --amber2:#ff7a45; --amber-ink:#160f04; + --violet:#8b6cf6; --cyan:#5cc8ee; --bad:#ff8f7d; + --mono:"Consolas","JetBrains Mono",monospace; + --disp:"Sora","Segoe UI",system-ui,sans-serif; + --radius:16px; } *{box-sizing:border-box} -body{margin:0;background:var(--ground);color:var(--ink);font:16px/1.55 "Segoe UI",system-ui,sans-serif} +body{margin:0;background:var(--ground);color:var(--ink);font:16px/1.6 "Segoe UI",system-ui,sans-serif;overflow-x:hidden} +/* aurora ground: two blurred glow fields, fixed behind everything */ +body::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none; + background: + radial-gradient(560px 420px at 82% -6%, rgba(139,108,246,.22), transparent 65%), + radial-gradient(640px 460px at -8% 34%, rgba(61,232,168,.13), transparent 65%), + radial-gradient(520px 420px at 58% 108%, rgba(255,122,69,.10), transparent 65%);} a{color:var(--mint);text-decoration:none} a:hover{text-decoration:underline} -.wrap{max-width:1020px;margin:0 auto;padding:0 18px} +.wrap{max-width:1060px;margin:0 auto;padding:0 20px} +h1,h2,h3{font-family:var(--disp);letter-spacing:-.01em} /* nav */ -nav{border-bottom:1px solid var(--line);background:rgba(13,20,32,.92);position:sticky;top:0;z-index:10} -nav .wrap{display:flex;align-items:center;gap:22px;min-height:58px;flex-wrap:wrap;padding-top:6px;padding-bottom:6px} -.logo{font-weight:800;font-size:19px;color:var(--ink)} -.logo b{color:var(--amber)} -nav .links{display:flex;gap:18px;font-size:14.5px;flex:1} +nav{border-bottom:1px solid var(--line);background:rgba(7,11,22,.72);backdrop-filter:blur(14px); + -webkit-backdrop-filter:blur(14px);position:sticky;top:0;z-index:10} +nav .wrap{display:flex;align-items:center;gap:22px;min-height:62px;flex-wrap:wrap;padding-top:6px;padding-bottom:6px} +.logo{font-family:var(--disp);font-weight:800;font-size:20px;color:var(--ink)} +.logo b{background:linear-gradient(100deg,var(--amber),var(--amber2));-webkit-background-clip:text;background-clip:text;color:transparent} +nav .links{display:flex;gap:20px;font-size:14.5px;flex:1;flex-wrap:wrap} nav a{color:var(--muted)} nav a:hover{color:var(--ink);text-decoration:none} #navWallet{font-size:13.5px} -.rehearsal{background:#3d2a52;color:#c9a9f7;text-align:center;font-size:13px;padding:6px 10px} -.rehearsal b{color:#e6d5ff} +.rehearsal{background:linear-gradient(90deg,#2c2150,#1c3050);color:#c9b8f7;text-align:center;font-size:13px;padding:7px 12px} +.rehearsal b{color:#eadfff} /* buttons */ -.btn{display:inline-block;background:var(--amber);color:var(--amber-ink);border:0;border-radius:10px; - padding:12px 22px;font-weight:800;font-size:15.5px;cursor:pointer;font-family:inherit} -.btn.sec{background:transparent;color:var(--mint);border:2px solid var(--mint);padding:10px 20px} -.btn.small{padding:8px 14px;font-size:13.5px} -.btn:disabled{opacity:.45;cursor:default} -/* layout blocks */ -.hero{padding:64px 0 40px} -.hero h1{font-size:clamp(30px,5.5vw,50px);line-height:1.08;margin:0 0 14px;max-width:640px} -.hero h1 em{color:var(--mint);font-style:normal} -.hero p.lead{color:var(--muted);font-size:18px;max-width:560px;margin:0 0 26px} -.card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:20px;margin:0 0 16px} +.btn{display:inline-block;background:linear-gradient(100deg,var(--amber),var(--amber2));color:var(--amber-ink); + border:0;border-radius:999px;padding:13px 26px;font-weight:800;font-size:15.5px;cursor:pointer;font-family:var(--disp); + box-shadow:0 6px 24px rgba(255,150,60,.28);transition:transform .15s ease,box-shadow .15s ease} +.btn:hover{transform:translateY(-2px);box-shadow:0 10px 30px rgba(255,150,60,.4);text-decoration:none} +.btn.sec{background:transparent;color:var(--mint);border:1.5px solid var(--mint);box-shadow:none} +.btn.sec:hover{box-shadow:0 6px 22px rgba(61,232,168,.22)} +.btn.small{padding:8px 16px;font-size:13.5px} +.btn:disabled{opacity:.45;cursor:default;transform:none;box-shadow:none} +/* hero */ +.hero{padding:76px 0 40px} +.eyebrow{font-family:var(--mono);font-size:12px;letter-spacing:.22em;text-transform:uppercase;color:var(--mint);margin:0 0 14px} +.hero h1{font-size:clamp(32px,5.6vw,58px);font-weight:800;line-height:1.06;margin:0 0 18px;max-width:720px;text-wrap:balance} +.hero h1 em{font-style:normal;background:linear-gradient(95deg,var(--mint),var(--cyan) 55%,var(--violet)); + -webkit-background-clip:text;background-clip:text;color:transparent} +.hero p.lead{color:var(--muted);font-size:19px;max-width:600px;margin:0 0 30px} +.hero p.lead b{color:var(--ink)} +/* live counter band — real chain numbers only */ +.stats{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;margin:38px 0 8px} +@media(min-width:760px){.stats{grid-template-columns:repeat(4,1fr)}} +.stat{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:18px 20px; + backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)} +.stat .n{font-family:var(--disp);font-size:30px;font-weight:800;font-variant-numeric:tabular-nums;color:var(--mint)} +.stat .l{font-size:12.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-top:2px} +.stat .sub{font-size:11.5px;color:var(--muted)} +/* cards / grids */ +.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:22px;margin:0 0 16px; + backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)} +.card:hover{border-color:var(--line-strong)} .grid{display:grid;gap:16px} @media(min-width:760px){.grid.c3{grid-template-columns:1fr 1fr 1fr}.grid.c2{grid-template-columns:1fr 1fr}} -h2{font-size:24px;margin:36px 0 14px} -h3{font-size:17px;margin:0 0 8px} +h2{font-size:clamp(22px,3vw,30px);font-weight:800;margin:52px 0 16px;text-wrap:balance} +h3{font-size:17px;margin:0 0 8px;font-weight:700} .muted{color:var(--muted)} .small{font-size:13.5px} .mono{font-family:var(--mono)} +/* ticker: live money marquee */ +.ticker{overflow:hidden;border-top:1px solid var(--line);border-bottom:1px solid var(--line); + background:rgba(61,232,168,.05);white-space:nowrap;padding:9px 0;margin:26px 0 0} +.ticker .inner{display:inline-block;font-family:var(--mono);font-size:13px;color:var(--mint);padding-left:100%; + animation:tick 40s linear infinite} +.ticker .inner span{margin-right:44px;color:var(--muted)} +.ticker .inner span b{color:var(--mint);font-weight:600} +@keyframes tick{to{transform:translateX(-100%)}} +@media(prefers-reduced-motion:reduce){.ticker .inner{animation:none;padding-left:0}} /* ladder table */ table{width:100%;border-collapse:collapse;font-size:15px} -th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:middle} -th{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:1px} +th,td{text-align:left;padding:12px 14px;border-bottom:1px solid var(--line);vertical-align:middle} +th{color:var(--muted);font-size:11.5px;text-transform:uppercase;letter-spacing:.1em;font-family:var(--disp)} td.num,th.num{font-variant-numeric:tabular-nums} tr:last-child td{border-bottom:0} .tablewrap{overflow-x:auto} /* ledger feed */ .feed{font-family:var(--mono);font-size:13.5px;line-height:1.7} -.feed .row{padding:7px 10px;border-bottom:1px solid var(--line);display:flex;gap:10px;align-items:baseline;flex-wrap:wrap} +.feed .row{padding:8px 12px;border-bottom:1px solid var(--line);display:flex;gap:10px;align-items:baseline;flex-wrap:wrap} .feed .row:first-child{background:var(--mint-soft)} .feed .t-Purchase{color:var(--amber)} .feed .t-TierPaid{color:var(--mint)} .feed .t-AdminPaid{color:var(--muted)} .feed .t-AwardPaid{color:var(--amber)} .feed .tx a{color:var(--muted);font-size:12px} -.badge{display:inline-block;background:var(--mint-soft);color:var(--mint);border-radius:10px;padding:2px 10px;font-size:12.5px;font-weight:700} -.badge.amber{background:#3c2f10;color:var(--amber)} -/* split diagram strip */ -.split{display:flex;gap:8px;margin:14px 0} -.split div{border-radius:8px;padding:10px 6px;text-align:center;font-size:12.5px;font-weight:700} -.split .s50{flex:5;background:var(--mint-soft);color:var(--mint)} -.split .s20{flex:2;background:#173347;color:#6cc4ee} -.split .s10{flex:1;background:#2c2440;color:#b39df1} -.split .sa{flex:2;background:#33290f;color:var(--amber)} -/* status line + toasts */ -#status{position:fixed;left:50%;transform:translateX(-50%);bottom:22px;background:var(--panel2); - border:1px solid var(--line);border-radius:12px;padding:12px 20px;font-size:14px;max-width:90vw; - box-shadow:0 8px 30px rgba(0,0,0,.5)} +.badge{display:inline-block;background:var(--mint-soft);color:var(--mint);border:1px solid rgba(61,232,168,.3); + border-radius:999px;padding:2px 12px;font-size:12.5px;font-weight:700} +.badge.amber{background:rgba(255,178,56,.1);border-color:rgba(255,178,56,.35);color:var(--amber)} +/* split strip */ +.split{display:flex;gap:8px;margin:16px 0} +.split div{border-radius:10px;padding:13px 6px;text-align:center;font-size:12.5px;font-weight:700;font-family:var(--disp)} +.split .s50{flex:5;background:var(--mint-soft);color:var(--mint);border:1px solid rgba(61,232,168,.3); + box-shadow:0 0 24px rgba(61,232,168,.12)} +.split .s20{flex:2;background:rgba(92,200,238,.09);color:var(--cyan);border:1px solid rgba(92,200,238,.3)} +.split .s10{flex:1;background:rgba(139,108,246,.1);color:var(--violet);border:1px solid rgba(139,108,246,.3)} +.split .sa{flex:2;background:rgba(255,178,56,.08);color:var(--amber);border:1px solid rgba(255,178,56,.28)} +/* FAQ accordions */ +details{border:1px solid var(--line);border-radius:12px;margin:0 0 10px;background:var(--panel)} +summary{cursor:pointer;padding:14px 18px;font-weight:700;font-family:var(--disp);font-size:15px} +details p{margin:0;padding:0 18px 16px;color:var(--muted);font-size:14.5px} +/* status toast */ +#status{position:fixed;left:50%;transform:translateX(-50%);bottom:22px;background:var(--panel-solid); + border:1px solid var(--line-strong);border-radius:14px;padding:13px 22px;font-size:14px;max-width:90vw;z-index:30; + box-shadow:0 12px 40px rgba(0,0,0,.55)} #status.ok{border-color:var(--mint)} #status.bad{border-color:var(--bad)} -footer{border-top:1px solid var(--line);margin-top:60px;padding:26px 0;color:var(--muted);font-size:13.5px} -input,select{background:var(--ground);border:1px solid var(--line);color:var(--ink);border-radius:8px; - padding:10px 12px;font-size:14.5px;font-family:inherit} +footer{border-top:1px solid var(--line);margin-top:70px;padding:30px 0;color:var(--muted);font-size:13.5px} +input,select{background:rgba(7,11,22,.6);border:1px solid var(--line-strong);color:var(--ink);border-radius:10px; + padding:11px 14px;font-size:14.5px;font-family:inherit} +input:focus,select:focus{border-color:var(--mint)} :focus-visible{outline:2px solid var(--mint);outline-offset:2px} @media(prefers-reduced-motion:no-preference){ .feed .row:first-child{animation:landed .9s ease} - @keyframes landed{from{background:#1d5c44}to{background:var(--mint-soft)}} + @keyframes landed{from{background:rgba(61,232,168,.28)}to{background:var(--mint-soft)}} } diff --git a/public/index.html b/public/index.html index e001655..158a30e 100644 --- a/public/index.html +++ b/public/index.html @@ -4,11 +4,13 @@ InstantAdPay: advertise and earn, locked in code +
+

Advertising · settled on-chain · instantly

Advertise and earn. Locked in code, not promises.

Buy real ad packages from 5 to 250 dollars. Every purchase settles through a smart contract you can read yourself, and your earnings land in @@ -18,8 +20,21 @@ Watch payments land live

-
+
+
–
On-chain members
+
–
Packages bought
+
–
POL settled through
+
–
Instant payouts delivered
+
+

Every number above comes from the blockchain, not a marketing database. + Click any line on the live ledger to verify it yourself.

+ +
+ + + +

Where every dollar goes

@@ -49,7 +64,7 @@ up to the next qualified person automatically. The contract handles it.

-

The ad packages

+

The ad packages

@@ -62,6 +77,16 @@ Packages of $20 or more count toward your sponsor's qualification.

+

What your credits buy

+
+

Display banners

All standard sizes, served across the network. Per impression.

live
+

Text ads

Headline plus one line, placed where members actually look. Per impression.

live
+

Login ads

Full attention at sign-in, priced per day.

live
+

Inbox ads

Delivered on-site, and viewers earn credits for reading.

shipping soon
+

Featured rotation

Your link in the featured rotation for 1, 2, or 7 days. We show you exactly how many links share it before you buy.

shipping soon
+

Verified visits

Buy a guaranteed count of verified unique visits. No bots, no recycled clicks.

shipping soon
+
+

Why this is different

No trust required. Your earnings are a transaction to your wallet.

@@ -76,6 +101,41 @@ code does: split the money and pay it out. That is it.

+

Questions people actually ask

+
+
Is this a pyramid or ponzi scheme? +

No, and here is the plain version of why. A pyramid scheme pays you just for recruiting, with no + real product behind it. Here every payment is for ad delivery, and the payouts only come from real + buyers who buy real ad packages. You earn from people actually advertising, not from building a chain + of signups. The whole thing runs on a public ledger you can verify yourself, so nothing stays hidden.

+
What happens if the company disappears? +

The important part is that there is no company holding the money. Every sale is split by the smart + contract itself, on the blockchain, in the same transaction. Nobody can pause it, change it, or run + off with the funds, because the code does the paying automatically. If the site vanished tomorrow, + the contract would keep paying exactly what it is set to pay.

+
Do I need crypto experience or a wallet to join? +

No. You can start with a free email signup and use the platform the same way you would use any ad + site. If you want payouts to a wallet, setting one up takes a few minutes and the platform walks you + through it. But you do not need to know anything about crypto to get in and get moving.

+
How fast do I really get paid? +

Payouts happen inside the very transaction that pays for the package. There is no approval step + and nobody holding funds. Your share lands in your own wallet the moment your referral's purchase + confirms. The public ledger lets you watch each one go through.

+
What am I actually buying? +

You are buying ad delivery. Credits are one cent of delivery each, recorded on-chain when you buy. + The 5 dollar package mints 500 credits, and the bigger packages mint bonus credits on top, up to + 32,500 on the 250 dollar package. That is the product, plain and simple. Your earnings come from + referring people who also buy real ad delivery, and the contract splits every purchase automatically.

+
+ +
+

See a payment land before you decide.

+

The ledger is open to everyone. + Watch real purchases split and settle, then join free when you have seen enough.

+ Join free + Open the live ledger +
+
InstantAdPay · every payment verifiable on-chain · live ledger · view the contract ↗
Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.
diff --git a/public/ledger.html b/public/ledger.html index 55e1040..5447535 100644 --- a/public/ledger.html +++ b/public/ledger.html @@ -4,6 +4,7 @@ Live ledger | InstantAdPay + diff --git a/public/my.html b/public/my.html index 872e45f..37af4d5 100644 --- a/public/my.html +++ b/public/my.html @@ -3,6 +3,7 @@ My account | InstantAdPay + diff --git a/server.js b/server.js index b47a5a6..984d467 100644 --- a/server.js +++ b/server.js @@ -43,7 +43,7 @@ function siteConfig() { const MIME = { '.html': 'text/html; charset=utf-8', '.css': 'text/css', '.js': 'text/javascript', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml', '.webp': 'image/webp', '.ico': 'image/x-icon', '.json': 'application/json', '.mp4': 'video/mp4', '.woff2': 'font/woff2' }; -const CSP = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; form-action 'self'"; +const CSP = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; connect-src 'self'; font-src 'self' data: https://fonts.gstatic.com; form-action 'self'"; function baseHeaders(extra) { return Object.assign({ 'Content-Security-Policy': CSP, 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'strict-origin-when-cross-origin' }, extra || {}); @@ -135,7 +135,7 @@ const server = http.createServer(async (req, res) => { } if (p === '/api/stats' && req.method === 'GET') { let members = 0; try { members = await chain.memberCount(); } catch (e) {} - return json(res, 200, { onchainMembers: members, siteAccounts: accounts.count() }); + return json(res, 200, Object.assign({ onchainMembers: members, siteAccounts: accounts.count() }, chain.totals())); } // -- accounts: email + password is the normal join path (wallet comes