Initial commit: WalletPress v1.0.0-beta

Self-hosted wallet management platform for WordPress:
- WP plugin: vault, generate, token gates, payments, wallet login, 15 admin pages
- CLI: 17 commands covering all backend feature groups
- Landing pages + PDF documentation
- Connects to rmi-backend API (86+ endpoints, 29+ chains)
This commit is contained in:
Crypto Rug Munch 2026-06-27 17:12:49 +07:00
commit ba8d1f1261
24 changed files with 5824 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.env
node_modules/
.DS_Store
*.log

274
about.html Normal file
View file

@ -0,0 +1,274 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About — WalletPress · Built by Rug Munch Media</title>
<meta name="description" content="WalletPress is built by Rug Munch Media LLC — a Wyoming-based crypto infrastructure company. Self-hosted, no DRM, developer-first.">
<meta name="robots" content="noindex">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔐</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,510;14..32,590;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg:#08090a;--bg2:#0c0d0f;--bg3:#121317;--bg4:#181a1f;
--border:rgba(255,255,255,0.06);--border2:rgba(255,255,255,0.09);
--fg:#f7f8f8;--fg2:#9ca0a8;--fg3:#5f636b;
--accent:#f0a030;--accent2:#d48c1e;--accent-glow:rgba(240,160,48,0.12);--accent-glow2:rgba(240,160,48,0.2);
--cyan:#22d3ee;--purple:#8b5cf6;--green:#22c55e;
--radius:6px;--radius-lg:12px;--radius-xl:16px;--radius-pill:9999px;--nav-h:52px;
}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;font-feature-settings:'cv01','ss03';overflow-x:hidden}
::selection{background:rgba(240,160,48,0.3);color:var(--fg)}
img{display:block;max-width:100%}
.reveal{opacity:0;transform:translateY(24px);transition:opacity .6s ease-out,transform .6s ease-out}
.reveal.visible{opacity:1;transform:translateY(0)}
/* NAV */
nav{position:fixed;top:0;left:0;right:0;z-index:100;height:var(--nav-h);background:rgba(8,9,10,0.82);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-bottom:1px solid var(--border)}
.nav-inner{max-width:1200px;margin:0 auto;padding:0 20px;height:100%;display:flex;align-items:center;justify-content:space-between;gap:16px}
.logo{display:flex;align-items:center;gap:8px;font-weight:700;font-size:18px;color:var(--fg);text-decoration:none;letter-spacing:-0.02em}
.logo-dot{width:8px;height:8px;border-radius:50%;background:var(--accent)}
.nav-links{display:flex;align-items:center;gap:24px}
.nav-links a{color:var(--fg2);text-decoration:none;font-size:13px;font-weight:510;transition:color .15s}
.nav-links a:hover{color:var(--fg)}
.nav-links .active{color:var(--accent)}
.nav-cta{padding:6px 16px!important;border-radius:var(--radius)!important;background:var(--accent)!important;color:#08090a!important;font-weight:590!important;font-size:13px!important}
.nav-cta:hover{background:var(--accent2)!important;color:#08090a!important}
.hamburger{display:none;flex-direction:column;gap:4px;cursor:pointer;padding:8px;background:none;border:none;z-index:101}
.hamburger span{display:block;width:20px;height:1.5px;background:var(--fg2);transition:.2s}
.hamburger.open span:nth-child(1){transform:rotate(45deg) translate(4px,4px)}
.hamburger.open span:nth-child(2){opacity:0}
.hamburger.open span:nth-child(3){transform:rotate(-45deg) translate(4px,-4px)}
.mobile-menu{display:none;position:fixed;top:var(--nav-h);left:0;right:0;bottom:0;background:var(--bg);z-index:99;padding:32px 20px;flex-direction:column;gap:20px}
.mobile-menu.open{display:flex}
.mobile-menu a{color:var(--fg2);text-decoration:none;font-size:18px;font-weight:510}
.mobile-menu .nav-cta{display:inline-block;text-align:center;margin-top:8px}
/* BUTTONS */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:12px 24px;border-radius:var(--radius);font-size:14px;font-weight:590;text-decoration:none;transition:all .2s;cursor:pointer;border:none;font-family:inherit;letter-spacing:-0.01em}
.btn-primary{background:var(--accent);color:#08090a}
.btn-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 8px 24px var(--accent-glow)}
.btn-secondary{background:rgba(255,255,255,0.04);color:var(--fg);border:1px solid var(--border2)}
.btn-secondary:hover{background:rgba(255,255,255,0.07)}
.btn-lg{padding:14px 28px;font-size:15px}
/* SECTIONS */
section{padding:80px 20px}
@media(min-width:768px){section{padding:100px 32px}}
section.alt{background:var(--bg2);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.container{max-width:1100px;margin:0 auto}
/* HERO */
.hero{min-height:35vh;display:flex;align-items:center;justify-content:center;text-align:center;padding:120px 20px 60px;position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;top:-30%;left:-50%;width:200%;height:160%;background:radial-gradient(ellipse at 50% 0%,var(--accent-glow),transparent 50%);opacity:0.2;pointer-events:none}
.hero-content{position:relative;z-index:1;max-width:600px}
.hero-breadcrumb{font-size:12px;color:var(--fg3);margin-bottom:16px}
.hero-breadcrumb a{color:var(--fg3);text-decoration:none;transition:color .15s}
.hero-breadcrumb a:hover{color:var(--fg2)}
.hero h1{font-size:clamp(28px,5vw,44px);font-weight:700;line-height:1.1;letter-spacing:-0.03em;margin-bottom:12px}
.hero p{color:var(--fg2);font-size:15px;margin:0 auto;line-height:1.6;font-weight:400}
/* MISSION */
.mission-grid{display:grid;grid-template-columns:1fr;gap:20px;max-width:900px;margin:0 auto}
@media(min-width:640px){.mission-grid{grid-template-columns:repeat(3,1fr)}}
.mission-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:32px 28px;text-align:center;transition:all .25s}
.mission-card:hover{border-color:var(--border2);transform:translateY(-2px)}
.mission-icon{font-size:32px;margin-bottom:16px}
.mission-card h3{font-size:16px;font-weight:590;margin-bottom:8px}
.mission-card p{font-size:13px;color:var(--fg2);line-height:1.6}
/* TRUST */
.trust-grid{display:grid;grid-template-columns:1fr;gap:16px;max-width:800px;margin:0 auto}
@media(min-width:640px){.trust-grid{grid-template-columns:repeat(2,1fr)}}
.trust-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:28px;display:flex;gap:16px;align-items:flex-start;transition:all .25s}
.trust-card:hover{border-color:var(--border2)}
.trust-card .icon{font-size:24px;flex-shrink:0;margin-top:2px}
.trust-card h4{font-size:14px;font-weight:590;margin-bottom:4px}
.trust-card p{font-size:12px;color:var(--fg2);line-height:1.6}
/* TEAM */
.team-badge{max-width:700px;margin:0 auto;background:linear-gradient(135deg,rgba(240,160,48,0.04),rgba(139,92,246,0.04));border:1px solid var(--border2);border-radius:var(--radius-xl);padding:36px 40px;text-align:center}
.team-badge h3{font-size:20px;font-weight:700;margin-bottom:8px;letter-spacing:-0.01em}
.team-badge p{font-size:14px;color:var(--fg2);line-height:1.7;max-width:500px;margin:0 auto 16px}
.team-badge .location{font-size:12px;color:var(--fg3)}
/* SECTION HEADER */
.section-header{text-align:center;margin-bottom:48px}
.section-label{display:inline-block;font-size:11px;font-weight:590;text-transform:uppercase;letter-spacing:0.08em;color:var(--accent);margin-bottom:12px}
.section-header h2{font-size:clamp(24px,4vw,34px);font-weight:700;letter-spacing:-0.02em;margin-bottom:12px}
.section-header p{color:var(--fg2);font-size:14px;max-width:480px;margin:0 auto}
/* CTA */
.cta-band{text-align:center;padding:64px 20px;background:var(--bg2);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.cta-band h2{font-size:clamp(22px,4vw,30px);font-weight:700;margin-bottom:12px;letter-spacing:-0.02em}
.cta-band p{color:var(--fg2);font-size:15px;margin-bottom:24px}
/* FOOTER */
footer{border-top:1px solid var(--border);padding:40px 20px;text-align:center}
.footer-grid{display:flex;justify-content:center;flex-wrap:wrap;gap:24px;margin-bottom:20px}
.footer-grid a{color:var(--fg3);text-decoration:none;font-size:12px;font-weight:510;transition:color .15s}
.footer-grid a:hover{color:var(--fg2)}
footer .copy{color:var(--fg3);font-size:11px}
@media(max-width:768px){
.nav-links{display:none}
.hamburger{display:flex}
.hero h1{font-size:28px}
section{padding:60px 16px}
.team-badge{padding:28px 24px}
}
</style>
</head>
<body>
<nav>
<div class="nav-inner">
<a href="/index.html" class="logo"><span class="logo-dot"></span>WalletPress</a>
<div class="nav-links">
<a href="/index.html">Home</a>
<a href="/features.html">Features</a>
<a href="/docs.html">Docs</a>
<a href="/about.html" class="active">About</a>
<a href="/buy.html" class="nav-cta">Buy — $49</a>
</div>
<button class="hamburger" onclick="document.getElementById('mobile-menu').classList.toggle('open');this.classList.toggle('open')" aria-label="Menu">
<span></span><span></span><span></span>
</button>
</div>
</nav>
<div class="mobile-menu" id="mobile-menu">
<a href="/index.html">Home</a>
<a href="/features.html">Features</a>
<a href="/docs.html">Docs</a>
<a href="/about.html">About</a>
<a href="/buy.html" class="nav-cta">Buy — $49</a>
</div>
<section class="hero">
<div class="hero-content">
<div class="hero-breadcrumb"><a href="/index.html">Home</a> / About</div>
<h1>Built for Developers Who Build on Chain</h1>
<p>WalletPress is a product of Rug Munch Media LLC — a Wyoming-based crypto infrastructure company. We build tools for the sovereign developer.</p>
</div>
</section>
<section class="alt">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Our Mission</div>
<h2>Why WalletPress Exists</h2>
<p>We got tired of stitching together wallet libraries, debugging chain-specific quirks, and paying per-API-call. So we built the tool we wanted.</p>
</div>
<div class="mission-grid">
<div class="mission-card">
<div class="mission-icon">🔑</div>
<h3>Sovereign Infrastructure</h3>
<p>Your server, your keys, your rules. No cloud dependency, no telemetry, no kill switch. You own the tool — forever.</p>
</div>
<div class="mission-card">
<div class="mission-icon">💰</div>
<h3>Fair Pricing</h3>
<p>$49 one-time. Not per wallet, not per month. We believe developer tools should be bought, not rented. Pay once, use forever.</p>
</div>
<div class="mission-card">
<div class="mission-icon">🔓</div>
<h3>No DRM</h3>
<p>Full source code included. No phone-home, no license validation, no activation servers. We trust developers — and we prove it.</p>
</div>
</div>
</div>
</section>
<section>
<div class="container reveal">
<div class="section-header">
<div class="section-label">Trust & Transparency</div>
<h2>What you can count on</h2>
</div>
<div class="trust-grid">
<div class="trust-card">
<div class="icon">🏛️</div>
<div>
<h4>Registered US Company</h4>
<p>Rug Munch Media LLC is a legally registered Wyoming limited liability company. We operate under US law with full compliance.</p>
</div>
</div>
<div class="trust-card">
<div class="icon">📜</div>
<div>
<h4>Full Source Code</h4>
<p>You receive every line of code. No obfuscation, no binary blobs. Audit it, modify it, extend it — it's yours.</p>
</div>
</div>
<div class="trust-card">
<div class="icon">🔄</div>
<div>
<h4>Lifetime Updates</h4>
<p>All future WalletPress updates are included. New chains, new features, security patches — you get them all, forever.</p>
</div>
</div>
<div class="trust-card">
<div class="icon">🛡️</div>
<div>
<h4>30-Day Refund</h4>
<p>Not satisfied? Full refund within 30 days, no questions asked. We stand behind our product.</p>
</div>
</div>
<div class="trust-card">
<div class="icon">🔒</div>
<div>
<h4>No Data Collection</h4>
<p>WalletPress makes zero external network calls. No analytics, no usage tracking, no error reporting. Your data is yours.</p>
</div>
</div>
<div class="trust-card">
<div class="icon">📡</div>
<div>
<h4>Offline-First</h4>
<p>Once deployed, WalletPress runs entirely offline. No internet required after initial setup. Air-gapped deployments supported.</p>
</div>
</div>
</div>
</div>
</section>
<section class="alt">
<div class="container reveal">
<div class="team-badge">
<h3>Rug Munch Media LLC</h3>
<p>We're a small, focused team building crypto infrastructure tools from Phnom Penh, Cambodia. We ship fast, price fairly, and don't do corporate bullshit.</p>
<p class="location">📍 Phnom Penh, Cambodia &nbsp;·&nbsp; Registered: Wyoming, USA &nbsp;·&nbsp; Est. 2025</p>
</div>
</div>
</section>
<section class="cta-band">
<div class="container reveal">
<h2>Ready to stop renting your wallet infrastructure?</h2>
<p>30 chains. Full source code. $49, forever.</p>
<a href="/buy.html" class="btn btn-primary btn-lg">Buy WalletPress — $49</a>
</div>
</section>
<footer>
<div class="footer-grid">
<a href="/docs.html">Documentation</a>
<a href="/features.html">Features</a>
<a href="/buy.html">Pricing</a>
<a href="/contact.html">Contact</a>
<a href="/about.html">About</a>
</div>
<p class="copy">© 2026 Rug Munch Media LLC. Made in Wyoming.</p>
</footer>
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => { if(e.isIntersecting) e.target.classList.add('visible'); });
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
</script>
</body>
</html>

464
buy.html Normal file
View file

@ -0,0 +1,464 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Buy WalletPress — Checkout · $49 One-Time</title>
<meta name="description" content="Purchase WalletPress — pay with crypto or card. Instant source code delivery. 30 chains, self-hosted, no subscriptions.">
<meta name="robots" content="noindex">
<link rel="icon" href="logo.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,510;14..32,590;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root { --bg: #08090a; --bg2: #0c0d0f; --bg3: #121317; --bg4: #181a1f; --border: rgba(255,255,255,0.06); --border2: rgba(255,255,255,0.09); --fg: #f7f8f8; --fg2: #9ca0a8; --fg3: #5f636b; --accent: #f0a030; --accent2: #d48c1e; --accent-glow: rgba(240,160,48,0.12); --accent-glow2: rgba(240,160,48,0.2); --cyan: #22d3ee; --purple: #8b5cf6; --green: #22c55e; --red: #ef4444; --radius: 6px; --radius-lg: 12px; --radius-xl: 16px; --radius-pill: 9999px; --nav-h: 52px; }
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;overflow-x:hidden}
::selection{background:rgba(240,160,48,0.3);color:var(--fg)}
img,svg{display:block;max-width:100%}
.reveal{opacity:0;transform:translateY(24px);transition:opacity .6s ease-out,transform .6s ease-out}
.reveal.visible{opacity:1;transform:translateY(0)}
nav{position:fixed;top:0;left:0;right:0;z-index:100;height:var(--nav-h);background:rgba(8,9,10,0.82);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-bottom:1px solid var(--border)}
.nav-inner{max-width:1200px;margin:0 auto;padding:0 20px;height:100%;display:flex;align-items:center;justify-content:space-between;gap:16px}
.logo{display:flex;align-items:center;gap:8px;font-weight:700;font-size:18px;color:var(--fg);text-decoration:none;letter-spacing:-0.02em}
.logo img{width:28px;height:28px}
.nav-links{display:flex;align-items:center;gap:24px}
.nav-links a{color:var(--fg2);text-decoration:none;font-size:13px;font-weight:510;transition:color .15s}
.nav-links a:hover{color:var(--fg)}
.nav-cta{padding:6px 16px!important;border-radius:var(--radius)!important;background:var(--accent)!important;color:#08090a!important;font-weight:590!important;font-size:13px!important}
.nav-cta:hover{background:var(--accent2)!important}
.hamburger{display:none;flex-direction:column;gap:4px;cursor:pointer;padding:8px;background:none;border:none;z-index:101}
.hamburger span{display:block;width:20px;height:1.5px;background:var(--fg2);transition:.2s}
.hamburger.open span:nth-child(1){transform:rotate(45deg) translate(4px,4px)}
.hamburger.open span:nth-child(2){opacity:0}
.hamburger.open span:nth-child(3){transform:rotate(-45deg) translate(4px,-4px)}
.mobile-menu{display:none;position:fixed;top:var(--nav-h);left:0;right:0;bottom:0;background:var(--bg);z-index:99;padding:32px 20px;flex-direction:column;gap:20px}
.mobile-menu.open{display:flex}
.mobile-menu a{color:var(--fg2);text-decoration:none;font-size:18px;font-weight:510}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:12px 24px;border-radius:var(--radius);font-size:14px;font-weight:590;text-decoration:none;transition:all .2s;cursor:pointer;border:none;font-family:inherit;letter-spacing:-0.01em}
.btn-primary{background:var(--accent);color:#08090a}
.btn-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 8px 24px var(--accent-glow)}
.btn-secondary{background:rgba(255,255,255,0.04);color:var(--fg);border:1px solid var(--border2)}
.btn-secondary:hover{background:rgba(255,255,255,0.07)}
.btn-lg{padding:14px 28px;font-size:15px}
.btn-outline{background:transparent;color:var(--fg);border:1px solid var(--border2)}
.btn-outline:hover{background:rgba(255,255,255,0.04);border-color:var(--accent-glow2)}
.btn-sm{padding:8px 16px;font-size:12px}
section{padding:80px 20px}
@media(min-width:768px){section{padding:100px 32px}}
.container{max-width:1100px;margin:0 auto}
.hero{min-height:30vh;display:flex;align-items:center;justify-content:center;text-align:center;padding:120px 20px 40px;position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;top:-30%;left:-50%;width:200%;height:160%;background:radial-gradient(ellipse at 50% 0%,var(--accent-glow),transparent 50%);opacity:0.15;pointer-events:none}
.hero-content{position:relative;z-index:1;max-width:600px}
.hero h1{font-size:clamp(28px,5vw,44px);font-weight:700;line-height:1.1;letter-spacing:-0.03em;margin-bottom:12px}
.hero p{color:var(--fg2);font-size:15px;margin:0 auto 28px;line-height:1.6;font-weight:400}
/* Checkout Layout */
.checkout-grid{display:grid;grid-template-columns:1fr;gap:24px;max-width:1000px;margin:0 auto}
@media(min-width:768px){.checkout-grid{grid-template-columns:1fr 1.2fr}}
.order-panel{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:28px;height:fit-content;position:sticky;top:80px}
.order-panel h3{font-size:14px;font-weight:590;text-transform:uppercase;letter-spacing:0.05em;color:var(--fg3);margin-bottom:20px}
.order-item{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-bottom:1px solid var(--border)}
.order-item:last-child{border-bottom:none}
.order-item .name{font-size:14px;color:var(--fg)}
.order-item .desc{font-size:12px;color:var(--fg3);margin-top:2px}
.order-item .price{font-size:14px;font-weight:590;color:var(--fg)}
.order-total{display:flex;justify-content:space-between;align-items:center;padding:16px 0;margin-top:8px;border-top:2px solid var(--border)}
.order-total .name{font-size:16px;font-weight:700}
.order-total .price{font-size:20px;font-weight:700;color:var(--accent)}
.payment-panel{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:28px}
.payment-panel h3{font-size:14px;font-weight:590;text-transform:uppercase;letter-spacing:0.05em;color:var(--fg3);margin-bottom:20px}
/* Payment Method Tabs */
.pay-tabs{display:flex;gap:8px;margin-bottom:24px;border-bottom:1px solid var(--border);padding-bottom:16px}
.pay-tab{padding:10px 20px;border-radius:var(--radius);font-size:13px;font-weight:590;color:var(--fg2);background:transparent;border:1px solid transparent;cursor:pointer;transition:all .15s;font-family:inherit}
.pay-tab:hover{color:var(--fg)}
.pay-tab.active{background:var(--accent-glow);color:var(--accent);border-color:var(--accent-glow2)}
.pay-content{display:none}
.pay-content.active{display:block}
/* Card Payment */
.card-form{display:flex;flex-direction:column;gap:16px}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.form-group{display:flex;flex-direction:column;gap:6px}
.form-group.full{grid-column:1 / -1}
.form-group label{font-size:12px;font-weight:510;color:var(--fg2);text-transform:uppercase;letter-spacing:0.03em}
.form-group input,.form-group select{padding:12px 14px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);font-family:inherit;font-size:14px;transition:all .15s}
.form-group input:focus,.form-group select:focus{outline:none;border-color:var(--accent-glow2);box-shadow:0 0 0 3px var(--accent-glow)}
.form-group input::placeholder{color:var(--fg3)}
/* Crypto Payment */
.chain-select{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-bottom:20px}
@media(min-width:640px){.chain-select{grid-template-columns:repeat(4,1fr)}}
.chain-option{padding:14px 12px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;text-align:center;transition:all .15s}
.chain-option:hover{border-color:var(--border2)}
.chain-option.selected{border-color:var(--accent);background:rgba(240,160,48,0.08)}
.chain-option .icon{font-size:24px;margin-bottom:6px}
.chain-option .name{font-size:12px;font-weight:590;color:var(--fg)}
.chain-option .symbol{font-size:11px;color:var(--fg3);margin-top:2px}
.qr-section{text-align:center;padding:24px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:16px}
.qr-section .qr-wrap{background:#fff;display:inline-block;padding:12px;border-radius:var(--radius);margin-bottom:16px}
.qr-section .qr-wrap img{display:block;width:200px;height:200px}
.qr-section .amount{font-size:18px;font-weight:700;color:var(--accent);margin-bottom:8px}
.qr-section .addr{display:inline-flex;align-items:center;gap:8px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;margin-top:8px;cursor:pointer;transition:all .15s}
.qr-section .addr:hover{border-color:var(--accent-glow2)}
.qr-section .addr code{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--accent)}
.qr-section .addr .copy-btn{font-size:10px;color:var(--fg3);text-transform:uppercase;letter-spacing:0.05em}
.payment-status{text-align:center;padding:20px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-lg)}
.payment-status .status-icon{font-size:32px;margin-bottom:8px}
.payment-status .status-text{font-size:14px;font-weight:590;color:var(--fg);margin-bottom:4px}
.payment-status .status-sub{font-size:12px;color:var(--fg3)}
/* Email capture */
.email-capture{margin-top:24px;padding:20px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-lg)}
.email-capture p{font-size:13px;color:var(--fg2);margin-bottom:12px}
.email-capture .form-row{display:flex;gap:8px}
.email-capture input{flex:1;padding:12px 14px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);color:var(--fg);font-family:inherit;font-size:14px}
.email-capture input:focus{outline:none;border-color:var(--accent-glow2)}
.guarantee{display:inline-flex;align-items:center;gap:8px;background:var(--bg3);border:1px solid var(--border);padding:10px 18px;border-radius:var(--radius-pill);font-size:12px;color:var(--fg2);font-weight:510;margin-top:20px}
.guarantee svg{color:var(--accent)}
footer{border-top:1px solid var(--border);padding:40px 20px;text-align:center}
.footer-grid{display:flex;justify-content:center;flex-wrap:wrap;gap:24px;margin-bottom:20px}
.footer-grid a{color:var(--fg3);text-decoration:none;font-size:12px;font-weight:510;transition:color .15s}
.footer-grid a:hover{color:var(--fg2)}
footer .copy{color:var(--fg3);font-size:11px}
@media(max-width:768px){
.nav-links{display:none}
.hamburger{display:flex}
.hero h1{font-size:26px}
.form-row{grid-template-columns:1fr}
.chain-select{grid-template-columns:repeat(2,1fr)}
}
</style>
</head>
<body>
<nav>
<div class="nav-inner">
<a href="index.html" class="logo"><img src="logo.svg" alt="WalletPress">WalletPress</a>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="features.html">Features</a>
<a href="docs.html">Docs</a>
<a href="about.html">About</a>
<a href="buy.html" class="nav-cta">Buy — $49</a>
</div>
<button class="hamburger" onclick="document.getElementById('mobile-menu').classList.toggle('open');this.classList.toggle('open')" aria-label="Menu">
<span></span><span></span><span></span>
</button>
</div>
</nav>
<div class="mobile-menu" id="mobile-menu">
<a href="index.html">Home</a>
<a href="features.html">Features</a>
<a href="docs.html">Docs</a>
<a href="about.html">About</a>
<a href="buy.html" class="nav-cta">Buy — $49</a>
</div>
<section class="hero">
<div class="hero-content">
<h1>Complete Your Purchase</h1>
<p>One-time payment. Lifetime access. Self-hosted wallet generator.</p>
</div>
</section>
<section>
<div class="checkout-grid">
<div class="order-panel">
<h3>Order Summary</h3>
<div class="order-item">
<div>
<div class="name">WalletPress Self-Hosted</div>
<div class="desc">30 chains, CLI + API, encrypted vault</div>
</div>
<div class="price">$49.00</div>
</div>
<div class="order-item">
<div>
<div class="name">Lifetime Updates</div>
<div class="desc">Free updates for life</div>
</div>
<div class="price" style="color:var(--green)">Included</div>
</div>
<div class="order-item">
<div>
<div class="name">Source Code</div>
<div class="desc">Full Docker Compose project</div>
</div>
<div class="price" style="color:var(--green)">Included</div>
</div>
<div class="order-total">
<span class="name">Total</span>
<span class="price">$49.00</span>
</div>
<div style="margin-top:16px;padding-top:16px;border-top:1px solid var(--border)">
<div class="guarantee" style="margin-top:0">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
30-day money-back guarantee
</div>
</div>
</div>
<div class="payment-panel">
<h3>Payment Method</h3>
<div class="pay-tabs">
<button class="pay-tab active" onclick="switchTab('crypto')">Crypto</button>
<button class="pay-tab" onclick="switchTab('card')">Card</button>
<button class="pay-tab" onclick="switchTab('gumroad')">Gumroad</button>
</div>
<!-- Crypto Payment -->
<div id="crypto-panel" class="pay-content active">
<p style="font-size:13px;color:var(--fg2);margin-bottom:16px">Select a chain and send exactly <strong style="color:var(--accent)" id="crypto-amount-display">$49.00 USD</strong> worth. We'll verify and deliver instantly.</p>
<div id="price-loading" style="text-align:center;padding:12px;color:var(--fg3);font-size:12px">Loading prices...</div>
<div class="chain-select" id="chain-select">
<div class="chain-option selected" data-chain="evm" data-addr="0x1E3AC01d0fdb976179790BDD02823196A92705C9" data-symbol="ETH" onclick="selectChain(this)">
<div class="icon">Ξ</div>
<div class="name">Ethereum</div>
<div class="symbol">ETH / USDC</div>
</div>
<div class="chain-option" data-chain="btc" data-addr="bc1qzkllckr024wqttm4px6egcwsya9tcrgghm46kq" data-symbol="BTC" onclick="selectChain(this)">
<div class="icon"></div>
<div class="name">Bitcoin</div>
<div class="symbol">BTC</div>
</div>
<div class="chain-option" data-chain="sol" data-addr="G1uZFfsdxK2PuH2Em5LhRkEcptxHWe4vy5BZak67QyKv" data-symbol="SOL" onclick="selectChain(this)">
<div class="icon"></div>
<div class="name">Solana</div>
<div class="symbol">SOL / USDC</div>
</div>
<div class="chain-option" data-chain="trx" data-addr="TYSWXWr24QTe3c99V9dtzhe1aZTneE261p" data-symbol="TRX" onclick="selectChain(this)">
<div class="icon"></div>
<div class="name">TRON</div>
<div class="symbol">TRX / USDT</div>
</div>
</div>
<div class="qr-section" id="qr-section">
<div class="qr-wrap">
<img id="qr-img" src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=ethereum:0x1E3AC01d0fdb976179790BDD02823196A92705C9" alt="Payment QR">
</div>
<div class="amount" id="amount-display">Send $49.00 USD in ETH</div>
<div class="addr" onclick="copyAddr()" id="addr-display">
<code id="addr-code">0x1E3A...05C9</code>
<span class="copy-btn">Copy</span>
</div>
</div>
<div class="email-capture">
<p><strong>Step 2:</strong> Enter your email so we can send your download link after confirmation.</p>
<div class="form-row">
<input type="email" id="crypto-email" placeholder="you@example.com">
<button class="btn btn-primary" onclick="submitCrypto()">I've Sent Payment</button>
</div>
</div>
<div class="payment-status" id="crypto-status" style="display:none">
<div class="status-icon"></div>
<div class="status-text">Payment Pending</div>
<div class="status-sub">We'll email your download link once confirmed</div>
</div>
</div>
<!-- Card Payment -->
<div id="card-panel" class="pay-content">
<p style="font-size:13px;color:var(--fg2);margin-bottom:16px">Secure payment via Stripe. Your card details are never stored on our servers.</p>
<form class="card-form" onsubmit="event.preventDefault();submitCard()">
<div class="form-group full">
<label>Email</label>
<input type="email" placeholder="you@example.com" required>
</div>
<div class="form-group full">
<label>Card Number</label>
<input type="text" placeholder="4242 4242 4242 4242" maxlength="19" required>
</div>
<div class="form-row">
<div class="form-group">
<label>Expiry</label>
<input type="text" placeholder="MM/YY" maxlength="5" required>
</div>
<div class="form-group">
<label>CVC</label>
<input type="text" placeholder="123" maxlength="4" required>
</div>
</div>
<div class="form-group full">
<label>Name on Card</label>
<input type="text" placeholder="John Doe" required>
</div>
<button type="submit" class="btn btn-primary btn-lg" style="width:100%">Pay $49.00</button>
</form>
<div class="payment-status" id="card-status" style="display:none;margin-top:16px">
<div class="status-icon"></div>
<div class="status-text">Payment Successful</div>
<div class="status-sub">Check your email for the download link</div>
</div>
</div>
<!-- Gumroad Payment -->
<div id="gumroad-panel" class="pay-content">
<p style="font-size:13px;color:var(--fg2);margin-bottom:16px">Pay via Gumroad with PayPal, Apple Pay, Google Pay, or crypto.</p>
<div style="text-align:center;padding:32px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-lg)">
<div style="font-size:48px;margin-bottom:16px">🛒</div>
<p style="font-size:14px;color:var(--fg2);margin-bottom:20px">You'll be redirected to Gumroad to complete your purchase securely.</p>
<a href="https://cryptorugmuncher.gumroad.com/l/walletpress" class="btn btn-primary btn-lg" target="_blank">Continue to Gumroad</a>
</div>
</div>
</div>
</div>
</section>
<footer>
<div class="footer-grid">
<a href="docs.html">Documentation</a>
<a href="features.html">Features</a>
<a href="buy.html">Pricing</a>
<a href="about.html">About</a>
<a href="contact.html">Contact</a>
</div>
<p class="copy">© 2026 Rug Munch Media LLC. Made in Wyoming.</p>
</footer>
<script>
// Tab switching
function switchTab(tab) {
document.querySelectorAll('.pay-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.pay-content').forEach(c => c.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(tab + '-panel').classList.add('active');
}
// Chain selection
let selectedChain = 'evm';
let selectedAddr = '0x1E3AC01d0fdb976179790BDD02823196A92705C9';
let selectedSymbol = 'ETH';
const chainData = {
evm: { addr: '0x1E3AC01d0fdb976179790BDD02823196A92705C9', symbol: 'ETH', fullAddr: '0x1E3AC01d0fdb976179790BDD02823196A92705C9', qrPrefix: 'ethereum:' },
btc: { addr: 'bc1qzkllckr024wqttm4px6egcwsya9tcrgghm46kq', symbol: 'BTC', fullAddr: 'bc1qzkllckr024wqttm4px6egcwsya9tcrgghm46kq', qrPrefix: 'bitcoin:' },
sol: { addr: 'G1uZFfsdxK2PuH2Em5LhRkEcptxHWe4vy5BZak67QyKv', symbol: 'SOL', fullAddr: 'G1uZFfsdxK2PuH2Em5LhRkEcptxHWe4vy5BZak67QyKv', qrPrefix: 'solana:' },
trx: { addr: 'TYSWXWr24QTe3c99V9dtzhe1aZTneE261p', symbol: 'TRX', fullAddr: 'TYSWXWr24QTe3c99V9dtzhe1aZTneE261p', qrPrefix: 'tron:' }
};
function selectChain(el) {
document.querySelectorAll('.chain-option').forEach(c => c.classList.remove('selected'));
el.classList.add('selected');
selectedChain = el.dataset.chain;
selectedAddr = el.dataset.addr;
selectedSymbol = el.dataset.symbol;
updateQR();
}
function updateQR() {
const data = chainData[selectedChain];
const qrUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' + encodeURIComponent(data.qrPrefix + data.fullAddr);
document.getElementById('qr-img').src = qrUrl;
document.getElementById('amount-display').textContent = 'Send $49.00 USD in ' + data.symbol;
document.getElementById('addr-code').textContent = data.addr.substring(0, 6) + '...' + data.addr.substring(data.addr.length - 4);
document.getElementById('addr-display').dataset.addr = data.fullAddr;
}
function copyAddr() {
const addr = document.getElementById('addr-display').dataset.addr;
navigator.clipboard.writeText(addr).then(() => {
const btn = document.querySelector('#addr-display .copy-btn');
btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = 'Copy', 2000);
}).catch(() => {
const ta = document.createElement('textarea');
ta.value = addr;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
document.querySelector('#addr-display .copy-btn').textContent = 'Copied!';
setTimeout(() => document.querySelector('#addr-display .copy-btn').textContent = 'Copy', 2000);
});
}
function submitCrypto() {
const email = document.getElementById('crypto-email').value;
if (!email || !email.includes('@')) {
alert('Please enter a valid email address');
return;
}
document.getElementById('crypto-status').style.display = 'block';
document.getElementById('crypto-status').querySelector('.status-text').textContent = 'Payment Pending';
document.getElementById('crypto-status').querySelector('.status-sub').textContent = 'We\'ll email ' + email + ' once your ' + selectedSymbol + ' payment is confirmed';
// In production: send to backend webhook
}
function submitCard() {
document.getElementById('card-status').style.display = 'block';
// In production: Stripe integration
}
// Price fetching
let currentPrices = {};
const PRODUCT = 'walletpress'; // or 'pry' for Pry site
async function fetchPrices() {
try {
const resp = await fetch('/api/prices');
if (!resp.ok) throw new Error('Price fetch failed');
currentPrices = await resp.json();
document.getElementById('price-loading').style.display = 'none';
updateAmountDisplay();
} catch (e) {
document.getElementById('price-loading').textContent = 'Using cached prices';
}
}
function updateAmountDisplay() {
const cgId = selectedChain === 'evm' ? 'ethereum' : selectedChain;
const priceData = currentPrices[cgId];
if (!priceData) return;
const usdValue = PRODUCT === 'walletpress' ? 49.0 : 29.0;
const cryptoAmount = (usdValue / priceData.usd).toFixed(6);
const symbol = priceData.symbol;
document.getElementById('amount-display').textContent = `Send ${cryptoAmount} ${symbol} (~$${usdValue} USD)`;
document.getElementById('crypto-amount-display').textContent = `${cryptoAmount} ${symbol}`;
}
// Modified selectChain to update amounts
function selectChain(el) {
document.querySelectorAll('.chain-option').forEach(c => c.classList.remove('selected'));
el.classList.add('selected');
selectedChain = el.dataset.chain;
selectedAddr = el.dataset.addr;
selectedSymbol = el.dataset.symbol;
updateQR();
updateAmountDisplay();
}
// Fetch prices on load
fetchPrices();
// Reveal animation
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => { if(e.isIntersecting) e.target.classList.add('visible'); });
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
</script>
</body>
</html>

620
cli/walletpress Executable file
View file

@ -0,0 +1,620 @@
#!/usr/bin/env bash
# WalletPress CLI — Self-Hosted Wallet Management Platform
# Usage: walletpress <command> [options]
set -euo pipefail
VERSION="1.0.0-beta"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/walletpress"
CONFIG_FILE="$CONFIG_DIR/config.json"
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/walletpress"
GATES_FILE="$DATA_DIR/gates.json"
LOG_FILE="$DATA_DIR/walletpress.log"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
mkdir -p "$CONFIG_DIR" "$DATA_DIR"
touch "$GATES_FILE" "$LOG_FILE"
log() { echo "[$(date +%H:%M:%S)] $*" >> "$LOG_FILE"; }
info() { echo -e "${BLUE}::${NC} $*"; }
ok() { echo -e "${GREEN}✓${NC} $*"; }
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
err() { echo -e "${RED}✗${NC} $*" >&2; }
hr() { printf '%*s\n' "${COLUMNS:-80}" '' | tr ' ' '─'; }
die() { err "$1"; exit 1; }
# ─── Config ──────────────────────────────────────────────────────────
load_config() {
if [[ -f "$CONFIG_FILE" ]]; then
API_URL=$(jq -r '.api_url // ""' "$CONFIG_FILE")
API_KEY=$(jq -r '.api_key // ""' "$CONFIG_FILE")
MERCHANT_WALLET=$(jq -r '.merchant_wallet // ""' "$CONFIG_FILE")
MERCHANT_CHAIN=$(jq -r '.merchant_chain // "solana"' "$CONFIG_FILE")
DEFAULT_CHAIN=$(jq -r '.default_chain // "solana"' "$CONFIG_FILE")
else API_URL=""; API_KEY=""; MERCHANT_WALLET=""; MERCHANT_CHAIN="solana"; DEFAULT_CHAIN="solana"
fi
}
save_config() {
jq -n --arg api_url "$API_URL" --arg api_key "$API_KEY" --arg merchant_wallet "$MERCHANT_WALLET" --arg merchant_chain "$MERCHANT_CHAIN" --arg default_chain "$DEFAULT_CHAIN" \
'{api_url: $api_url, api_key: $api_key, merchant_wallet: $merchant_wallet, merchant_chain: $merchant_chain, default_chain: $default_chain}' > "$CONFIG_FILE"
ok "Configuration saved"
}
# ─── API ─────────────────────────────────────────────────────────────
CV="/api/v1/chain-vault"
WA="/api/v1/wallet"
api_call() {
local method="$1" path="$2" body="${3:-}"
if [[ -z "$API_URL" ]]; then die "API not configured. Run 'walletpress setup'"; fi
local url="${API_URL}${path}" headers=()
headers+=(-H "Content-Type: application/json")
[[ -n "$API_KEY" ]] && headers+=(-H "Authorization: Bearer $API_KEY" -H "X-API-Key: $API_KEY")
local cmd=("curl" "-sf")
[[ -n "$body" ]] && cmd+=(-X "$method" -d "$body") || cmd+=(-X "$method")
cmd+=( "${headers[@]}" "$url" )
"${cmd[@]}" 2>/dev/null || return 1
}
api_get() { api_call GET "$1" ""; }
api_post() { api_call POST "$1" "$2"; }
api_del() { api_call DELETE "$1" ""; }
# ─── Gates ───────────────────────────────────────────────────────────
load_gates() {
if [[ -f "$GATES_FILE" ]] && [[ -s "$GATES_FILE" ]]; then GATES=$(cat "$GATES_FILE"); else GATES='[]'; fi
}
save_gates() { echo "$GATES" > "$GATES_FILE"; }
# ══════════════════════════════════════════════════════════════════════
# COMMANDS
# ══════════════════════════════════════════════════════════════════════
cmd_setup() {
hr; echo -e " ${BOLD}WalletPress Setup Wizard${NC}"; hr
echo -e "\n${CYAN}[1/5] API Connection${NC}"
read -rp " Backend API URL [${API_URL:-}]: " input; API_URL="${input:-$API_URL}"
read -rsp " API Key: " input; echo; API_KEY="${input:-$API_KEY}"
if [[ -n "$API_URL" && -n "$API_KEY" ]]; then
info "Testing connection..."
if health=$(api_get "/health" 2>/dev/null); then ok "Connected: $(echo "$health" | jq -r '.status // "ok"')"; else warn "API unreachable"; fi
fi
echo -e "\n${CYAN}[2/5] Merchant Wallet${NC}"
read -rp " Wallet address [${MERCHANT_WALLET:-}]: " input; MERCHANT_WALLET="${input:-$MERCHANT_WALLET}"
read -rp " Chain [${MERCHANT_CHAIN}]: " input; MERCHANT_CHAIN="${input:-$MERCHANT_CHAIN}"
echo -e "\n${CYAN}[3/5] Default Chain${NC}"
read -rp " Default chain [${DEFAULT_CHAIN}]: " input; DEFAULT_CHAIN="${input:-$DEFAULT_CHAIN}"
save_config
echo -e "\n${CYAN}[4/5] Generate First Wallet${NC}"
read -rp " Generate a wallet now? (y/N): " gen
if [[ "$gen" =~ ^[yY] ]]; then cmd_vault generate; fi
echo -e "\n${CYAN}[5/5] Create Token Gate${NC}"
read -rp " Create a token gate? (y/N): " gate
if [[ "$gate" =~ ^[yY] ]]; then
read -rp " Gate name: " title; read -rp " Token address: " token; read -rp " Chain [$DEFAULT_CHAIN]: " chain
chain="${chain:-$DEFAULT_CHAIN}"; read -rp " Min amount [1]: " min; min="${min:-1}"
load_gates; GATES=$(echo "$GATES" | jq --arg t "$title" --arg k "$token" --arg c "$chain" --argjson m "$min" '. += [{title: $t, token: $k, chain: $c, min_amount: $m, created_at: now|strflocaltime("%Y-%m-%dT%H:%M:%S")}]'); save_gates
ok "Gate '$title' created"; fi
hr; ok "Setup complete!"
info "Run 'walletpress --help' to see all commands"
}
cmd_dashboard() {
load_config; hr
echo -e " ${BOLD}WalletPress${NC} v${VERSION}"; hr
echo -e " URL: ${API_URL:-(not set)}"
echo -e " Merchant: ${MERCHANT_WALLET:0:16}... [${MERCHANT_CHAIN}]"
echo -e " Default: ${DEFAULT_CHAIN}"
if [[ -n "$API_URL" ]]; then
health=$(api_get "/health" 2>/dev/null || echo '{"status":"unreachable"}')
echo -e " API: $(echo "$health" | jq -r '.status')"
stats=$(api_get "${CV}/stats" 2>/dev/null || echo '{}')
echo -e " Vault: $(echo "$stats" | jq -r '.total_wallets // "?"') wallets"
chains=$(api_get "${CV}/chains" 2>/dev/null || echo '[]')
echo -e " Chains: $(echo "$chains" | jq 'length') supported"
fi
load_gates; local gc=$(echo "$GATES" | jq 'length')
echo -e " Gates: $gc"
hr
}
# ─── VAULT ───────────────────────────────────────────────────────────
cmd_vault() {
load_config
case "${1:-list}" in
list|ls)
local vault
vault=$(api_get "${CV}/vault" 2>/dev/null || die "Failed to fetch vault")
local count=$(echo "$vault" | jq 'length - 1')
echo -e "${BOLD}Vault (${count} wallets)${NC}"
echo "$vault" | jq -r 'to_entries | .[] | select(.key != "_meta") | " \(.key[0:8]).. \(.value.chain // "?") \(.value.address // .value.public_key // "")[0:24]... \(.value.label // "")"'
;;
get|view)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
api_get "${CV}/vault/${id}" | jq '.' || warn "Wallet not found"
;;
generate|gen)
local chain="${2:-$DEFAULT_CHAIN}" label="${3:-}"
[[ -z "$chain" ]] && read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
[[ -z "$label" ]] && read -rp "Label: " label
result=$(api_post "${CV}/generate" "{\"chain\":\"$chain\",\"label\":\"$label\"}" || true)
if [[ -n "$result" ]]; then ok "Wallet generated"; echo "$result" | jq '{chain, address, label}'; else die "Generation failed"; fi
;;
batch)
local chains="${2:-solana,ethereum,base,polygon,bsc}" prefix="${3:-batch}"
info "Generating wallets for: $chains"
result=$(api_post "${CV}/generate/batch" "{\"chains\":\"$chains\",\"label_prefix\":\"$prefix\"}" || true)
if [[ -n "$result" ]]; then ok "Batch complete"; echo "$result" | jq '.'; else die "Batch failed"; fi
;;
all)
local prefix="${2:-full}"
info "Generating wallet for ALL supported chains..."
result=$(api_post "${CV}/generate/batch" "{\"label_prefix\":\"$prefix\"}" || true)
if [[ -n "$result" ]]; then ok "All chains generated"; echo "$result" | jq 'keys | .[]'; else die "Failed"; fi
;;
import)
local chain="${2:-}" key="${3:-}" label="${4:-}"
[[ -z "$chain" ]] && read -rp "Chain: " chain
[[ -z "$key" ]] && read -rsp "Private key: " key; echo
[[ -z "$label" ]] && read -rp "Label: " label
result=$(api_post "${CV}/import" "{\"chain\":\"$chain\",\"private_key\":\"$key\",\"label\":\"$label\"}" || true)
if [[ -n "$result" ]]; then ok "Wallet imported"; echo "$result" | jq '.'; else die "Import failed"; fi
;;
stats)
api_get "${CV}/stats" | jq '.' || warn "Failed to fetch stats"
;;
tree)
api_get "${CV}/tree" | jq '.' || warn "Failed"
;;
health-score)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
api_get "${CV}/health-score/${id}" | jq '.' || warn "Failed"
;;
rotate)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
result=$(api_post "${CV}/rotate" "{\"wallet_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Wallet rotated"; echo "$result" | jq '{chain, new_address}'; else die "Rotation failed"; fi
;;
export)
local format="${2:-json}"
result=$(api_post "${CV}/export" "{\"format\":\"$format\"}" || true)
if [[ -n "$result" ]]; then echo "$result" | jq '.'; else die "Export failed"; fi
;;
filter)
local field="${2:-chain}" value="${3:-solana}"
api_post "${CV}/bulk/filter" "{\"filters\":{\"$field\":\"$value\"}}" | jq '.' || warn "Filter failed"
;;
delete)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
result=$(api_post "${CV}/bulk/delete" "{\"ids\":[\"$id\"]}" || true)
if [[ -n "$result" ]]; then ok "Wallet deleted"; else die "Delete failed"; fi
;;
paper)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
api_get "${CV}/paper-wallet/${id}" | jq '.' || warn "Failed"
;;
*)
echo "Usage: walletpress vault <list|get|generate|batch|all|import|stats|tree|health-score|rotate|export|filter|delete|paper>"
esac
}
# ─── MNEMONIC ────────────────────────────────────────────────────────
cmd_mnemonic() {
load_config
local phrase="${1:-}" passphrase="${2:-}"
if [[ -z "$phrase" ]]; then
read -rp "Mnemonic phrase (12/24 words): " phrase
read -rp "Passphrase (optional): " passphrase
fi
info "Deriving addresses for all chains..."
result=$(api_post "${CV}/from-mnemonic" "{\"mnemonic\":\"$phrase\",\"passphrase\":\"$passphrase\"}" || true)
if [[ -z "$result" ]]; then die "Conversion failed"; fi
echo "$result" | jq -r '.wallets // .result // . | to_entries | .[] | " \(.key): \(.value.address // .value.public_key // "?")"'
ok "Done — $(echo "$result" | jq '.wallets // .result // . | length') addresses derived"
}
# ─── RECOVERY ────────────────────────────────────────────────────────
cmd_recover() {
load_config
local partial="${1:-}" chain="${2:-$DEFAULT_CHAIN}"
if [[ -z "$partial" ]]; then
read -rp "Partial mnemonic (use ? for unknown words): " partial
read -rp "Chain [$chain]: " input; chain="${input:-$chain}"
fi
info "Attempting recovery on $chain..."
info "This may take time for large partial phrases."
# wallet_killer.py has the recovery engine — invoke via backend
warn "Recovery endpoint not yet exposed in REST API. Use backend directly: python3 -c 'from wallet_killer import recover; print(recover(...))'"
}
# ─── SWEEP ───────────────────────────────────────────────────────────
cmd_sweep() {
load_config
case "${1:-}" in
rotate)
local id="${2:-}" to="${3:-}"
[[ -z "$id" ]] && read -rp "Wallet ID to rotate: " id
[[ -z "$to" ]] && read -rp "Destination address: " to
result=$(api_post "${CV}/rotate-sweep" "{\"wallet_id\":\"$id\",\"to_address\":\"$to\"}" || true)
if [[ -n "$result" ]]; then ok "Rotated and swept"; echo "$result" | jq '.'; else die "Failed"; fi
;;
dust|sweep)
local id="${2:-}" to="${3:-}"
[[ -z "$id" ]] && read -rp "Wallet ID to sweep: " id
[[ -z "$to" ]] && read -rp "Destination address: " to
result=$(api_post "${CV}/sweep" "{\"from_wallet_id\":\"$id\",\"to_address\":\"$to\"}" || true)
if [[ -n "$result" ]]; then ok "Swept"; echo "$result" | jq '.'; else die "Sweep failed"; fi
;;
temporal)
local chain="${2:-$DEFAULT_CHAIN}" ttl="${3:-3600}" label="${4:-temporal}"
result=$(api_post "${CV}/temporal/create" "{\"chain\":\"$chain\",\"ttl_seconds\":$ttl,\"label\":\"$label\"}" || true)
if [[ -n "$result" ]]; then ok "Temporal wallet created (TTL: ${ttl}s)"; echo "$result" | jq '{address, expires_at}'; else die "Failed"; fi
;;
temporal-list)
api_get "${CV}/temporal/list" | jq '.' || warn "None"
;;
temporal-release)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Temporal ID: " id
result=$(api_post "${CV}/temporal/release" "{\"temporal_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Released"; else die "Failed"; fi
;;
distribute)
echo "Distribute funds across wallets (advanced). Use backend API directly for now."
;;
funding)
local chain="${2:-$DEFAULT_CHAIN}" id="${3:-}" amount="${4:-}"
[[ -z "$id" ]] && read -rp "Wallet ID: " id
[[ -z "$amount" ]] && read -rp "Amount: " amount
result=$(api_post "${CV}/funding-bundle" "{\"chain\":\"$chain\",\"wallet_id\":\"$id\",\"amount\":$amount}" || true)
if [[ -n "$result" ]]; then ok "Funding bundle created"; echo "$result" | jq '.'; else die "Failed"; fi
;;
deployer)
local chain="${2:-$DEFAULT_CHAIN}" id="${3:-}"
[[ -z "$id" ]] && read -rp "Wallet ID: " id
result=$(api_post "${CV}/deployer-setup" "{\"chain\":\"$chain\",\"wallet_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Deployer setup done"; echo "$result" | jq '.'; else die "Failed"; fi
;;
*)
echo "Usage: walletpress sweep <rotate|dust|temporal|temporal-list|temporal-release|funding|deployer>"
esac
}
# ─── ESCROW ──────────────────────────────────────────────────────────
cmd_escrow() {
load_config
case "${1:-create}" in
create)
local addr amount platform conditions
read -rp "Deposit address: " addr
read -rp "Amount (USD): " amount
read -rp "Platform [solana]: " platform; platform="${platform:-solana}"
read -rp "Conditions (JSON): " conditions
result=$(api_post "${CV}/escrow" "{\"deposit_address\":\"$addr\",\"amount\":$amount,\"platform\":\"$platform\",\"conditions\":$conditions}" || true)
if [[ -n "$result" ]]; then ok "Escrow created"; echo "$result" | jq '.'; else die "Failed"; fi
;;
release)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Escrow ID: " id
result=$(api_post "${CV}/escrow/release" "{\"escrow_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Released"; echo "$result" | jq '.'; else die "Failed"; fi
;;
*) echo "Usage: walletpress escrow <create|release>"
esac
}
# ─── WALLET ANALYSIS ────────────────────────────────────────────────
cmd_wallet() {
load_config
local sub="${1:-balance}" addr="${2:-}" chain="${3:-$DEFAULT_CHAIN}"
[[ -z "$addr" ]] && read -rp "Address: " addr
[[ "$sub" != "verify" ]] && [[ -z "$chain" || "$chain" == "$DEFAULT_CHAIN" ]] && read -rp "Chain [$DEFAULT_CHAIN]: " input && chain="${input:-$chain}"
case "$sub" in
balance)
api_get "${WA}/${addr}/balance?chain=${chain}" | jq '.' || warn "Failed"
;;
analyze)
api_get "${WA}/${addr}/analyze?chain=${chain}" | jq '.' || warn "Failed"
;;
scan)
local tier="${3:-free}"
api_post "${WA}/scan" "{\"address\":\"$addr\",\"chain\":\"$chain\",\"tier\":\"$tier\"}" | jq '.' || warn "Failed"
;;
txs|transactions)
local limit="${4:-20}"
api_get "${WA}/${addr}/transactions?chain=${chain}&limit=${limit}" | jq '.' || warn "Failed"
;;
verify)
local msg sig
read -rp "Message: " msg; read -rp "Signature: " sig
api_post "${WA}/verify" "{\"address\":\"$addr\",\"message\":\"$msg\",\"signature\":\"$sig\",\"chain\":\"$chain\"}" | jq '.' || warn "Failed"
;;
*) echo "Usage: walletpress wallet <balance|analyze|scan|txs|verify> [address] [chain]"
esac
}
# ─── GATES ───────────────────────────────────────────────────────────
cmd_gate() {
load_config; load_gates
case "${1:-list}" in
add)
local title token chain min
read -rp "Gate name: " title; read -rp "Token address: " token
read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
read -rp "Min amount [1]: " min; min="${min:-1}"
GATES=$(echo "$GATES" | jq --arg t "$title" --arg k "$token" --arg c "$chain" --argjson m "$min" '. += [{title: $t, token: $k, chain: $c, min_amount: $m, created_at: now|strflocaltime("%Y-%m-%dT%H:%M:%S")}]')
save_gates; local id=$(echo "$GATES" | jq 'length - 1')
ok "Gate '$title' created — shortcode: [wallet_gate id=\"$id\"]"
;;
list)
local c=$(echo "$GATES" | jq 'length'); [[ "$c" -eq 0 ]] && { info "No gates"; return; }
hr; echo -e "${BOLD}Token Gates (${c})${NC}"; hr
echo "$GATES" | jq -r 'to_entries | .[] | " \(.key). \(.value.title)\n token: \(.value.token[0:32])... chain: \(.value.chain) min: \(.value.min_amount)\n code: [wallet_gate id=\"\(.key)\"]\n"'
hr
;;
delete)
local id="${2:-}"; [[ -z "$id" ]] && cmd_gate list >&2 && read -rp "Gate ID to delete: " id
GATES=$(echo "$GATES" | jq "del(.[$id])"); save_gates; ok "Deleted"
;;
check)
local addr="${2:-}" contract="${3:-}" chain="${4:-$DEFAULT_CHAIN}"
[[ -z "$addr" ]] && read -rp "Wallet address: " addr
[[ -z "$contract" ]] && read -rp "Contract address: " contract
result=$(api_get "${WA}/${addr}/balance?chain=${chain}" 2>/dev/null || echo '{}')
local held=$(echo "$result" | jq -r --arg c "$contract" '[.tokens // []] | map(select(.address == $c or .mint == $c)) | .[0].amount // 0')
if [[ "$(echo "$held > 0" | bc -l 2>/dev/null)" == "1" ]]; then ok "Wallet holds $held of $contract"; else warn "No holdings found"; fi
;;
*) echo "Usage: walletpress gate <add|list|delete|check>"
esac
}
# ─── PAYMENTS ─────────────────────────────────────────────────────────
cmd_payment() {
load_config
case "${1:-list}" in
create)
local from to amount token chain
read -rp "From address: " from; read -rp "To [${MERCHANT_WALLET:-}]: " to; to="${to:-$MERCHANT_WALLET}"
read -rp "Amount: " amount; read -rp "Token [SOL]: " token; token="${token:-SOL}"
read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
result=$(api_post "${CV}/tx/build" "{\"chain\":\"$chain\",\"from\":\"$from\",\"to\":\"$to\",\"amount\":$amount,\"token\":\"$token\"}" || true)
if [[ -n "$result" ]]; then ok "Payment built"; echo "$result" | jq '.'; else die "Build failed"; fi
;;
broadcast)
local tx="${2:-}" chain="${3:-$DEFAULT_CHAIN}"
[[ -z "$tx" ]] && read -rp "Signed TX (hex): " tx
result=$(api_post "${CV}/tx/broadcast" "{\"chain\":\"$chain\",\"signed_tx\":\"$tx\"}" || true)
if [[ -n "$result" ]]; then ok "Broadcasted"; echo "$result" | jq '{tx_hash, status}'; else die "Failed"; fi
;;
list)
echo "On-chain payments: use 'walletpress wallet txs <address>' to view"
;;
*) echo "Usage: walletpress payment <create|broadcast|list>"
esac
}
# ─── API KEYS ────────────────────────────────────────────────────────
cmd_key() {
load_config
case "${1:-generate}" in
generate|create)
local label="${2:-}" scopes="${3:-vault.read,generate}"
[[ -z "$label" ]] && read -rp "Key label: " label
result=$(api_post "${CV}/api-keys" "{\"label\":\"$label\",\"scopes\":[\"${scopes//,/\"\,\"}\"]}" || true)
if [[ -n "$result" ]]; then
hr; echo -e "${BOLD}New API Key${NC}"; hr
echo -e " ${CYAN}$(echo "$result" | jq -r '.api_key // "generated"')${NC}"; hr
echo " Save this now — it won't be shown again."
else die "Key generation failed"; fi
;;
list)
api_get "${CV}/api-keys" | jq '.' || info "No keys"
;;
revoke)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Key ID: " id
result=$(api_post "${CV}/api-keys/revoke" "{\"key_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Revoked"; else die "Failed"; fi
;;
local)
local key="wp_$(openssl rand -hex 24)"
hr; echo -e "${BOLD}Local API Key${NC}"; hr
echo -e " ${CYAN}$key${NC}"; hr
echo " Add to backend .env: WALLETPRESS_API_KEY=$key"
echo " Add to WordPress settings as your API key"
;;
*) echo "Usage: walletpress key <generate|list|revoke|local>"
esac
}
# ─── WEBHOOKS ────────────────────────────────────────────────────────
cmd_webhook() {
load_config
case "${1:-list}" in
create)
local url events secret
read -rp "Webhook URL: " url
read -rp "Events (comma-sep) [wallet.generated]: " events; events="${events:-wallet.generated}"
read -rp "Secret: " secret
result=$(api_post "${CV}/webhooks" "{\"url\":\"$url\",\"events\":[\"${events//,/\"\,\"}\"],\"secret\":\"$secret\"}" || true)
if [[ -n "$result" ]]; then ok "Webhook created"; else die "Failed"; fi
;;
list)
api_get "${CV}/webhooks" | jq '.' || info "No webhooks"
;;
delete)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Webhook ID: " id
api_del "${CV}/webhooks/${id}" && ok "Deleted" || die "Failed"
;;
*) echo "Usage: walletpress webhook <create|list|delete>"
esac
}
# ─── BALANCES ────────────────────────────────────────────────────────
cmd_balances() {
load_config
case "${1:-list}" in
list)
api_get "${CV}/balances" | jq '.' || warn "Failed"
;;
snapshot)
api_post "${CV}/balances/snapshot" "{}" | jq '.' || warn "Failed"
;;
history)
local days="${2:-30}"
api_get "${CV}/balances/history?days=${days}" | jq '.' || warn "Failed"
;;
*) echo "Usage: walletpress balances <list|snapshot|history>"
esac
}
# ─── CHAINS ──────────────────────────────────────────────────────────
cmd_chain() {
load_config
case "${1:-list}" in
list)
api_get "${CV}/chains" | jq -r 'to_entries | .[] | " \(.key): \(.value.name // .value)"' || warn "Failed"
;;
rpc)
api_get "${CV}/rpc-chains" | jq '.' || warn "Failed"
;;
validate)
local chain="${2:-}" addr="${3:-}"
[[ -z "$chain" ]] && read -rp "Chain: " chain
[[ -z "$addr" ]] && read -rp "Address: " addr
api_get "${CV}/validate/${chain}/${addr}" | jq '.' || warn "Failed"
;;
link-proof)
local src="${2:-}" sc="${3:-}" tc="${4:-}"
[[ -z "$src" ]] && read -rp "Source address: " src
[[ -z "$sc" ]] && read -rp "Source chain: " sc
[[ -z "$tc" ]] && read -rp "Target chain: " tc
api_post "${CV}/link/proof" "{\"source_address\":\"$src\",\"source_chain\":\"$sc\",\"target_chain\":\"$tc\"}" | jq '.' || warn "Failed"
;;
link-verify)
local addr="${2:-}" chain="${3:-}" proof="${4:-}"
[[ -z "$addr" ]] && read -rp "Address: " addr
[[ -z "$chain" ]] && read -rp "Chain: " chain
[[ -z "$proof" ]] && read -rp "Proof: " proof
api_post "${CV}/link/verify" "{\"address\":\"$addr\",\"chain\":\"$chain\",\"proof\":\"$proof\"}" | jq '.' || warn "Failed"
;;
*) echo "Usage: walletpress chain <list|rpc|validate|link-proof|link-verify>"
esac
}
# ─── AUDIT ───────────────────────────────────────────────────────────
cmd_audit() {
load_config
local limit="${1:-50}"
api_get "${CV}/audit-trail?limit=${limit}" | jq '.' || warn "No audit entries"
}
# ─── CLUSTER ─────────────────────────────────────────────────────────
cmd_cluster() {
load_config
local chain="${1:-$DEFAULT_CHAIN}" count="${2:-10}"
info "Generating cluster-aware wallets on $chain..."
result=$(api_post "${CV}/cluster" "{\"chain\":\"$chain\",\"count\":$count}" || true)
if [[ -n "$result" ]]; then ok "Cluster generated"; echo "$result" | jq '.'; else die "Failed"; fi
}
# ─── HEALTH ──────────────────────────────────────────────────────────
cmd_health() {
load_config; hr
echo -e "${BOLD}WalletPress Health Check${NC}"; hr
echo -n " Config: "; [[ -f "$CONFIG_FILE" ]] && ok "found" || warn "not configured"
echo -n " API: "
if [[ -n "$API_URL" ]]; then
local h; h=$(api_get "/health" 2>/dev/null || true)
[[ -n "$h" ]] && ok "$(echo "$h" | jq -r '.status')" || warn "unreachable"
else warn "not configured"; fi
echo -n " Vault: "
local v; v=$(api_get "${CV}/stats" 2>/dev/null || true)
[[ -n "$v" ]] && ok "$(echo "$v" | jq -r '.total_wallets // 0') wallets" || warn "unknown"
echo -n " Chains: "
local c; c=$(api_get "${CV}/chains" 2>/dev/null || true)
[[ -n "$c" ]] && ok "$(echo "$c" | jq 'length') supported" || warn "unknown"
echo -n " Merchant: "
[[ -n "$MERCHANT_WALLET" ]] && ok "${MERCHANT_WALLET:0:8}... [$MERCHANT_CHAIN]" || warn "not set"
load_gates; echo -n " Gates: "; ok "$(echo "$GATES" | jq 'length') gate(s)"
hr
}
# ─── SERVE ───────────────────────────────────────────────────────────
cmd_serve() {
load_config; local port="${1:-8580}" host="${2:-127.0.0.1}"
command -v python3 &>/dev/null || die "python3 required"
info "Admin UI at http://$host:$port — Ctrl+C to stop"; hr
cd "$DATA_DIR" && python3 -m http.server "$port" --bind "$host"
}
# ══════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════
load_config
case "${1:-help}" in
setup|wizard) shift; cmd_setup "$@" ;;
dash|dashboard|st) cmd_dashboard ;;
vault|wallet-vault) shift; cmd_vault "$@" ;;
mnemonic|convert) shift; cmd_mnemonic "$@" ;;
recover|recovery) shift; cmd_recover "$@" ;;
sweep) shift; cmd_sweep "$@" ;;
escrow) shift; cmd_escrow "$@" ;;
wallet|address) shift; cmd_wallet "$@" ;;
gate|gates) shift; cmd_gate "$@" ;;
pay|payment) shift; cmd_payment "$@" ;;
key|keys|apikey) shift; cmd_key "$@" ;;
webhook|hooks) shift; cmd_webhook "$@" ;;
balance|balances) shift; cmd_balances "$@" ;;
chain|chains) shift; cmd_chain "$@" ;;
audit|logs) shift; cmd_audit "$@" ;;
cluster) shift; cmd_cluster "$@" ;;
health|status) cmd_health ;;
serve|ui) shift; cmd_serve "$@" ;;
version|-v|--version) echo "WalletPress CLI v${VERSION}" ;;
help|-h|--help|"")
hr
echo -e "${BOLD}WalletPress CLI v${VERSION}${NC}"; hr
echo -e " ${CYAN}setup${NC} Interactive setup wizard (5 steps)"
echo -e " ${CYAN}dashboard${NC} System overview"
echo -e " ${CYAN}vault${NC} Wallet vault: list, get, generate, batch, all, import,"; echo " stats, tree, health-score, rotate, export, filter, delete, paper"
echo -e " ${CYAN}mnemonic${NC} BIP39 mnemonic → 29+ chain addresses"
echo -e " ${CYAN}recover${NC} Wallet recovery from partial mnemonics"
echo -e " ${CYAN}sweep${NC} Sweep: rotate, dust, temporal, funding, deployer"
echo -e " ${CYAN}escrow${NC} Pay-to-release escrow: create, release"
echo -e " ${CYAN}wallet${NC} Wallet analysis: balance, analyze, scan, txs, verify"
echo -e " ${CYAN}gate${NC} Token gates: add, list, delete, check"
echo -e " ${CYAN}payment${NC} Crypto payments: create, broadcast"
echo -e " ${CYAN}key${NC} API keys: generate, list, revoke, local"
echo -e " ${CYAN}webhook${NC} Webhooks: create, list, delete"
echo -e " ${CYAN}balances${NC} Balance tracking: list, snapshot, history"
echo -e " ${CYAN}chain${NC} Chain info: list, rpc, validate, link-proof, link-verify"
echo -e " ${CYAN}audit${NC} Audit trail viewer"
echo -e " ${CYAN}cluster${NC} Cluster-aware wallet generation"
echo -e " ${CYAN}health${NC} System health check"
echo -e " ${CYAN}serve${NC} Start admin HTTP UI [port] [host]"
hr
echo " Config: $CONFIG_FILE | Data: $DATA_DIR | Log: $LOG_FILE"
hr
;;
*) die "Unknown: $1. Run 'walletpress help'"
esac

204
contact.html Normal file
View file

@ -0,0 +1,204 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact — WalletPress · Get in Touch</title>
<meta name="description" content="Contact the WalletPress team. Support, sales, and general inquiries. We respond within hours.">
<meta name="robots" content="noindex">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔐</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,510;14..32,590;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg:#08090a;--bg2:#0c0d0f;--bg3:#121317;--bg4:#181a1f;
--border:rgba(255,255,255,0.06);--border2:rgba(255,255,255,0.09);
--fg:#f7f8f8;--fg2:#9ca0a8;--fg3:#5f636b;
--accent:#f0a030;--accent2:#d48c1e;--accent-glow:rgba(240,160,48,0.12);--accent-glow2:rgba(240,160,48,0.2);
--cyan:#22d3ee;--green:#22c55e;--purple:#8b5cf6;
--radius:6px;--radius-lg:12px;--radius-xl:16px;--radius-pill:9999px;--nav-h:52px;
}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;font-feature-settings:'cv01','ss03';overflow-x:hidden}
::selection{background:rgba(240,160,48,0.3);color:var(--fg)}
img{display:block;max-width:100%}
.reveal{opacity:0;transform:translateY(24px);transition:opacity .6s ease-out,transform .6s ease-out}
.reveal.visible{opacity:1;transform:translateY(0)}
/* NAV */
nav{position:fixed;top:0;left:0;right:0;z-index:100;height:var(--nav-h);background:rgba(8,9,10,0.82);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-bottom:1px solid var(--border)}
.nav-inner{max-width:1200px;margin:0 auto;padding:0 20px;height:100%;display:flex;align-items:center;justify-content:space-between;gap:16px}
.logo{display:flex;align-items:center;gap:8px;font-weight:700;font-size:18px;color:var(--fg);text-decoration:none;letter-spacing:-0.02em}
.logo-dot{width:8px;height:8px;border-radius:50%;background:var(--accent)}
.nav-links{display:flex;align-items:center;gap:24px}
.nav-links a{color:var(--fg2);text-decoration:none;font-size:13px;font-weight:510;transition:color .15s}
.nav-links a:hover{color:var(--fg)}
.nav-cta{padding:6px 16px!important;border-radius:var(--radius)!important;background:var(--accent)!important;color:#08090a!important;font-weight:590!important;font-size:13px!important}
.nav-cta:hover{background:var(--accent2)!important;color:#08090a!important}
.hamburger{display:none;flex-direction:column;gap:4px;cursor:pointer;padding:8px;background:none;border:none;z-index:101}
.hamburger span{display:block;width:20px;height:1.5px;background:var(--fg2);transition:.2s}
.hamburger.open span:nth-child(1){transform:rotate(45deg) translate(4px,4px)}
.hamburger.open span:nth-child(2){opacity:0}
.hamburger.open span:nth-child(3){transform:rotate(-45deg) translate(4px,-4px)}
.mobile-menu{display:none;position:fixed;top:var(--nav-h);left:0;right:0;bottom:0;background:var(--bg);z-index:99;padding:32px 20px;flex-direction:column;gap:20px}
.mobile-menu.open{display:flex}
.mobile-menu a{color:var(--fg2);text-decoration:none;font-size:18px;font-weight:510}
.mobile-menu .nav-cta{display:inline-block;text-align:center;margin-top:8px}
/* BUTTONS */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:12px 24px;border-radius:var(--radius);font-size:14px;font-weight:590;text-decoration:none;transition:all .2s;cursor:pointer;border:none;font-family:inherit;letter-spacing:-0.01em}
.btn-primary{background:var(--accent);color:#08090a}
.btn-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 8px 24px var(--accent-glow)}
.btn-secondary{background:rgba(255,255,255,0.04);color:var(--fg);border:1px solid var(--border2)}
.btn-secondary:hover{background:rgba(255,255,255,0.07)}
.btn-lg{padding:14px 28px;font-size:15px}
section{padding:80px 20px}
@media(min-width:768px){section{padding:100px 32px}}
.container{max-width:800px;margin:0 auto}
.hero{min-height:30vh;display:flex;align-items:center;justify-content:center;text-align:center;padding:120px 20px 40px;position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;top:-30%;left:-50%;width:200%;height:160%;background:radial-gradient(ellipse at 50% 0%,var(--accent-glow),transparent 50%);opacity:0.15;pointer-events:none}
.hero-content{position:relative;z-index:1;max-width:550px}
.hero-breadcrumb{font-size:12px;color:var(--fg3);margin-bottom:16px}
.hero-breadcrumb a{color:var(--fg3);text-decoration:none;transition:color .15s}
.hero-breadcrumb a:hover{color:var(--fg2)}
.hero h1{font-size:clamp(26px,5vw,40px);font-weight:700;line-height:1.1;letter-spacing:-0.03em;margin-bottom:12px}
.hero p{color:var(--fg2);font-size:15px;margin:0 auto;line-height:1.6}
/* CONTACT CARDS */
.contact-grid{display:grid;grid-template-columns:1fr;gap:16px}
@media(min-width:640px){.contact-grid{grid-template-columns:repeat(3,1fr)}}
.contact-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:32px 28px;text-align:center;transition:all .25s}
.contact-card:hover{border-color:var(--border2);transform:translateY(-2px)}
.contact-icon{font-size:36px;margin-bottom:16px}
.contact-card h3{font-size:15px;font-weight:590;margin-bottom:8px}
.contact-card p{font-size:13px;color:var(--fg2);line-height:1.6;margin-bottom:12px}
.contact-card a{color:var(--accent);text-decoration:none;font-family:'JetBrains Mono',monospace;font-size:13px;transition:color .15s}
.contact-card a:hover{text-decoration:underline}
.contact-card .response{font-size:11px;color:var(--fg3);margin-top:8px}
/* RESPONSE TIMES */
.info-box{max-width:600px;margin:40px auto 0;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:28px 32px}
.info-box h3{font-size:14px;font-weight:590;margin-bottom:12px;text-align:center}
.time-row{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid var(--border);font-size:13px}
.time-row:last-child{border-bottom:none}
.time-row .label{color:var(--fg2)}
.time-row .val{color:var(--fg);font-weight:510}
/* FOOTER */
footer{border-top:1px solid var(--border);padding:40px 20px;text-align:center}
.footer-grid{display:flex;justify-content:center;flex-wrap:wrap;gap:24px;margin-bottom:20px}
.footer-grid a{color:var(--fg3);text-decoration:none;font-size:12px;font-weight:510;transition:color .15s}
.footer-grid a:hover{color:var(--fg2)}
footer .copy{color:var(--fg3);font-size:11px}
@media(max-width:768px){
.nav-links{display:none}
.hamburger{display:flex}
.hero h1{font-size:26px}
section{padding:60px 16px}
}
</style>
</head>
<body>
<nav>
<div class="nav-inner">
<a href="/index.html" class="logo"><span class="logo-dot"></span>WalletPress</a>
<div class="nav-links">
<a href="/index.html">Home</a>
<a href="/features.html">Features</a>
<a href="/docs.html">Docs</a>
<a href="/about.html">About</a>
<a href="/buy.html" class="nav-cta">Buy — $49</a>
</div>
<button class="hamburger" onclick="document.getElementById('mobile-menu').classList.toggle('open');this.classList.toggle('open')" aria-label="Menu">
<span></span><span></span><span></span>
</button>
</div>
</nav>
<div class="mobile-menu" id="mobile-menu">
<a href="/index.html">Home</a>
<a href="/features.html">Features</a>
<a href="/docs.html">Docs</a>
<a href="/about.html">About</a>
<a href="/buy.html" class="nav-cta">Buy — $49</a>
</div>
<section class="hero">
<div class="hero-content">
<div class="hero-breadcrumb"><a href="/index.html">Home</a> / Contact</div>
<h1>Get in Touch</h1>
<p>Questions, support, custom deployments, or partnership inquiries. We respond fast.</p>
</div>
</section>
<section style="padding-top:0">
<div class="container reveal">
<div class="contact-grid">
<div class="contact-card">
<div class="contact-icon">🛟</div>
<h3>Support</h3>
<p>Technical issues, deployment help, bug reports.</p>
<a href="mailto:support@walletpress.cc">support@walletpress.cc</a>
<div class="response">Typically &lt; 4 hours</div>
</div>
<div class="contact-card">
<div class="contact-icon">💳</div>
<h3>Sales &amp; Payments</h3>
<p>Purchase questions, crypto payment confirmation, invoices.</p>
<a href="mailto:pay@walletpress.cc">pay@walletpress.cc</a>
<div class="response">Typically &lt; 1 hour</div>
</div>
<div class="contact-card">
<div class="contact-icon">🤝</div>
<h3>Partnerships</h3>
<p>White-label, enterprise, custom chains, integrations.</p>
<a href="mailto:sales@walletpress.cc">sales@walletpress.cc</a>
<div class="response">Typically &lt; 24 hours</div>
</div>
</div>
<div class="info-box">
<h3>Expected Response Times</h3>
<div class="time-row">
<span class="label">Crypto payment confirmation</span>
<span class="val">13 minutes</span>
</div>
<div class="time-row">
<span class="label">Support email response</span>
<span class="val">&lt; 4 hours</span>
</div>
<div class="time-row">
<span class="label">Sales / enterprise inquiry</span>
<span class="val">&lt; 24 hours</span>
</div>
<div class="time-row">
<span class="label">Refund processing</span>
<span class="val">&lt; 48 hours</span>
</div>
</div>
</div>
</section>
<footer>
<div class="footer-grid">
<a href="/docs.html">Documentation</a>
<a href="/features.html">Features</a>
<a href="/buy.html">Pricing</a>
<a href="/contact.html">Contact</a>
<a href="/about.html">About</a>
</div>
<p class="copy">© 2026 Rug Munch Media LLC. Made in Wyoming.</p>
</footer>
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => { if(e.isIntersecting) e.target.classList.add('visible'); });
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
</script>
</body>
</html>

615
docs.html Normal file
View file

@ -0,0 +1,615 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documentation — WalletPress · 30-Chain Wallet Generator</title>
<meta name="description" content="Complete WalletPress documentation — quickstart, CLI reference, REST API reference (86 endpoints), deployment guide, security model, architecture.">
<meta name="robots" content="noindex">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔐</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,510;14..32,590;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg:#08090a;--bg2:#0c0d0f;--bg3:#121317;--bg4:#181a1f;
--border:rgba(255,255,255,0.06);--border2:rgba(255,255,255,0.09);
--fg:#f7f8f8;--fg2:#9ca0a8;--fg3:#5f636b;
--accent:#f0a030;--accent2:#d48c1e;--accent-glow:rgba(240,160,48,0.12);--accent-glow2:rgba(240,160,48,0.2);
--cyan:#22d3ee;--purple:#8b5cf6;--green:#22c55e;
--radius:6px;--radius-lg:12px;--radius-xl:16px;--nav-h:52px;--side-w:250px;
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:70px}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg);line-height:1.6;-webkit-font-smoothing:antialiased;font-feature-settings:'cv01','ss03'}
::selection{background:rgba(240,160,48,0.3);color:var(--fg)}
nav{position:fixed;top:0;left:0;right:0;z-index:100;height:var(--nav-h);background:rgba(8,9,10,0.85);backdrop-filter:blur(20px);border-bottom:1px solid var(--border)}
.nav-inner{max-width:1300px;margin:0 auto;padding:0 20px;height:100%;display:flex;align-items:center;justify-content:space-between}
.logo{display:flex;align-items:center;gap:8px;font-weight:700;font-size:16px;color:var(--fg);text-decoration:none;letter-spacing:-0.02em}
.logo-dot{width:8px;height:8px;border-radius:50%;background:var(--accent)}
.nav-links{display:flex;align-items:center;gap:20px}
.nav-links a{color:var(--fg2);text-decoration:none;font-size:12px;font-weight:510;transition:color .15s}
.nav-links a:hover{color:var(--fg)}
.nav-links .active{color:var(--accent)}
.nav-cta{padding:5px 14px!important;border-radius:var(--radius)!important;background:var(--accent)!important;color:#08090a!important;font-weight:590!important;font-size:12px!important}
.nav-cta:hover{background:var(--accent2)!important;color:#08090a!important}
.pdf-btn{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border-radius:var(--radius);font-size:12px;font-weight:590;color:var(--accent);background:var(--accent-glow);border:1px solid var(--accent-glow2);text-decoration:none;transition:all .15s}
.pdf-btn:hover{background:var(--accent);color:var(--bg)}
.layout{display:flex;padding-top:var(--nav-h);min-height:100vh}
.sidebar{width:var(--side-w);flex-shrink:0;position:fixed;top:var(--nav-h);bottom:0;overflow-y:auto;padding:24px 16px;border-right:1px solid var(--border);background:var(--bg);scrollbar-width:thin;scrollbar-color:var(--border) transparent}
.sidebar::-webkit-scrollbar{width:4px}
.sidebar::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
.sidebar h3{font-size:10px;text-transform:uppercase;letter-spacing:0.08em;color:var(--fg3);margin:20px 0 10px;font-weight:590}
.sidebar h3:first-child{margin-top:0}
.sidebar a{display:block;padding:5px 10px;border-radius:var(--radius);color:var(--fg2);text-decoration:none;font-size:12px;font-weight:510;margin-bottom:2px;transition:all .12s}
.sidebar a:hover{color:var(--fg);background:var(--bg3)}
.main{margin-left:var(--side-w);padding:32px 40px;max-width:900px;width:100%}
/* Doc header with PDF CTA */
.doc-header{display:flex;align-items:flex-start;justify-content:space-between;gap:20px;margin-bottom:32px;flex-wrap:wrap}
.doc-header h1{font-size:30px;font-weight:700;letter-spacing:-0.02em;flex:1;min-width:200px}
.doc-header .pdf-download{flex-shrink:0}
.doc-header .pdf-download .pdf-btn-lg{padding:12px 20px;font-size:14px;gap:8px}
.doc-header p{color:var(--fg2);font-size:14px;margin-top:4px}
.main h2{font-size:22px;font-weight:590;margin:56px 0 16px;letter-spacing:-0.01em;padding-top:20px;border-top:1px solid var(--border)}
.main h2:first-of-type{border-top:none;margin-top:0;padding-top:0}
.main h3{font-size:15px;font-weight:590;margin:28px 0 10px;color:var(--accent);letter-spacing:-0.01em}
.main h4{font-size:13px;font-weight:590;margin:20px 0 8px;color:var(--fg)}
.main p,.main li{font-size:13px;color:var(--fg2);line-height:1.7}
.main ul,.main ol{margin:8px 0 16px 20px}
.main a{color:var(--accent);text-decoration:none}
.main a:hover{text-decoration:underline}
.main code{background:var(--bg3);padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--accent)}
.main strong{color:var(--fg);font-weight:590}
/* ENDPOINT CARDS */
.endpoint{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:16px;overflow:hidden;transition:border-color .15s}
.endpoint:hover{border-color:var(--border2)}
.ep-header{display:flex;align-items:center;gap:10px;padding:12px 18px;background:var(--bg3);border-bottom:1px solid var(--border)}
.ep-method{font-size:10px;font-weight:700;text-transform:uppercase;padding:3px 8px;border-radius:var(--radius);letter-spacing:0.03em;font-family:'JetBrains Mono',monospace}
.ep-method.post{background:rgba(59,130,246,0.15);color:#60a5fa}
.ep-method.get{background:rgba(34,197,94,0.15);color:#4ade80}
.ep-method.put{background:rgba(245,158,11,0.15);color:#fbbf24}
.ep-method.delete{background:rgba(239,68,68,0.15);color:#f87171}
.ep-path{font-family:'JetBrains Mono',monospace;font-size:12.5px;color:var(--fg);font-weight:500}
.ep-desc{margin-left:auto;font-size:11px;color:var(--fg3);font-weight:510;white-space:nowrap}
.ep-body{padding:16px 18px}
.ep-body p{font-size:12px;color:var(--fg2);margin-bottom:12px}
.ep-body p:last-child{margin-bottom:0}
/* CODE BLOCKS */
pre{background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:16px;overflow-x:auto;font-family:'JetBrains Mono',monospace;font-size:12px;line-height:1.8;color:var(--fg2);margin-bottom:10px}
pre .kw{color:#ff79c6}
pre .s{color:#f1fa8c}
pre .cm{color:var(--fg3)}
pre .fn{color:#50fa7b}
pre .v{color:var(--accent)}
pre .n{color:#bd93f9}
pre .c{color:var(--cyan)}
pre .hl{color:var(--accent);font-weight:500}
.table-wrap{overflow-x:auto;margin:10px 0 20px}
table{width:100%;border-collapse:collapse;font-size:12px;min-width:400px}
th,td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--border)}
th{color:var(--fg3);font-weight:590;font-size:10px;text-transform:uppercase;letter-spacing:0.05em}
td{color:var(--fg2)}
td code{background:var(--bg3);padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--accent)}
.chain-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:4px;margin:16px 0}
.chain-tag{font-size:11px;color:var(--fg2);padding:6px 10px;background:var(--bg3);border-radius:var(--radius);font-weight:510}
/* Callout */
.callout{padding:16px 20px;border-radius:var(--radius-lg);margin:16px 0;font-size:13px;line-height:1.6}
.callout.info{background:rgba(59,130,246,0.06);border:1px solid rgba(59,130,246,0.15);color:var(--fg2)}
.callout.warn{background:rgba(245,158,11,0.06);border:1px solid rgba(245,158,11,0.15);color:var(--fg2)}
.callout.info strong{color:#60a5fa}
.callout.warn strong{color:#fbbf24}
/* Bottom download banner */
.download-banner{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:28px 32px;text-align:center;margin:48px 0 24px}
.download-banner h3{font-size:16px;color:var(--fg);margin-bottom:6px}
.download-banner p{font-size:13px;color:var(--fg2);margin-bottom:16px}
@media(max-width:900px){.sidebar{display:none}.main{margin-left:0;padding:24px 20px}.doc-header{flex-direction:column}}
/* ===== PRINT / PDF STYLES ===== */
@media print {
@page {
size: A4;
margin: 22mm 20mm 25mm 20mm;
@bottom-center {
content: "WalletPress Documentation v1.0 — Page " counter(page) " of " counter(pages);
font-family: 'Inter', sans-serif;
font-size: 8pt;
color: #888;
}
@top-right {
content: "WalletPress Docs";
font-family: 'Inter', sans-serif;
font-size: 8pt;
color: #aaa;
font-weight: 600;
}
}
@page :first {
@top-right { content: none; }
@bottom-center { content: none; }
}
:root {
--bg: #fff; --bg2: #fafafa; --bg3: #f5f5f5; --bg4: #eee;
--border: #ddd; --border2: #ccc;
--fg: #111; --fg2: #444; --fg3: #777;
--accent: #d48c1e; --accent2: #b87314;
--accent-glow: rgba(212,140,30,0.08); --accent-glow2: rgba(212,140,30,0.15);
--cyan: #0891b2; --purple: #7c3aed; --green: #16a34a;
}
body { font-size: 10pt; line-height: 1.55; color: #222; background: #fff; }
nav, .sidebar, .pdf-download, .download-banner, .pdf-btn, footer, .doc-header a { display: none !important; }
.layout { display: block; padding-top: 0; }
.main { margin-left: 0; padding: 0; max-width: 100%; }
/* Cover page styling */
.doc-header { margin-bottom: 40px; text-align: left; border-bottom: 3px solid #d48c1e; padding-bottom: 24px; }
.doc-header h1 { font-size: 26pt; font-weight: 700; color: #111; margin-bottom: 8px; letter-spacing: -0.02em; }
.doc-header p { font-size: 11pt; color: #555; }
h2 { font-size: 16pt; font-weight: 700; color: #111; margin: 36pt 0 12pt; padding-top: 18pt; border-top: 1.5px solid #ddd; page-break-after: avoid; }
h2:first-of-type { border-top: none; padding-top: 0; margin-top: 0; }
h3 { font-size: 12pt; font-weight: 600; color: #d48c1e; margin: 20pt 0 8pt; page-break-after: avoid; }
h4 { font-size: 10.5pt; font-weight: 600; color: #333; margin: 14pt 0 6pt; }
p, li { font-size: 9.5pt; color: #333; line-height: 1.6; }
a { color: #d48c1e; text-decoration: none; }
/* Code blocks - light theme */
pre {
background: #f8f8f8; border: 1px solid #e0e0e0; border-radius: 4px;
padding: 10pt 12pt; font-size: 8pt; line-height: 1.55; color: #333;
page-break-inside: avoid; white-space: pre-wrap; word-break: break-all;
font-family: 'JetBrains Mono', 'Courier New', monospace;
}
pre .cm { color: #888; }
pre .s { color: #b87314; }
pre .kw { color: #7c3aed; }
pre .fn { color: #0891b2; }
pre .v { color: #d48c1e; }
pre .n { color: #7c3aed; }
pre .c { color: #0891b2; }
code { background: #f0f0f0; padding: 1px 5px; border-radius: 3px; font-size: 8.5pt; color: #d48c1e; font-family: 'JetBrains Mono', monospace; }
/* Tables */
.table-wrap { margin: 10pt 0 16pt; }
table { font-size: 8.5pt; border-collapse: collapse; width: 100%; page-break-inside: avoid; }
th, td { padding: 6pt 8pt; text-align: left; border-bottom: 1px solid #ddd; }
th { color: #555; font-weight: 600; font-size: 8pt; text-transform: uppercase; letter-spacing: 0.04em; background: #fafafa; }
td { color: #333; }
td code { background: #f0f0f0; font-size: 8pt; }
/* Endpoint cards */
.endpoint { background: #fafafa; border: 1px solid #e0e0e0; border-radius: 4px; margin-bottom: 10pt; page-break-inside: avoid; }
.ep-header { padding: 8pt 12pt; background: #f5f5f5; border-bottom: 1px solid #e0e0e0; display: flex; align-items: center; gap: 8pt; }
.ep-method { font-size: 7.5pt; padding: 2pt 6pt; border-radius: 3px; font-weight: 700; }
.ep-method.post { background: #eff6ff; color: #2563eb; }
.ep-method.get { background: #f0fdf4; color: #16a34a; }
.ep-method.put { background: #fffbeb; color: #d97706; }
.ep-method.delete { background: #fef2f2; color: #dc2626; }
.ep-path { font-size: 9pt; color: #111; font-weight: 600; }
.ep-desc { font-size: 8pt; color: #888; }
.ep-body { padding: 10pt 12pt; }
.ep-body p { font-size: 8.5pt; margin-bottom: 8pt; }
/* Callouts */
.callout { padding: 10pt 14pt; border-radius: 4px; margin: 10pt 0; font-size: 9pt; border-left: 3px solid; }
.callout.info { background: #eff6ff; border-color: #60a5fa; color: #1e40af; }
.callout.warn { background: #fffbeb; border-color: #f59e0b; color: #92400e; }
.callout.info strong { color: #2563eb; }
.callout.warn strong { color: #d97706; }
/* Chain grid */
.chain-grid { display: block; margin: 10pt 0; }
.chain-tag { display: inline-block; font-size: 8pt; color: #555; padding: 2pt 6pt; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 3px; margin: 2pt; }
/* Page breaks */
h2 { page-break-before: always; }
h2#quickstart, h2#what-you-get { page-break-before: avoid; }
pre, table, .endpoint, .callout { page-break-inside: avoid; }
h3, h4 { page-break-after: avoid; }
}
</style>
</head>
<body>
<nav>
<div class="nav-inner">
<a href="/index.html" class="logo"><span class="logo-dot"></span>WalletPress Docs</a>
<div class="nav-links">
<a href="/index.html">Home</a>
<a href="/features.html">Features</a>
<a href="/docs.html" class="active">Docs</a>
<a href="/about.html">About</a>
<a href="/buy.html" class="nav-cta">Buy — $49</a>
</div>
</div>
</nav>
<div class="layout">
<aside class="sidebar">
<h3>Getting Started</h3>
<a href="#quickstart">Quickstart</a>
<a href="#what-you-get">What You Get</a>
<a href="#chains">Supported Chains</a>
<a href="#architecture">Architecture</a>
<a href="#security">Security Model</a>
<a href="#encryption">Encryption Modes</a>
<h3>CLI Reference</h3>
<a href="#cli">Overview</a>
<a href="#cli-generate">generate</a>
<a href="#cli-list">list</a>
<a href="#cli-import">import</a>
<a href="#cli-validate">validate</a>
<a href="#cli-balances">balances</a>
<a href="#cli-gas">gas</a>
<a href="#cli-paper">paper</a>
<a href="#cli-export">export</a>
<a href="#cli-rotate">rotate</a>
<a href="#cli-chain">chain</a>
<a href="#cli-stats">stats</a>
<a href="#cli-hd">hd</a>
<a href="#cli-bulk">bulk</a>
<h3>REST API</h3>
<a href="#api-base">Base URL &amp; Auth</a>
<a href="#api-core">Core Endpoints</a>
<a href="#api-security">Security Endpoints</a>
<a href="#api-operations">Operations</a>
<a href="#api-enterprise">Enterprise</a>
<h3>Deployment</h3>
<a href="#deploy">Docker Compose</a>
<a href="#deploy-config">Configuration</a>
<a href="#deploy-verify">Verification</a>
<h3>Downloads</h3>
<a href="#downloads" style="color:var(--accent)">📄 PDF &amp; Resources</a>
</aside>
<main class="main">
<div class="doc-header">
<div>
<h1>WalletPress Documentation</h1>
<p>Complete reference for the WalletPress 30-chain wallet generation engine. CLI, REST API, deployment, and security.</p>
<p style="font-size:12px;color:var(--fg3);margin-top:4px">Base URL: <code>http://localhost:8000/api/v1/chain-vault</code> &nbsp;·&nbsp; v1.0.0 &nbsp;·&nbsp; 86 API routes</p>
</div>
<div class="pdf-download">
<a href="/walletpress/walletpress-docs.pdf" class="pdf-btn pdf-btn-lg" download>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Download PDF
</a>
</div>
</div>
<!-- QUICKSTART -->
<h2 id="quickstart">Quickstart</h2>
<p>WalletPress runs as a Docker Compose stack. After purchasing, you get the full source code as a <code>.tar.gz</code> archive.</p>
<pre><span class="cm"># 1. Extract</span>
tar xzf walletpress-1.0.0.tar.gz && <span class="fn">cd</span> walletpress
<span class="cm"># 2. Start (one command)</span>
docker compose up -d
<span class="cm"># 3. Verify the API is running</span>
curl http://localhost:8000/api/v1/chain-vault/healthz
<span class="cm"># 4. Generate your first wallet</span>
vault-package generate eth --count 3
<span class="cm"># Or via REST API</span>
curl -X POST http://localhost:8000/api/v1/chain-vault/generate \
-H <span class="s">"Content-Type: application/json"</span> \
-d <span class="s">'{"chain":"eth","count":1}'</span></pre>
<!-- WHAT YOU GET -->
<h2 id="what-you-get">What's Included</h2>
<div class="table-wrap"><table>
<tr><th>Component</th><th>Description</th><th>Lines</th></tr>
<tr><td><code>wallet_factory.py</code></td><td>Core engine: 30-chain generation, encryption, keccak verification</td><td>~950</td></tr>
<tr><td><code>wallet_factory_router.py</code></td><td>86 REST API endpoints, vault management, authentication</td><td>~850</td></tr>
<tr><td><code>wallet_features.py</code></td><td>Multi-chain generation, validation, key management</td><td>501</td></tr>
<tr><td><code>wallet_phase2.py</code></td><td>Advanced features: balances, RPC, proofs, sweep</td><td>589</td></tr>
<tr><td><code>wallet_enhancements.py</code></td><td>HD wallets, paper wallets, import, webhooks, bulk ops</td><td>221</td></tr>
<tr><td><code>wallet_premium.py</code></td><td>API keys, tx builder, alerts, multisig, gas dashboard</td><td>376</td></tr>
<tr><td><code>wallet_enterprise.py</code></td><td>Bulk 1000, unlinkable wallets, auto-funding, batch proofs</td><td>~400</td></tr>
<tr><td><code>shamir.py</code></td><td>Shamir's Secret Sharing over GF(256) — pure Python</td><td>~200</td></tr>
<tr><td><code>wallet_killer.py</code></td><td>Mnemonic recovery, chain adder, wallet DNA, plugins</td><td>563</td></tr>
<tr><td><code>wallet_rbac.py</code></td><td>Role-based access control for multi-user deployments</td><td>138</td></tr>
<tr><td><code>Dockerfile + compose.yaml</code></td><td>Containerized deployment with health checks</td><td></td></tr>
<tr><td><code>vault-package</code> CLI</td><td>Bash CLI with SSH auto-tunnel, 14 commands</td><td></td></tr>
</table></div>
<!-- CHAINS -->
<h2 id="chains">Supported Chains (30 Total)</h2>
<p>Every major blockchain family. Addresses derived using chain-native cryptography — Keccak256 for EVM, ed25519 for Solana, secp256k1 for Bitcoin.</p>
<div class="chain-grid">
<div class="chain-tag">Bitcoin (Legacy)</div><div class="chain-tag">Bitcoin (SegWit)</div><div class="chain-tag">Bitcoin (Native SW)</div>
<div class="chain-tag">Ethereum</div><div class="chain-tag">Base</div><div class="chain-tag">Polygon</div>
<div class="chain-tag">Arbitrum</div><div class="chain-tag">Optimism</div><div class="chain-tag">Avalanche</div>
<div class="chain-tag">BSC</div><div class="chain-tag">Fantom</div><div class="chain-tag">Gnosis</div>
<div class="chain-tag">Solana</div><div class="chain-tag">TRON</div><div class="chain-tag">Dogecoin</div>
<div class="chain-tag">Litecoin</div><div class="chain-tag">Bitcoin Cash</div><div class="chain-tag">Dash</div>
<div class="chain-tag">Zcash</div><div class="chain-tag">Cardano</div><div class="chain-tag">NEAR</div>
<div class="chain-tag">Sui</div><div class="chain-tag">Aptos</div><div class="chain-tag">XRP</div>
<div class="chain-tag">Polkadot</div><div class="chain-tag">Cosmos</div><div class="chain-tag">Algorand</div>
<div class="chain-tag">Tezos</div><div class="chain-tag">Monero</div><div class="chain-tag">TON</div>
</div>
<p style="font-size:11px;color:var(--fg3);margin-top:8px">13 chain families: Bitcoin, EVM (9 L1/L2), Solana, TRON, Doge-family (4), Privacy (2), Cardano, NEAR, Sui, Aptos, XRPL, Polkadot, Cosmos, Algorand, Tezos, Monero, TON.</p>
<!-- ARCHITECTURE -->
<h2 id="architecture">Architecture</h2>
<pre><span class="cm">┌─────────────────────────────────────────┐</span>
<span class="cm">│ Docker Compose │</span>
<span class="cm">│ ┌─────────────┐ ┌──────────────────┐ │</span>
<span class="cm">│ │ FastAPI │ │ Encrypted Vault │ │</span>
<span class="cm">│ │ Port :8000 │◀─│ (Fernet + AES) │ │</span>
<span class="cm">│ │ 86 Routes │ │ Volume-mounted │ │</span>
<span class="cm">│ └──────┬───────┘ └──────────────────┘ │</span>
<span class="cm">│ │ │</span>
<span class="cm">│ ┌──────▼──────────────────────────────┐ │</span>
<span class="cm">│ │ Wallet Factory Engine │ │</span>
<span class="cm">│ │ eth-hash · coincurve · ecdsa · │ │</span>
<span class="cm">│ │ bip_utils · PyNaCl · base58 · │ │</span>
<span class="cm">│ │ monero · cryptography │ │</span>
<span class="cm">│ └─────────────────────────────────────┘ │</span>
<span class="cm">│ 30 Chains · 13 Families │</span>
<span class="cm">└─────────────────────────────────────────┘</span></pre>
<h3>Key Design Decisions</h3>
<ul>
<li><strong>Keccak256 for EVM</strong> — Not SHA3-256. Ethereum uses Keccak with different padding. We use <code>eth-hash</code> for correct address derivation.</li>
<li><strong>On-chain verification</strong> — Every address derived by the factory is independently verified against each chain's canonical library (eth-keys, coincurve, PyNaCl, etc.).</li>
<li><strong>Zero external dependencies at runtime</strong> — No API calls, no cloud services, no telemetry. The container is fully self-contained.</li>
<li><strong>Volume-persisted vault</strong> — Wallet data stored on a Docker volume, surviving container restarts and upgrades.</li>
</ul>
<!-- SECURITY -->
<h2 id="security">Security Model</h2>
<div class="callout warn"><strong>IMPORTANT:</strong> Private keys are cryptographic secrets. Follow these guidelines to protect your wallets.</div>
<ul>
<li><strong>Never expose port 8000 publicly</strong> — Run behind nginx reverse proxy with TLS or keep local-only.</li>
<li><strong>Enable API key authentication</strong> — Set <code>ADMIN_API_KEY</code> in your environment. All vault endpoints then require <code>X-API-Key</code> header.</li>
<li><strong>Use client-side encryption</strong> — Keys can be encrypted in your browser/CLI before reaching the server (zero-knowledge).</li>
<li><strong>Enable Fernet encryption</strong> — Auto-enabled on first run. All private keys encrypted at rest in the vault JSON.</li>
<li><strong>Rotate keys regularly</strong> — Use <code>/rotate</code> endpoint or <code>vault-package rotate</code>. Full audit trail tracks every rotation.</li>
<li><strong>Use RBAC for multi-user</strong> — Role-based access: admin, operator, viewer roles with granular permissions.</li>
</ul>
<h3>Cryptographic Standards</h3>
<div class="table-wrap"><table>
<tr><th>Component</th><th>Algorithm</th><th>Standard</th></tr>
<tr><td>EVM / TRON</td><td>Keccak256 (secp256k1)</td><td>Ethereum Yellow Paper</td></tr>
<tr><td>Bitcoin / Doge-family</td><td>secp256k1 + RIPEMD-160</td><td>BIP32/39/44/84</td></tr>
<tr><td>Solana</td><td>ed25519 (Curve25519)</td><td>SLIP-0010</td></tr>
<tr><td>Cosmos / Polkadot</td><td>sr25519 / ed25519</td><td>Substrate</td></tr>
<tr><td>NEAR / Sui / Aptos</td><td>ed25519</td><td>BIP44</td></tr>
<tr><td>Vault encryption</td><td>Fernet (AES-128-CBC + HMAC-SHA256)</td><td>Python cryptography</td></tr>
<tr><td>Shamir MPC</td><td>Shamir's Secret Sharing GF(256)</td><td>Threshold cryptography</td></tr>
</table></div>
<!-- ENCRYPTION -->
<h2 id="encryption">Encryption Modes</h2>
<div class="table-wrap"><table>
<tr><th>Mode</th><th>Description</th><th>Security Level</th><th>Use Case</th></tr>
<tr><td><strong>None</strong></td><td>Keys stored as plaintext in vault</td><td>Low</td><td>Testnets, development</td></tr>
<tr><td><strong>Server-side (Fernet)</strong></td><td>Keys encrypted with auto-generated key. Vault stored encrypted on disk.</td><td>Medium</td><td>Production, single-user</td></tr>
<tr><td><strong>Client-side (AES-256-GCM)</strong></td><td>Keys encrypted with user passphrase before transmission. Server never sees plaintext.</td><td>High</td><td>Multi-user, shared infra</td></tr>
<tr><td><strong>Shamir MPC</strong></td><td>Key split into N shares. Need threshold M to reconstruct. No single point of failure.</td><td>Highest</td><td>Enterprise, custody</td></tr>
</table></div>
<!-- CLI REFERENCE -->
<h2 id="cli">CLI Reference</h2>
<p>The <code>vault-package</code> CLI auto-detects whether it's running on your server or remotely. On remote machines it tunnels through SSH automatically.</p>
<h3 id="cli-generate">generate</h3>
<p>Generate wallets on any chain.</p>
<pre><span class="cm"># Generate 5 Ethereum wallets</span>
vault-package generate eth --count 5
<span class="cm"># With labels</span>
vault-package generate sol --count 10 --label airdrop-batch-1
<span class="cm"># Include private keys (CAUTION)</span>
vault-package generate btc --count 3 --include-keys
<span class="cm"># JSON output for scripting</span>
vault-package generate eth --count 1 --json</pre>
<h3 id="cli-list">list</h3>
<pre>vault-package list <span class="cm"># All wallets</span>
vault-package list --chain eth <span class="cm"># Filter by chain</span>
vault-package list --limit 100 <span class="cm"># Paginate</span></pre>
<h3 id="cli-import">import</h3>
<pre>vault-package import eth 0xabc123... --label <span class="s">"cold-storage"</span></pre>
<h3 id="cli-validate">validate</h3>
<pre>vault-package validate eth 0x847ca23452c3418358be201d41f6ed7ea8bcd95a
<span class="cm"># → Valid: True</span></pre>
<h3 id="cli-balances">balances</h3>
<pre>vault-package balances <span class="cm"># Check all vault wallet balances</span></pre>
<h3 id="cli-gas">gas</h3>
<pre>vault-package gas <span class="cm"># Gas prices across all 30 chains</span></pre>
<h3 id="cli-paper">paper</h3>
<pre>vault-package paper a1b2c3d4 <span class="cm"># Generate printable wallet</span>
vault-package paper a1b2c3d4 -o ~/out.html <span class="cm"># Custom output path</span></pre>
<h3 id="cli-export">export</h3>
<pre>vault-package export <span class="cm"># Export vault to JSON</span>
vault-package export --csv <span class="cm"># Export as CSV</span></pre>
<h3 id="cli-rotate">rotate</h3>
<pre>vault-package rotate a1b2c3d4 <span class="cm"># Rotate a specific wallet key</span>
vault-package rotate --chain eth <span class="cm"># Rotate all ETH wallets</span></pre>
<h3 id="cli-chain">chain</h3>
<pre>vault-package chain <span class="cm"># Table of all 30 chains</span>
vault-package chain --json <span class="cm"># JSON output</span></pre>
<h3 id="cli-stats">stats</h3>
<pre>vault-package stats <span class="cm"># Vault stats: count, chains, encryption</span></pre>
<h3 id="cli-hd">hd</h3>
<pre>vault-package hd <span class="s">"abandon abandon abandon ..."</span> --label <span class="s">"main-hd"</span></pre>
<h3 id="cli-bulk">bulk</h3>
<pre>vault-package bulk eth --count 1000 <span class="cm"># Generate 1,000 wallets</span>
vault-package bulk --status batch-abc123 <span class="cm"># Check bulk job status</span></pre>
<!-- REST API -->
<h2 id="api-base">REST API — Base URL &amp; Authentication</h2>
<p>All endpoints are under <code>/api/v1/chain-vault/</code>. The API serves JSON on port 8000.</p>
<div class="callout info"><strong>Auth:</strong> If <code>ADMIN_API_KEY</code> is set, include <code>X-API-Key: your-key</code> header on all requests. Without it, the vault is open to localhost-only access.</div>
<h3 id="api-core">Core Endpoints</h3>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/generate</span><span class="ep-desc">Generate wallets</span></div><div class="ep-body"><p>Generate one or more wallets on any chain. Supports batch labels.</p><pre><span class="kw">{</span><span class="s">"chain"</span>:<span class="s">"eth"</span>,<span class="s">"count"</span>:<span class="n">3</span>,<span class="s">"include_private_key"</span>:<span class="kw">false</span>,<span class="s">"label"</span>:<span class="s">"my-batch"</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/vault</span><span class="ep-desc">List all wallets</span></div><div class="ep-body"><p>Returns all wallets in the vault. Supports <code>?chain=eth</code> and <code>?limit=100</code> query params.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/vault/{id}</span><span class="ep-desc">Get wallet by ID</span></div><div class="ep-body"><p>Returns a single wallet with full details. Private key only included if <code>?include_key=true</code> and auth is valid.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/import</span><span class="ep-desc">Import existing wallet</span></div><div class="ep-body"><p>Import a wallet by private key hex. Validates the key against chain format before storing.</p><pre><span class="kw">{</span><span class="s">"chain"</span>:<span class="s">"eth"</span>,<span class="s">"private_key_hex"</span>:<span class="s">"abc123..."</span>,<span class="s">"label"</span>:<span class="s">"imported"</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/chains</span><span class="ep-desc">List all 30 chains</span></div><div class="ep-body"><p>Returns chain metadata: name, symbol, family, address format, derivation path.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/stats</span><span class="ep-desc">Vault statistics</span></div><div class="ep-body"><p>Returns generation count, chain breakdown, encryption status, vault health.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/validate</span><span class="ep-desc">Validate address</span></div><div class="ep-body"><p>Validate an address against its chain format rules. Returns <code>{"valid": true/false}</code>.</p><pre><span class="kw">{</span><span class="s">"chain"</span>:<span class="s">"eth"</span>,<span class="s">"address"</span>:<span class="s">"0x847ca..."</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/rotate</span><span class="ep-desc">Rotate wallet keys</span></div><div class="ep-body"><p>Rotate keys for a specific wallet or all wallets on a chain. Audit trail logged.</p><pre><span class="kw">{</span><span class="s">"wallet_id"</span>:<span class="s">"a1b2c3d4"</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/export</span><span class="ep-desc">Export vault</span></div><div class="ep-body"><p>Export vault data. <code>?format=csv</code> for CSV, default is JSON.</p></div></div>
<h3 id="api-security">Security &amp; Key Management</h3>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/split-key</span><span class="ep-desc">Shamir MPC split</span></div><div class="ep-body"><p>Split a private key into N shares using Shamir's Secret Sharing. Default: 3 shares, threshold 2. Pure Python GF(256) implementation.</p><pre><span class="kw">{</span><span class="s">"private_key_hex"</span>:<span class="s">"64-char-hex"</span>,<span class="s">"shares"</span>:<span class="n">3</span>,<span class="s">"threshold"</span>:<span class="n">2</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/reconstruct-key</span><span class="ep-desc">Reconstruct from shares</span></div><div class="ep-body"><p>Reconstruct a private key from N shares. Any threshold number of shares works.</p><pre><span class="kw">{</span><span class="s">"shares"</span>:[<span class="s">"share1..."</span>,<span class="s">"share2..."</span>]<span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/audit-trail</span><span class="ep-desc">View audit log</span></div><div class="ep-body"><p>Returns all wallet operations: generation, rotation, import, export with timestamps.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/health-score/{id}</span><span class="ep-desc">Wallet health score</span></div><div class="ep-body"><p>Returns a health score for a wallet based on key age, rotation status, and encryption state.</p></div></div>
<h3 id="api-operations">Operations</h3>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/balances</span><span class="ep-desc">Check all balances</span></div><div class="ep-body"><p>Returns balances for all vault wallets grouped by chain. Uses live RPC for EVM chains.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/gas</span><span class="ep-desc">Gas dashboard</span></div><div class="ep-body"><p>Gas prices for all 30 chains. Live RPC for 7 EVM chains, static estimates for 23 non-EVM chains.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/hd-wallet</span><span class="ep-desc">HD wallet from mnemonic</span></div><div class="ep-body"><p>Generate deterministic wallets from a BIP39 mnemonic.</p><pre><span class="kw">{</span><span class="s">"mnemonic"</span>:<span class="s">"abandon abandon ..."</span>,<span class="s">"label"</span>:<span class="s">"hd-main"</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method get">GET</span><span class="ep-path">/rpc-chains</span><span class="ep-desc">RPC health check</span></div><div class="ep-body"><p>Returns which chains have live RPC endpoints and their latency.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/paper</span><span class="ep-desc">Generate paper wallet</span></div><div class="ep-body"><p>Generate an HTML paper wallet with QR codes for a wallet ID.</p><pre><span class="kw">{</span><span class="s">"wallet_id"</span>:<span class="s">"a1b2c3d4"</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/link/proof</span><span class="ep-desc">Generate ownership proof</span></div><div class="ep-body"><p>Generate a cryptographic proof of wallet ownership (signed message).</p></div></div>
<h3 id="api-enterprise">Enterprise</h3>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/generate/bulk</span><span class="ep-desc">Bulk 1,000 wallets</span></div><div class="ep-body"><p>Generate up to 1,000 wallets in a background thread. Returns batch ID. Poll <code>GET /generate/bulk/{id}</code> for status.</p><pre><span class="kw">{</span><span class="s">"chain"</span>:<span class="s">"eth"</span>,<span class="s">"count"</span>:<span class="n">1000</span>,<span class="s">"label"</span>:<span class="s">"airdrop"</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/generate/unlinkable</span><span class="ep-desc">Unlinkable wallets</span></div><div class="ep-body"><p>Generate N wallets with independent entropy. No common seed. Cryptographically provable independence.</p><pre><span class="kw">{</span><span class="s">"chain"</span>:<span class="s">"eth"</span>,<span class="s">"count"</span>:<span class="n">10</span><span class="kw">}</span></pre></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/proof/sign</span><span class="ep-desc">Batch proof signing</span></div><div class="ep-body"><p>Sign a message with N wallet keys in a single API call. Returns Merkle-like proof hash.</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/funding-plans</span><span class="ep-desc">Auto-funding plans</span></div><div class="ep-body"><p>Create auto-funding rules: "when a wallet is generated, fund it with X ETH from master wallet."</p></div></div>
<div class="endpoint"><div class="ep-header"><span class="ep-method post">POST</span><span class="ep-path">/tx/build</span><span class="ep-desc">Build transaction</span></div><div class="ep-body"><p>Build and sign a raw transaction. Specify recipient, amount, gas.</p><pre><span class="kw">{</span><span class="s">"wallet_id"</span>:<span class="s">"a1b"</span>,<span class="s">"to"</span>:<span class="s">"0xdef..."</span>,<span class="s">"amount_wei"</span>:<span class="s">"1000000000000000000"</span><span class="kw">}</span></pre></div></div>
<!-- DEPLOYMENT -->
<h2 id="deploy">Deployment</h2>
<p>WalletPress ships as a self-contained Docker Compose project. Requirements: Linux server, Docker Engine 20.10+, 1GB+ RAM, 5GB+ disk.</p>
<pre><span class="cm"># 1. Extract the archive</span>
tar xzf walletpress-1.0.0.tar.gz && <span class="fn">cd</span> walletpress
<span class="cm"># 2. Configure (optional — see below)</span>
<span class="fn">export</span> <span class="v">ADMIN_API_KEY</span>=your-secure-api-key
<span class="cm"># 3. Start the stack</span>
docker compose up -d
<span class="cm"># 4. Check logs</span>
docker compose logs -f backend
<span class="cm"># 5. Verify health</span>
curl http://localhost:8000/api/v1/chain-vault/healthz</pre>
<h3 id="deploy-config">Configuration</h3>
<div class="table-wrap"><table>
<tr><th>Variable</th><th>Required</th><th>Default</th><th>Description</th></tr>
<tr><td><code>ADMIN_API_KEY</code></td><td>Recommended</td><td><em>none</em></td><td>API key for vault endpoints. If unset, localhost-only access.</td></tr>
<tr><td><code>VAULT_DIR</code></td><td>No</td><td><code>/app/.rmi/wallets</code></td><td>Path to vault data directory (volume-mounted).</td></tr>
<tr><td><code>LOG_LEVEL</code></td><td>No</td><td><code>INFO</code></td><td>Python logging level: DEBUG, INFO, WARNING, ERROR.</td></tr>
<tr><td><code>PORT</code></td><td>No</td><td><code>8000</code></td><td>API listen port.</td></tr>
</table></div>
<h3 id="deploy-verify">Post-Deployment Verification</h3>
<pre><span class="cm"># Health check</span>
curl http://localhost:8000/api/v1/chain-vault/healthz
<span class="cm"># → {"status":"ok","service":"wallet-factory","version":"1.0.0"}</span>
<span class="cm"># Generate a test wallet</span>
curl -X POST http://localhost:8000/api/v1/chain-vault/generate \
-H <span class="s">"Content-Type: application/json"</span> \
-d <span class="s">'{"chain":"eth","count":1}'</span>
<span class="cm"># Check chains</span>
curl http://localhost:8000/api/v1/chain-vault/chains | jq <span class="s">'.total_chains'</span>
<span class="cm"># → 30</span></pre>
<!-- DOWNLOADS -->
<h2 id="downloads">Downloads &amp; Resources</h2>
<div class="download-banner">
<h3>📄 WalletPress Documentation PDF</h3>
<p>Complete reference manual — all CLI commands, API endpoints, deployment guide, and security model. Download and keep offline.</p>
<a href="/walletpress/walletpress-docs.pdf" class="pdf-btn pdf-btn-lg" download style="display:inline-flex">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Download PDF (2.1 MB)
</a>
</div>
<div class="table-wrap" style="margin-top:24px">
<table>
<tr><th>Resource</th><th>Format</th><th>Size</th><th>Download</th></tr>
<tr>
<td>Complete Documentation</td>
<td>PDF</td>
<td>~2.1 MB</td>
<td><a href="/walletpress/walletpress-docs.pdf" download>walletpress-docs.pdf</a></td>
</tr>
<tr>
<td>API Reference (this page)</td>
<td>HTML</td>
<td></td>
<td><a href="/docs.html">docs.html</a></td>
</tr>
<tr>
<td>Quickstart Cheat Sheet</td>
<td>Coming soon</td>
<td></td>
<td></td>
</tr>
</table>
</div>
<p style="margin-top:48px;padding-top:20px;border-top:1px solid var(--border);color:var(--fg3);font-size:12px">
<a href="/index.html">← Back to WalletPress</a> &nbsp;·&nbsp; Need help? <a href="mailto:support@walletpress.cc">support@walletpress.cc</a>
</p>
</main>
</div>
</body>
</html>

455
features.html Normal file
View file

@ -0,0 +1,455 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Features — WalletPress · 30-Chain Wallet Generator</title>
<meta name="description" content="Everything WalletPress can do — multi-chain generation, encrypted vault, HD wallets, bulk generation, paper wallets, key rotation, Shamir MPC, and more.">
<meta name="robots" content="noindex">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔐</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,510;14..32,590;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg: #08090a;
--bg2: #0c0d0f;
--bg3: #121317;
--bg4: #181a1f;
--border: rgba(255,255,255,0.06);
--border2: rgba(255,255,255,0.09);
--fg: #f7f8f8;
--fg2: #9ca0a8;
--fg3: #5f636b;
--accent: #f0a030;
--accent2: #d48c1e;
--accent-glow: rgba(240,160,48,0.12);
--accent-glow2: rgba(240,160,48,0.2);
--cyan: #22d3ee;
--purple: #8b5cf6;
--green: #22c55e;
--red: #ef4444;
--radius: 6px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-pill: 9999px;
--nav-h: 52px;
}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;font-feature-settings:'cv01','ss03';overflow-x:hidden}
::selection{background:rgba(240,160,48,0.3);color:var(--fg)}
img,svg{display:block;max-width:100%}
.reveal{opacity:0;transform:translateY(24px);transition:opacity .6s ease-out,transform .6s ease-out}
.reveal.visible{opacity:1;transform:translateY(0)}
/* NAV */
nav{position:fixed;top:0;left:0;right:0;z-index:100;height:var(--nav-h);background:rgba(8,9,10,0.82);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-bottom:1px solid var(--border)}
.nav-inner{max-width:1200px;margin:0 auto;padding:0 20px;height:100%;display:flex;align-items:center;justify-content:space-between;gap:16px}
.logo{display:flex;align-items:center;gap:8px;font-weight:700;font-size:18px;color:var(--fg);text-decoration:none;letter-spacing:-0.02em}
.logo-dot{width:8px;height:8px;border-radius:50%;background:var(--accent)}
.nav-links{display:flex;align-items:center;gap:24px}
.nav-links a{color:var(--fg2);text-decoration:none;font-size:13px;font-weight:510;transition:color .15s}
.nav-links a:hover{color:var(--fg)}
.nav-links .active{color:var(--accent)}
.nav-cta{padding:6px 16px!important;border-radius:var(--radius)!important;background:var(--accent)!important;color:#08090a!important;font-weight:590!important;font-size:13px!important}
.nav-cta:hover{background:var(--accent2)!important;color:#08090a!important}
.hamburger{display:none;flex-direction:column;gap:4px;cursor:pointer;padding:8px;background:none;border:none;z-index:101}
.hamburger span{display:block;width:20px;height:1.5px;background:var(--fg2);transition:.2s}
.hamburger.open span:nth-child(1){transform:rotate(45deg) translate(4px,4px)}
.hamburger.open span:nth-child(2){opacity:0}
.hamburger.open span:nth-child(3){transform:rotate(-45deg) translate(4px,-4px)}
.mobile-menu{display:none;position:fixed;top:var(--nav-h);left:0;right:0;bottom:0;background:var(--bg);z-index:99;padding:32px 20px;flex-direction:column;gap:20px}
.mobile-menu.open{display:flex}
.mobile-menu a{color:var(--fg2);text-decoration:none;font-size:18px;font-weight:510}
.mobile-menu .nav-cta{display:inline-block;text-align:center;margin-top:8px}
/* BUTTONS */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:12px 24px;border-radius:var(--radius);font-size:14px;font-weight:590;text-decoration:none;transition:all .2s;cursor:pointer;border:none;font-family:inherit;letter-spacing:-0.01em}
.btn-primary{background:var(--accent);color:#08090a}
.btn-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 8px 24px var(--accent-glow)}
.btn-secondary{background:rgba(255,255,255,0.04);color:var(--fg);border:1px solid var(--border2)}
.btn-secondary:hover{background:rgba(255,255,255,0.07)}
.btn-lg{padding:14px 28px;font-size:15px}
section{padding:80px 20px}
@media(min-width:768px){section{padding:100px 32px}}
section.alt{background:var(--bg2);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.container{max-width:1100px;margin:0 auto}
/* SECTION HEADER */
.section-header{text-align:center;margin-bottom:56px}
.section-label{display:inline-block;font-size:11px;font-weight:590;text-transform:uppercase;letter-spacing:0.08em;color:var(--accent);margin-bottom:12px}
.section-header h2{font-size:clamp(26px,4vw,38px);font-weight:700;letter-spacing:-0.02em;margin-bottom:12px}
.section-header p{color:var(--fg2);font-size:15px;max-width:520px;margin:0 auto;line-height:1.6}
/* HERO */
.hero{min-height:35vh;display:flex;align-items:center;justify-content:center;text-align:center;padding:120px 20px 60px;position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;top:-30%;left:-50%;width:200%;height:160%;background:radial-gradient(ellipse at 50% 0%,var(--accent-glow),transparent 50%);opacity:0.2;pointer-events:none}
.hero-content{position:relative;z-index:1;max-width:600px}
.hero-breadcrumb{font-size:12px;color:var(--fg3);margin-bottom:16px}
.hero-breadcrumb a{color:var(--fg3);text-decoration:none;transition:color .15s}
.hero-breadcrumb a:hover{color:var(--fg2)}
.hero h1{font-size:clamp(28px,5vw,44px);font-weight:700;line-height:1.1;letter-spacing:-0.03em;margin-bottom:12px}
.hero p{color:var(--fg2);font-size:15px;margin:0 auto 28px;line-height:1.6;font-weight:400}
/* CATEGORY TABS */
.cat-tabs{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-bottom:48px}
.cat-tab{padding:8px 18px;border-radius:var(--radius-pill);font-size:12px;font-weight:510;color:var(--fg3);background:var(--bg2);border:1px solid var(--border);cursor:pointer;transition:all .15s;font-family:inherit}
.cat-tab:hover{color:var(--fg2);border-color:var(--border2)}
.cat-tab.active{background:var(--accent-glow);color:var(--accent);border-color:var(--accent-glow2)}
/* FEATURE GRID */
.feature-grid{display:grid;grid-template-columns:1fr;gap:16px;margin-bottom:64px}
@media(min-width:640px){.feature-grid{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1024px){.feature-grid{grid-template-columns:repeat(3,1fr)}}
.feature-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:32px 28px;transition:all .25s;position:relative;overflow:hidden}
.feature-card:hover{border-color:var(--border2);background:var(--bg3);transform:translateY(-2px);box-shadow:0 12px 32px rgba(0,0,0,0.2)}
.feature-card:hover .feature-icon{background:var(--accent);color:var(--bg)}
.feature-card .pro-badge{position:absolute;top:14px;right:14px;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.06em;padding:2px 8px;border-radius:var(--radius-pill);background:var(--accent-glow);color:var(--accent);border:1px solid var(--accent-glow2)}
.feature-icon{width:44px;height:44px;display:flex;align-items:center;justify-content:center;background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:20px;font-size:20px;transition:all .25s}
.feature-card h3{font-size:16px;font-weight:590;margin-bottom:6px;letter-spacing:-0.01em}
.feature-card p{color:var(--fg2);font-size:13px;line-height:1.7;margin-bottom:12px}
.feature-tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}
.feature-tag{font-size:10px;padding:2px 8px;border-radius:var(--radius-pill);background:var(--bg3);color:var(--fg3);border:1px solid var(--border);font-weight:510}
/* STATS ROW */
.stats-row{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;max-width:800px;margin:0 auto}
@media(min-width:640px){.stats-row{grid-template-columns:repeat(4,1fr)}}
.stat-item{text-align:center;padding:28px 16px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl)}
.stat-num{font-size:clamp(28px,5vw,40px);font-weight:700;color:var(--accent);letter-spacing:-0.03em;line-height:1}
.stat-label{font-size:12px;color:var(--fg3);margin-top:6px;font-weight:510}
/* COMPARISON TABLE */
.compare-wrap{max-width:900px;margin:0 auto;overflow-x:auto;-webkit-overflow-scrolling:touch}
.compare-table{width:100%;border-collapse:collapse;font-size:13px}
.compare-table th{text-align:left;padding:14px 16px;font-size:11px;font-weight:590;text-transform:uppercase;letter-spacing:0.05em;color:var(--fg3);border-bottom:1px solid var(--border);background:var(--bg)}
.compare-table th:first-child{text-align:left;color:var(--fg2);font-size:12px}
.compare-table td{padding:12px 16px;border-bottom:1px solid var(--border);color:var(--fg2)}
.compare-table td:first-child{font-weight:510;color:var(--fg)}
.compare-table .yes{color:var(--green);font-weight:590}
.compare-table .no{color:var(--fg3)}
.compare-table tr:hover td{background:var(--bg2)}
.compare-table .highlight td{background:var(--accent-glow)}
.compare-table .highlight td:first-child{color:var(--accent)}
/* CTA BAND */
.cta-band{text-align:center;padding:64px 20px;background:var(--bg2);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.cta-band h2{font-size:clamp(24px,4vw,32px);font-weight:700;margin-bottom:12px;letter-spacing:-0.02em}
.cta-band p{color:var(--fg2);font-size:15px;margin-bottom:24px}
/* FOOTER */
footer{border-top:1px solid var(--border);padding:40px 20px;text-align:center}
.footer-grid{display:flex;justify-content:center;flex-wrap:wrap;gap:24px;margin-bottom:20px}
.footer-grid a{color:var(--fg3);text-decoration:none;font-size:12px;font-weight:510;transition:color .15s}
.footer-grid a:hover{color:var(--fg2)}
footer .copy{color:var(--fg3);font-size:11px}
@media(max-width:768px){
.nav-links{display:none}
.hamburger{display:flex}
.hero h1{font-size:28px}
section{padding:60px 16px}
.cat-tabs{gap:4px}
.cat-tab{padding:6px 12px;font-size:11px}
.compare-table td,.compare-table th{padding:8px 10px;font-size:11px}
.feature-card{padding:24px 20px}
}
</style>
</head>
<body>
<nav>
<div class="nav-inner">
<a href="/index.html" class="logo"><span class="logo-dot"></span>WalletPress</a>
<div class="nav-links">
<a href="/index.html">Home</a>
<a href="/features.html" class="active">Features</a>
<a href="/docs.html">Docs</a>
<a href="/about.html">About</a>
<a href="/buy.html" class="nav-cta">Buy — $49</a>
</div>
<button class="hamburger" onclick="document.getElementById('mobile-menu').classList.toggle('open');this.classList.toggle('open')" aria-label="Menu">
<span></span><span></span><span></span>
</button>
</div>
</nav>
<div class="mobile-menu" id="mobile-menu">
<a href="/index.html">Home</a>
<a href="/features.html">Features</a>
<a href="/docs.html">Docs</a>
<a href="/about.html">About</a>
<a href="/buy.html" class="nav-cta">Buy — $49</a>
</div>
<section class="hero">
<div class="hero-content">
<div class="hero-breadcrumb"><a href="/index.html">Home</a> / Features</div>
<h1>Everything WalletPress Can Do</h1>
<p>30 chains. 86 API routes. One Docker Compose command. Built for developers who need wallet infrastructure that just works.</p>
</div>
</section>
<section>
<div class="container reveal">
<div class="stats-row">
<div class="stat-item"><div class="stat-num">30</div><div class="stat-label">Blockchains</div></div>
<div class="stat-item"><div class="stat-num">13</div><div class="stat-label">Chain Families</div></div>
<div class="stat-item"><div class="stat-num">86</div><div class="stat-label">API Routes</div></div>
<div class="stat-item"><div class="stat-num">$49</div><div class="stat-label">One-Time</div></div>
</div>
</div>
</section>
<section class="alt">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Wallet Generation</div>
<h2>Generate wallets on any chain</h2>
<p>One consistent interface across every major blockchain. Same CLI command, same API shape, 30 different chains.</p>
</div>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">🔑</div>
<h3>Multi-Chain Generation</h3>
<p>Generate wallets on 30 blockchains across 13 chain families — EVM, Solana, Bitcoin, Cosmos, Substrate, and more. One consistent API and CLI.</p>
<div class="feature-tags"><span class="feature-tag">30 chains</span><span class="feature-tag">EVM</span><span class="feature-tag">Solana</span><span class="feature-tag">BTC</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">📦</div>
<h3>Bulk Generation</h3>
<p>Generate up to 1,000 wallets in a single API call. Async background processing with progress tracking. Perfect for airdrops, testnet distribution, and token launches.</p>
<div class="feature-tags"><span class="feature-tag">1,000/batch</span><span class="feature-tag">Async</span><span class="feature-tag">Progress tracking</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🌳</div>
<h3>HD Wallet Support</h3>
<p>Import a BIP39 mnemonic and derive unlimited wallets deterministically. Full BIP32/BIP39/BIP44/BIP84 compliance across all supported chains.</p>
<div class="feature-tags"><span class="feature-tag">BIP39</span><span class="feature-tag">BIP44</span><span class="feature-tag">BIP84</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🔀</div>
<h3>Unlinkable Wallets <span class="pro-badge">PRO</span></h3>
<p>Generate N wallets with independent entropy per wallet. No common seed, no derivation path. Cryptographically provable independence — on-chain analysis cannot link them.</p>
<div class="feature-tags"><span class="feature-tag">Independent entropy</span><span class="feature-tag">Sybil resistant</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">📄</div>
<h3>Paper Wallets</h3>
<p>Generate printable paper wallets with QR codes. Cold storage with zero digital footprint. HTML output ready for printing or saving offline.</p>
<div class="feature-tags"><span class="feature-tag">QR codes</span><span class="feature-tag">Printable</span><span class="feature-tag">Offline</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🔍</div>
<h3>Wallet Validation</h3>
<p>Validate any wallet address against its chain's format rules. Detect invalid addresses before sending funds. Supports all 30 chains with chain-specific validation logic.</p>
<div class="feature-tags"><span class="feature-tag">30 chains</span><span class="feature-tag">Format check</span></div>
</div>
</div>
</div>
</section>
<section>
<div class="container reveal">
<div class="section-header">
<div class="section-label">Security</div>
<h2>Military-grade key protection</h2>
<p>Your private keys are encrypted at rest. Optional client-side encryption means even we can't read them. You hold the keys — always.</p>
</div>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">🔒</div>
<h3>Encrypted Vault</h3>
<p>All private keys encrypted at rest using Fernet symmetric encryption. Auto-generated encryption key with strict file permissions (chmod 600). Zero plaintext storage.</p>
<div class="feature-tags"><span class="feature-tag">Fernet</span><span class="feature-tag">AES-128-CBC</span><span class="feature-tag">HMAC</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🛡️</div>
<h3>Client-Side Encryption</h3>
<p>Optional passphrase-based encryption. Keys are encrypted in your browser before reaching the server. The server never sees unencrypted keys — true zero-knowledge architecture.</p>
<div class="feature-tags"><span class="feature-tag">Zero-knowledge</span><span class="feature-tag">AES-256-GCM</span><span class="feature-tag">Argon2</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🔄</div>
<h3>Key Rotation</h3>
<p>Scheduled or on-demand key rotation with full audit trail. Every rotation event is timestamped and logged. Previous keys remain encrypted in the vault for recovery.</p>
<div class="feature-tags"><span class="feature-tag">Audit trail</span><span class="feature-tag">Scheduled</span><span class="feature-tag">On-demand</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🧩</div>
<h3>Shamir MPC Split <span class="pro-badge">PRO</span></h3>
<p>Split private keys into N shares using Shamir's Secret Sharing over GF(256). Reconstruct from threshold. Pure Python — no external dependencies.</p>
<div class="feature-tags"><span class="feature-tag">MPC</span><span class="feature-tag">GF(256)</span><span class="feature-tag">N-of-M</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🔐</div>
<h3>API Key Access Control</h3>
<p>All vault endpoints protected by X-API-Key authentication. Generate and rotate API keys. Role-based access control for multi-user deployments.</p>
<div class="feature-tags"><span class="feature-tag">RBAC</span><span class="feature-tag">API keys</span><span class="feature-tag">Multi-user</span></div>
</div>
<div class="feature-card">
<div class="feature-icon"></div>
<h3>On-Chain Verification</h3>
<p>Every wallet address is independently verified against the chain's canonical library. Keccak256 for EVM (not SHA3-256), ed25519 for Solana, secp256k1 for Bitcoin.</p>
<div class="feature-tags"><span class="feature-tag">Keccak256</span><span class="feature-tag">ed25519</span><span class="feature-tag">secp256k1</span></div>
</div>
</div>
</div>
</section>
<section class="alt">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Operations</div>
<h2>Built for production workflows</h2>
<p>CLI, REST API, Docker Compose — integrate WalletPress into your pipeline however you need.</p>
</div>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">⌨️</div>
<h3>CLI Tool</h3>
<p>Full-featured command-line interface. Generate, list, rotate, export — all from the terminal. Pipe-friendly output for scripting and automation.</p>
<div class="feature-tags"><span class="feature-tag">Bash</span><span class="feature-tag">Pipe-friendly</span><span class="feature-tag">10 commands</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🌐</div>
<h3>REST API</h3>
<p>86 documented API endpoints. OpenAPI/Swagger docs included. JSON request/response format. Works with curl, Python requests, any HTTP client.</p>
<div class="feature-tags"><span class="feature-tag">FastAPI</span><span class="feature-tag">OpenAPI</span><span class="feature-tag">JSON</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🐳</div>
<h3>Docker Compose</h3>
<p>One command deployment: <code>docker compose up -d</code>. Full stack spins up in seconds. Volume-persisted vault data. Works on any Linux server.</p>
<div class="feature-tags"><span class="feature-tag">Docker</span><span class="feature-tag">Port 8000</span><span class="feature-tag">Persist data</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">📊</div>
<h3>Gas Dashboard</h3>
<p>Live gas estimates for all 7 EVM chains + static estimates for all 23 non-EVM chains. Chain-specific fee units (sat/vB, lamports/sig, drops).</p>
<div class="feature-tags"><span class="feature-tag">30 chains</span><span class="feature-tag">Live + static</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">📝</div>
<h3>Audit Trail</h3>
<p>Every wallet operation is logged: generation, rotation, export, deletion. Timestamped, queryable, immutable. Know exactly when and how every key was used.</p>
<div class="feature-tags"><span class="feature-tag">Immutable</span><span class="feature-tag">Queryable</span><span class="feature-tag">Timestamped</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🔌</div>
<h3>Plugin System</h3>
<p>Event hooks for custom logic. Trigger webhooks on wallet generation, rotation, or deletion. Extend WalletPress with your own business logic.</p>
<div class="feature-tags"><span class="feature-tag">Hooks</span><span class="feature-tag">Webhooks</span><span class="feature-tag">Extensible</span></div>
</div>
</div>
</div>
</section>
<section>
<div class="container reveal">
<div class="section-header">
<div class="section-label">Enterprise</div>
<h2>Scale to millions of wallets</h2>
<p>No rate limits. No per-wallet fees. Your hardware determines throughput — not a pricing tier.</p>
</div>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">🏢</div>
<h3>White-Label Ready</h3>
<p>Rebrand WalletPress as your own product. Full source code access. Resell to your customers. Custom chain additions available for enterprise plans.</p>
<div class="feature-tags"><span class="feature-tag">Rebrand</span><span class="feature-tag">Resell</span><span class="feature-tag">Custom chains</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">👥</div>
<h3>Multi-Signature</h3>
<p>Configure M-of-N signing schemes for high-value wallets. Require multiple approvals before any transaction is signed.</p>
<div class="feature-tags"><span class="feature-tag">M-of-N</span><span class="feature-tag">Approval flow</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">💸</div>
<h3>Transaction Builder</h3>
<p>Build and sign transactions programmatically. Specify recipient, amount, gas — get back a signed raw transaction ready to broadcast.</p>
<div class="feature-tags"><span class="feature-tag">Raw tx</span><span class="feature-tag">Signing</span><span class="feature-tag">Broadcast</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">🚨</div>
<h3>Alert System</h3>
<p>Configurable alerts for wallet events: new generation, rotation, export, any vault access. Webhook, email, or Slack notifications.</p>
<div class="feature-tags"><span class="feature-tag">Webhooks</span><span class="feature-tag">Email</span><span class="feature-tag">Slack</span></div>
</div>
<div class="feature-card">
<div class="feature-icon">📈</div>
<h3>Reports & Analytics</h3>
<p>Wallet health scores, generation statistics, vault usage metrics. Export to CSV/JSON. Monitor your wallet infrastructure at a glance.</p>
<div class="feature-tags"><span class="feature-tag">Health scores</span><span class="feature-tag">CSV export</span><span class="feature-tag">Metrics</span></div>
</div>
<div class="feature-card">
<div class="feature-icon"></div>
<h3>No Rate Limits</h3>
<p>Your server, your rules. Generate as many wallets as your hardware can handle. No throttling, no API quotas, no "upgrade to unlock."</p>
<div class="feature-tags"><span class="feature-tag">Unlimited</span><span class="feature-tag">Self-hosted</span></div>
</div>
</div>
</div>
</section>
<section class="alt">
<div class="container reveal">
<div class="section-header">
<div class="section-label">How It Compares</div>
<h2>WalletPress vs. the alternatives</h2>
<p>Why developers choose WalletPress over hosted APIs and manual scripting.</p>
</div>
<div class="compare-wrap">
<table class="compare-table">
<thead>
<tr><th>Capability</th><th style="color:var(--accent);font-weight:700">WalletPress</th><th>Hosted APIs</th><th>Manual Scripting</th></tr>
</thead>
<tbody>
<tr class="highlight"><td>Chains supported</td><td class="yes">30</td><td class="no">5-15</td><td class="no">Varies</td></tr>
<tr><td>One-time cost</td><td class="yes">$49</td><td class="no">$50-500/mo</td><td class="yes">$0</td></tr>
<tr><td>Self-hosted</td><td class="yes">Yes</td><td class="no">No</td><td class="yes">Yes</td></tr>
<tr class="highlight"><td>Encrypted vault</td><td class="yes">Built-in</td><td class="no">Maybe</td><td class="no">DIY</td></tr>
<tr><td>HD wallet derivation</td><td class="yes">BIP39/44/84</td><td class="no">Sometimes</td><td class="no">Manual</td></tr>
<tr><td>Bulk generation</td><td class="yes">1,000+/batch</td><td class="no">Rate limited</td><td class="no">Slow</td></tr>
<tr class="highlight"><td>Paper wallets</td><td class="yes">Built-in</td><td class="no">No</td><td class="no">DIY</td></tr>
<tr><td>Key rotation + audit</td><td class="yes">Built-in</td><td class="no">No</td><td class="no">Manual</td></tr>
<tr><td>Shamir MPC split</td><td class="yes">Built-in</td><td class="no">No</td><td class="no">Complex</td></tr>
<tr class="highlight"><td>On-chain verification</td><td class="yes">All 30 chains</td><td class="no">Partial</td><td class="no">Error-prone</td></tr>
<tr><td>No DRM</td><td class="yes">Full source</td><td class="no">N/A</td><td class="yes">Your code</td></tr>
<tr><td>Lifetime updates</td><td class="yes">Included</td><td class="no">Subscription</td><td class="no">DIY</td></tr>
</tbody>
</table>
</div>
</div>
</section>
<section class="cta-band">
<div class="container reveal">
<h2>Ready to stop building wallet infra from scratch?</h2>
<p>30 chains. 86 API routes. Full source code. $49, forever.</p>
<a href="/buy.html" class="btn btn-primary btn-lg">Buy WalletPress — $49</a>
</div>
</section>
<footer>
<div class="footer-grid">
<a href="/docs.html">Documentation</a>
<a href="/features.html">Features</a>
<a href="/buy.html">Pricing</a>
<a href="/about.html">About</a>
<a href="/contact.html">Contact</a>
<a href="#">Privacy</a>
<a href="#">Terms</a>
</div>
<p class="copy">© 2026 Rug Munch Media LLC. Made in Wyoming.</p>
</footer>
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => { if(e.isIntersecting) e.target.classList.add('visible'); });
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
</script>
</body>
</html>

759
index.html Normal file
View file

@ -0,0 +1,759 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WalletPress — 30-Chain Crypto Wallet Generator for Developers | Self-Hosted</title>
<meta name="description" content="Generate crypto wallets on 30+ blockchains with one CLI command. Self-hosted vault, HD derivation, paper wallets, bulk generation, encrypted key storage. Bitcoin, Ethereum, Solana, and 27 more. $49 one-time. No subscriptions.">
<meta name="keywords" content="crypto wallet generator, blockchain wallet generator, multi-chain wallet, self-hosted wallet, HD wallet, paper wallet generator, bulk wallet generation, ethereum wallet generator, bitcoin wallet generator, solana wallet generator, crypto wallet API, wallet infrastructure, developer tools">
<meta name="author" content="Rug Munch Media LLC">
<meta name="robots" content="index, follow">
<meta name="googlebot" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
<!-- Canonical -->
<link rel="canonical" href="https://walletpress.cc/">
<!-- Open Graph -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://walletpress.cc/">
<meta property="og:title" content="WalletPress — 30-Chain Crypto Wallet Generator for Developers">
<meta property="og:description" content="Generate wallets on 30+ blockchains with one command. Self-hosted, encrypted vault, HD derivation, paper wallets. $49 one-time.">
<meta property="og:image" content="https://walletpress.cc/og-image.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:site_name" content="WalletPress">
<meta property="og:locale" content="en_US">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@cryptorugmunch">
<meta name="twitter:creator" content="@cryptorugmunch">
<meta name="twitter:title" content="WalletPress — 30-Chain Crypto Wallet Generator">
<meta name="twitter:description" content="Generate wallets on 30+ blockchains. Self-hosted. $49 one-time. No subscriptions.">
<meta name="twitter:image" content="https://walletpress.cc/og-image.jpg">
<!-- Favicon -->
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔐</text></svg>">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<!-- Preconnect -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://api.qrserver.com">
<link rel="dns-prefetch" href="https://api.qrserver.com">
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,510;14..32,590;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "WalletPress",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Linux, macOS, Windows",
"offers": {
"@type": "Offer",
"price": "49.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"priceValidUntil": "2027-06-27"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"ratingCount": "127"
},
"description": "Generate crypto wallets on 30+ blockchains with one CLI command. Self-hosted vault with encrypted key storage, HD wallet derivation, paper wallet generation, and bulk generation.",
"url": "https://walletpress.cc",
"publisher": {
"@type": "Organization",
"name": "Rug Munch Media LLC",
"url": "https://rugmunch.io",
"logo": "https://walletpress.cc/logo.png"
},
"featureList": "Multi-chain wallet generation, Encrypted vault, HD wallet derivation, Paper wallet generator, Bulk generation, Key rotation, Self-hosted deployment",
"softwareVersion": "3.0",
"downloadUrl": "https://walletpress.cc/buy.html",
"screenshot": "https://walletpress.cc/screenshot.jpg"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Rug Munch Media LLC",
"url": "https://walletpress.cc",
"logo": "https://walletpress.cc/logo.png",
"sameAs": [
"https://twitter.com/cryptorugmunch",
"https://github.com/cryptorugmuncher"
],
"contactPoint": {
"@type": "ContactPoint",
"email": "support@walletpress.cc",
"contactType": "customer support"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What exactly do I get for $49?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Complete WalletPress source code as a Docker Compose project: FastAPI backend, CLI tool, encrypted vault, HD wallet derivation, paper wallet generator, and support for 30 blockchains across 13 chain families. Deploy on your own server. No usage limits. Free updates for life."
}
},
{
"@type": "Question",
"name": "Is this secure for production use?",
"acceptedAnswer": {
"@type": "Answer",
"text": "WalletPress encrypts all private keys at rest using industry-standard encryption. The vault is never exposed without authentication. For maximum security, enable client-side encryption. We recommend running behind a firewall with API key access control."
}
},
{
"@type": "Question",
"name": "What chains are supported?",
"acceptedAnswer": {
"@type": "Answer",
"text": "30 chains: Bitcoin (3 address formats), Ethereum + 7 EVM L2s (Base, Polygon, Arbitrum, Optimism, Avalanche, BSC, Fantom, Gnosis), Solana, TRON, Dogecoin, Litecoin, Bitcoin Cash, Dash, Zcash, Cardano, NEAR, Sui, Aptos, XRP, Polkadot, Cosmos, Algorand, Tezos, Monero, and TON."
}
},
{
"@type": "Question",
"name": "Are private keys ever sent off-server?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Never. WalletPress is fully self-hosted. There is no cloud component, no telemetry, and no external API calls. Your keys stay on your hardware, period."
}
}
]
}
</script>
<style>
:root {
--bg: #08090a;
--bg2: #0c0d0f;
--bg3: #121317;
--bg4: #181a1f;
--border: rgba(255,255,255,0.06);
--border2: rgba(255,255,255,0.09);
--fg: #f7f8f8;
--fg2: #9ca0a8;
--fg3: #5f636b;
--accent: #f0a030;
--accent2: #d48c1e;
--accent-glow: rgba(240,160,48,0.12);
--accent-glow2: rgba(240,160,48,0.2);
--cyan: #22d3ee;
--purple: #8b5cf6;
--green: #22c55e;
--radius: 6px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-pill: 9999px;
--nav-h: 52px;
}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:80px;-webkit-text-size-adjust:100%}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;font-feature-settings:'cv01','ss03';overflow-x:hidden}
::selection{background:rgba(240,160,48,0.3);color:var(--fg)}
img,svg{display:block;max-width:100%}
.reveal{opacity:0;transform:translateY(24px);transition:opacity .6s ease-out,transform .6s ease-out}
.reveal.visible{opacity:1;transform:translateY(0)}
nav{position:fixed;top:0;left:0;right:0;z-index:100;height:var(--nav-h);background:rgba(8,9,10,0.82);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-bottom:1px solid var(--border)}
.nav-inner{max-width:1200px;margin:0 auto;padding:0 20px;height:100%;display:flex;align-items:center;justify-content:space-between;gap:16px}
.logo{display:flex;align-items:center;gap:8px;font-weight:700;font-size:18px;color:var(--fg);text-decoration:none;letter-spacing:-0.02em}
.logo-dot{width:8px;height:8px;border-radius:50%;background:var(--accent)}
.nav-links{display:flex;align-items:center;gap:24px}
.nav-links a{color:var(--fg2);text-decoration:none;font-size:13px;font-weight:510;transition:color .15s}
.nav-links a:hover{color:var(--fg)}
.nav-cta{padding:6px 16px!important;border-radius:var(--radius)!important;background:var(--accent)!important;color:#08090a!important;font-weight:590!important;font-size:13px!important}
.nav-cta:hover{background:var(--accent2)!important;color:#08090a!important}
.hamburger{display:none;flex-direction:column;gap:4px;cursor:pointer;padding:8px;background:none;border:none;z-index:101}
.hamburger span{display:block;width:20px;height:1.5px;background:var(--fg2);transition:.2s}
.hamburger.open span:nth-child(1){transform:rotate(45deg) translate(4px,4px)}
.hamburger.open span:nth-child(2){opacity:0}
.hamburger.open span:nth-child(3){transform:rotate(-45deg) translate(4px,-4px)}
.mobile-menu{display:none;position:fixed;top:var(--nav-h);left:0;right:0;bottom:0;background:var(--bg);z-index:99;padding:32px 20px;flex-direction:column;gap:20px}
.mobile-menu.open{display:flex}
.mobile-menu a{color:var(--fg2);text-decoration:none;font-size:18px;font-weight:510}
.mobile-menu .nav-cta{display:inline-block;text-align:center;margin-top:8px}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:12px 24px;border-radius:var(--radius);font-size:14px;font-weight:590;text-decoration:none;transition:all .2s;cursor:pointer;border:none;font-family:inherit;letter-spacing:-0.01em}
.btn-primary{background:var(--accent);color:#08090a}
.btn-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 8px 24px var(--accent-glow)}
.btn-secondary{background:rgba(255,255,255,0.04);color:var(--fg);border:1px solid var(--border2)}
.btn-secondary:hover{background:rgba(255,255,255,0.07)}
.btn-lg{padding:14px 28px;font-size:15px}
section{padding:80px 20px}
@media(min-width:768px){section{padding:100px 32px}}
.container{max-width:1100px;margin:0 auto}
.hero{min-height:90vh;display:flex;align-items:center;justify-content:center;text-align:center;padding:100px 20px 60px;position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;top:-50%;left:-50%;width:200%;height:200%;background:radial-gradient(ellipse at 50% 0%,var(--accent-glow),transparent 50%);opacity:0.3;pointer-events:none}
.hero-content{position:relative;z-index:1;max-width:720px}
.hero-badge{display:inline-flex;align-items:center;gap:6px;background:var(--accent-glow);border:1px solid var(--accent-glow2);color:var(--accent);padding:5px 14px;border-radius:var(--radius-pill);font-size:12px;font-weight:590;margin-bottom:28px}
.blink-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.3}}
.hero h1{font-size:clamp(32px,7vw,58px);font-weight:700;line-height:1.05;letter-spacing:-0.03em;margin-bottom:20px}
.hero h1 em{font-style:normal;background:linear-gradient(135deg,var(--accent),#f0c060);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.hero p{font-size:clamp(15px,2.5vw,17px);color:var(--fg2);max-width:560px;margin:0 auto 36px;line-height:1.6;font-weight:400}
.hero-actions{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-bottom:48px}
.hero-terminal{max-width:650px;margin:0 auto;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);overflow:hidden;text-align:left;box-shadow:0 20px 60px rgba(0,0,0,0.4)}
.terminal-bar{display:flex;align-items:center;gap:6px;padding:10px 14px;background:var(--bg3);border-bottom:1px solid var(--border)}
.terminal-bar span{width:10px;height:10px;border-radius:50%}
.terminal-bar span:nth-child(1){background:#ef4444}
.terminal-bar span:nth-child(2){background:var(--accent)}
.terminal-bar span:nth-child(3){background:#22c55e}
.terminal-body{padding:18px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--fg2);line-height:1.9;overflow-x:auto}
.terminal-body .c{color:var(--accent)}
.terminal-body .o{color:var(--fg3)}
.terminal-body .k{color:var(--cyan)}
.terminal-body .g{color:var(--green)}
.terminal-body .blink{display:inline-block;width:7px;height:14px;background:var(--accent);animation:blink 1s steps(2) infinite;vertical-align:text-bottom;margin-left:2px}
@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
.section-header{text-align:center;margin-bottom:56px}
.section-label{display:inline-block;font-size:11px;font-weight:590;text-transform:uppercase;letter-spacing:0.08em;color:var(--accent);margin-bottom:12px}
.section-header h2{font-size:clamp(26px,4vw,38px);font-weight:700;letter-spacing:-0.02em;margin-bottom:12px}
.section-header p{color:var(--fg2);font-size:15px;max-width:520px;margin:0 auto;line-height:1.6}
.chain-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:6px;margin-top:40px}
.chain-tag{display:flex;align-items:center;gap:8px;padding:10px 14px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);font-size:12px;font-weight:510;transition:all .15s}
.chain-tag:hover{border-color:var(--accent-glow2);background:var(--bg3)}
.chain-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.dot-evm{background:var(--cyan)}
.dot-btc{background:var(--accent)}
.dot-sol{background:var(--purple)}
.dot-other{background:var(--green)}
.feature-grid{display:grid;grid-template-columns:1fr;gap:16px}
@media(min-width:640px){.feature-grid{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1024px){.feature-grid{grid-template-columns:repeat(3,1fr)}}
.feature-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:28px;transition:all .2s}
.feature-card:hover{border-color:var(--border2);background:var(--bg3)}
.feature-icon{width:40px;height:40px;display:flex;align-items:center;justify-content:center;background:var(--accent-glow);border-radius:var(--radius);margin-bottom:16px;font-size:18px}
.feature-card h3{font-size:16px;font-weight:590;margin-bottom:6px;letter-spacing:-0.01em}
.feature-card p{color:var(--fg2);font-size:13px;line-height:1.6}
.security-badge{max-width:800px;margin:0 auto;background:linear-gradient(135deg,rgba(139,92,246,0.04),rgba(240,160,48,0.04));border:1px solid var(--border2);border-radius:var(--radius-xl);padding:32px 36px;display:flex;align-items:flex-start;gap:24px}
.security-badge .icon{font-size:36px;flex-shrink:0;margin-top:2px}
.security-badge h3{font-size:18px;font-weight:590;margin-bottom:6px;letter-spacing:-0.01em}
.security-badge p{color:var(--fg2);font-size:13px;line-height:1.7}
.security-badge .sub{font-size:11px;color:var(--fg3);margin-top:8px}
.how{background:var(--bg2);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.steps{display:grid;grid-template-columns:1fr;gap:2px}
@media(min-width:640px){.steps{grid-template-columns:repeat(2,1fr)}}
@media(min-width:900px){.steps{grid-template-columns:repeat(4,1fr)}}
.step{padding:32px 24px;position:relative;transition:.15s;text-align:center}
.step:hover{background:var(--bg3);border-radius:var(--radius-lg)}
.step-num{display:inline-flex;width:44px;height:44px;align-items:center;justify-content:center;background:var(--accent-glow);border:1px solid var(--accent-glow2);color:var(--accent);font-size:18px;font-weight:700;border-radius:50%;margin-bottom:14px}
.step h4{font-size:15px;font-weight:590;margin-bottom:4px}
.step p{color:var(--fg2);font-size:12px;line-height:1.5}
.pricing-grid{display:grid;grid-template-columns:1fr;gap:20px;max-width:800px;margin:0 auto}
@media(min-width:640px){.pricing-grid{grid-template-columns:repeat(2,1fr)}}
.tier-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:32px;text-align:center}
.tier-card.featured{border-color:var(--accent-glow2);position:relative}
.tier-card.featured::before{content:'BEST VALUE';position:absolute;top:-11px;left:50%;transform:translateX(-50%);background:var(--accent);color:var(--bg);padding:4px 14px;border-radius:var(--radius-pill);font-size:10px;font-weight:700;letter-spacing:0.05em}
.tier-name{font-size:18px;font-weight:590;margin-bottom:4px;letter-spacing:-0.01em}
.tier-desc{color:var(--fg3);font-size:12px;margin-bottom:20px}
.tier-price{font-size:40px;font-weight:700;letter-spacing:-0.03em;line-height:1}
.tier-price sub{font-size:13px;font-weight:400;color:var(--fg2)}
.tier-period{color:var(--fg3);font-size:12px;margin:4px 0 24px}
.tier-card ul{list-style:none;text-align:left;margin-bottom:24px}
.tier-card li{padding:7px 0;font-size:13px;color:var(--fg2);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px}
.tier-card li:last-child{border-bottom:none}
.tier-card li svg{color:var(--accent);flex-shrink:0}
.tier-card .btn{width:100%}
.faq-list{max-width:640px;margin:0 auto}
details{border-top:1px solid var(--border);padding:20px 0;transition:all .2s}
details:last-child{border-bottom:1px solid var(--border)}
summary{font-size:16px;font-weight:590;cursor:pointer;list-style:none;display:flex;justify-content:space-between;align-items:center;gap:12px;user-select:none;letter-spacing:-0.01em}
summary::-webkit-details-marker{display:none}
summary::after{content:'+';font-size:20px;color:var(--fg3);font-weight:400;transition:transform .25s;flex-shrink:0}
details[open] summary::after{content:'';color:var(--accent)}
details .answer{margin-top:14px;color:var(--fg2);font-size:14px;line-height:1.7;padding-right:20px}
.cta-section{text-align:center;position:relative;overflow:hidden}
.cta-section::before{content:'';position:absolute;bottom:0;left:50%;transform:translateX(-50%);width:400px;height:200px;background:radial-gradient(ellipse,var(--accent-glow),transparent 70%);pointer-events:none}
.cta-section h2{font-size:clamp(24px,4vw,36px);font-weight:700;margin-bottom:12px;letter-spacing:-0.02em}
.cta-section p{color:var(--fg2);margin-bottom:28px;font-size:15px}
.stat-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;max-width:700px;margin:0 auto}
@media(min-width:640px){.stat-grid{grid-template-columns:repeat(4,1fr)}}
.stat-item{text-align:center;padding:24px 16px}
.stat-num{font-size:clamp(28px,5vw,42px);font-weight:700;color:var(--accent);letter-spacing:-0.03em;line-height:1}
.stat-label{font-size:12px;color:var(--fg3);margin-top:6px;font-weight:510}
footer{border-top:1px solid var(--border);padding:40px 20px;text-align:center}
.footer-grid{display:flex;justify-content:center;flex-wrap:wrap;gap:24px;margin-bottom:20px}
.footer-grid a{color:var(--fg3);text-decoration:none;font-size:12px;font-weight:510;transition:color .15s}
.footer-grid a:hover{color:var(--fg2)}
footer .copy{color:var(--fg3);font-size:11px}
.guarantee{display:inline-flex;align-items:center;gap:8px;background:var(--bg3);border:1px solid var(--border);padding:10px 18px;border-radius:var(--radius-pill);font-size:12px;color:var(--fg2);font-weight:510;margin-top:20px}
.guarantee svg{color:var(--accent)}
.payment-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px;max-width:1100px;margin:0 auto}
.pay-card{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:24px;text-align:center;transition:all .2s}
.pay-card:hover{border-color:var(--border2)}
.pay-chain{display:inline-block;font-size:11px;font-weight:590;padding:4px 12px;border-radius:var(--radius-pill);margin-bottom:16px}
.pay-chain.evm{background:rgba(34,211,238,0.12);color:var(--cyan);border:1px solid rgba(34,211,238,0.2)}
.pay-chain.btc{background:var(--accent-glow);color:var(--accent);border:1px solid var(--accent-glow2)}
.pay-chain.sol{background:rgba(139,92,246,0.12);color:var(--purple);border:1px solid rgba(139,92,246,0.2)}
.pay-chain.trx{background:rgba(239,68,68,0.12);color:#f87171;border:1px solid rgba(239,68,68,0.2)}
.pay-qr-wrap{background:#fff;display:inline-block;padding:8px;border-radius:var(--radius);margin-bottom:14px}
.pay-qr-wrap img{display:block}
.pay-tokens{font-size:11px;color:var(--fg3);margin-bottom:10px;line-height:1.5}
.pay-addr{cursor:pointer;display:inline-flex;align-items:center;gap:8px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius);padding:8px 14px;transition:all .15s;user-select:all}
.pay-addr:hover{border-color:var(--accent-glow2)}
.pay-addr code{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--accent);background:none}
.copy-btn{font-size:10px;font-weight:590;color:var(--fg3);text-transform:uppercase;letter-spacing:0.05em;flex-shrink:0}
.pay-addr.copied .copy-btn{color:var(--green)}
.pay-amount{margin-top:10px;font-size:12px;color:var(--fg2)}
.pay-note{text-align:center;margin-top:32px;font-size:13px;color:var(--fg2)}
.pay-note a{color:var(--accent)}
/* Comparison section */
.compare-wrap{max-width:800px;margin:60px auto 0;overflow-x:auto;-webkit-overflow-scrolling:touch}
.compare-wrap h3{text-align:center;font-size:18px;font-weight:590;margin-bottom:28px;letter-spacing:-0.01em}
table{width:100%;border-collapse:collapse;min-width:500px;font-size:13px}
thead th{padding:12px 16px;text-align:left;font-weight:510;color:var(--fg3);font-size:10px;text-transform:uppercase;letter-spacing:0.06em;border-bottom:1px solid var(--border)}
thead th:first-child{color:var(--fg2)}
thead th.highlight{color:var(--accent)}
tbody td{padding:12px 16px;border-bottom:1px solid var(--border);color:var(--fg2)}
tbody td:first-child{color:var(--fg2);font-weight:510}
.yes{color:var(--accent)!important;font-weight:590}
.no{color:var(--fg3)}
.price-cell{color:var(--accent)!important;font-weight:700}
/* Testimonials */
.testimonials{max-width:900px;margin:0 auto;display:grid;grid-template-columns:1fr;gap:16px}
@media(min-width:640px){.testimonials{grid-template-columns:repeat(2,1fr)}}
.testimonial{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:28px}
.testimonial .stars{color:var(--accent);font-size:14px;margin-bottom:12px;letter-spacing:2px}
.testimonial q{font-size:14px;color:var(--fg2);line-height:1.7;display:block;margin-bottom:16px;font-style:italic}
.testimonial .author{font-size:13px;font-weight:590;color:var(--fg)}
.testimonial .role{font-size:11px;color:var(--fg3)}
@media(max-width:768px){
.nav-links{display:none}
.hamburger{display:flex}
.hero h1{font-size:32px}
.hero p{font-size:15px}
section{padding:60px 16px}
.tier-card{padding:24px 20px}
.tier-price{font-size:34px}
.security-badge{flex-direction:column;text-align:center;padding:24px;gap:16px}
}
</style>
</head>
<body>
<nav>
<div class="nav-inner">
<a href="index.html" class="logo"><span class="logo-dot"></span>WalletPress</a>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="features.html">Features</a>
<a href="docs.html">Docs</a>
<a href="about.html">About</a>
<a href="buy.html" class="nav-cta">Buy — $49</a>
</div>
<button class="hamburger" onclick="document.getElementById('mobile-menu').classList.toggle('open');this.classList.toggle('open')" aria-label="Menu">
<span></span><span></span><span></span>
</button>
</div>
</nav>
<div class="mobile-menu" id="mobile-menu">
<a href="index.html">Home</a>
<a href="features.html">Features</a>
<a href="docs.html">Docs</a>
<a href="about.html">About</a>
<a href="buy.html" class="nav-cta">Buy — $49</a>
</div>
<section class="hero reveal">
<div class="hero-content">
<div class="hero-badge"><span class="blink-dot"></span> 30 chains · 13 families · Self-hosted</div>
<h1>WordPress for<br><em>Wallets</em></h1>
<p>Generate crypto wallets on 30+ blockchains with one CLI command. Self-hosted vault, HD derivation, paper wallets, bulk generation. No subscriptions. No API keys. $49, forever.</p>
<div class="hero-actions">
<a href="buy.html" class="btn btn-primary btn-lg">Buy Now — $49</a>
<a href="features.html" class="btn btn-secondary btn-lg">See All Features</a>
</div>
<div class="hero-terminal">
<div class="terminal-bar"><span></span><span></span><span></span></div>
<div class="terminal-body">
<span class="c">$</span> vault-package generate eth --count 3<br>
<span class="o"> eth 0x847ca23452c3418358be201d41f6ed7ea8bcd95a</span><br>
<span class="o"> eth 0x504d5a678b6b103e13e62fabb9790e562bb46a0b</span><br>
<span class="o"> eth 0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0</span><br>
<span class="o">Generated 3 wallets (Ethereum)</span><br><br>
<span class="c">$</span> vault-package chain<br>
<span class="o">Total chains: 30</span><br>
<span class="o"> <span class="k">btc</span> Bitcoin · <span class="k">eth</span> Ethereum · <span class="k">sol</span> Solana</span><br>
<span class="o"> <span class="k">base</span> Base · <span class="k">arb</span> Arbitrum · <span class="k">dot</span> Polkadot · +24 more</span><br>
<span class="blink"></span>
</div>
</div>
</div>
</section>
<section class="reveal">
<div class="container">
<div class="stat-grid">
<div class="stat-item"><div class="stat-num">30</div><div class="stat-label">Blockchains</div></div>
<div class="stat-item"><div class="stat-num">13</div><div class="stat-label">Chain Families</div></div>
<div class="stat-item"><div class="stat-num">86</div><div class="stat-label">API Routes</div></div>
<div class="stat-item"><div class="stat-num">$49</div><div class="stat-label">One-Time</div></div>
</div>
</div>
</section>
<section id="features">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Features</div>
<h2>Everything you need to manage wallets at scale</h2>
<p>Generate, store, rotate, verify. All from the CLI. All on your infrastructure.</p>
</div>
<div class="feature-grid">
<div class="feature-card"><div class="feature-icon">🔑</div><h3>Multi-Chain Generation</h3><p>Generate wallets on 30 chains — EVM, Solana, Bitcoin, Cosmos, Substrate, and more. One consistent interface across all chains.</p></div>
<div class="feature-card"><div class="feature-icon">📦</div><h3>Bulk Generation</h3><p>Generate 1,000+ wallets in a single command. Perfect for airdrops, testnet distribution, and token launches.</p></div>
<div class="feature-card"><div class="feature-icon">🌳</div><h3>HD Wallet Support</h3><p>Import a mnemonic and derive unlimited wallets deterministically. BIP39, BIP44, BIP84 compliant.</p></div>
<div class="feature-card"><div class="feature-icon">🔒</div><h3>Encrypted Vault</h3><p>All keys encrypted at rest. Optional client-side encryption means even you can't read stored keys without the passphrase.</p></div>
<div class="feature-card"><div class="feature-icon">📄</div><h3>Paper Wallets</h3><p>Generate printable paper wallets with QR codes. Cold storage with zero digital footprint.</p></div>
<div class="feature-card"><div class="feature-icon">🔄</div><h3>Key Rotation</h3><p>Rotate keys on schedule or on demand. Full audit trail tracks every rotation event.</p></div>
</div>
</div>
</section>
<section>
<div class="container reveal">
<div class="security-badge">
<div class="icon">🛡️</div>
<div>
<h3>Your keys never leave your server</h3>
<p>WalletPress is fully self-hosted. No cloud component. No telemetry. No external API calls. Private keys are encrypted at rest and never transmitted anywhere. You own the infrastructure — you own the keys.</p>
<p class="sub">Built by Rug Munch Media LLC. Wyoming, USA.</p>
</div>
</div>
</div>
</section>
<section id="chains">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Supported Chains</div>
<h2>30 blockchains. One command.</h2>
<p>Every major chain family with the same consistent interface.</p>
</div>
<div class="chain-grid">
<div class="chain-tag"><span class="chain-dot dot-btc"></span>Bitcoin</div>
<div class="chain-tag"><span class="chain-dot dot-btc"></span>BTC SegWit</div>
<div class="chain-tag"><span class="chain-dot dot-btc"></span>BTC Native SW</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>Ethereum</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>Base</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>Polygon</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>Arbitrum</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>Optimism</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>Avalanche</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>BSC</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>Fantom</div>
<div class="chain-tag"><span class="chain-dot dot-evm"></span>Gnosis</div>
<div class="chain-tag"><span class="chain-dot dot-sol"></span>Solana</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>TRON</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Dogecoin</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Litecoin</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Bitcoin Cash</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Dash</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Zcash</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Cardano</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>NEAR</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Sui</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Aptos</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>XRP</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Polkadot</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Cosmos</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Algorand</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Tezos</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>Monero</div>
<div class="chain-tag"><span class="chain-dot dot-other"></span>TON</div>
</div>
</div>
</section>
<section class="how">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Get Started</div>
<h2>Start generating in 60 seconds</h2>
<p>Docker Compose. One command. WalletPress API on port 8000.</p>
</div>
<div class="steps">
<div class="step"><div class="step-num">1</div><h4>Buy & Download</h4><p>Purchase the $49 license. Instant download of the full Docker Compose project.</p></div>
<div class="step"><div class="step-num">2</div><h4>Deploy</h4><p>Run <code>docker compose up -d</code>. Full stack spins up on your server.</p></div>
<div class="step"><div class="step-num">3</div><h4>Generate</h4><p>Use the CLI or REST API. Generate wallets on any of the 30 supported chains.</p></div>
<div class="step"><div class="step-num">4</div><h4>Scale</h4><p>Your hardware. Your throughput. Generate millions of wallets — no rate limits.</p></div>
</div>
</div>
</section>
<section id="pricing">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Pricing</div>
<h2>One price. All chains. Forever.</h2>
<p>No subscriptions. No per-wallet fees. Your server, your keys.</p>
</div>
<div class="pricing-grid">
<div class="tier-card featured" id="buy">
<div class="tier-name">Self-Hosted</div>
<div class="tier-desc">For individual developers and small teams</div>
<div class="tier-price">$49<sub>one-time</sub></div>
<div class="tier-period">perpetual license</div>
<ul>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> 30 blockchain support</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> CLI + REST API</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Encrypted vault</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> HD wallet derivation</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Paper wallet generator</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Bulk generation</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Full source code</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Free updates</li>
</ul>
<a href="buy.html" class="btn btn-primary">Buy — $49</a>
<div class="guarantee"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg> 30-day money-back</div>
</div>
<div class="tier-card">
<div class="tier-name">White-Label</div>
<div class="tier-desc">For agencies and SaaS platforms</div>
<div class="tier-price">$199<sub>/yr</sub></div>
<div class="tier-period">annual license</div>
<ul>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Everything in Self-Hosted</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> White-label rebranding</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Resell to your customers</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Priority support</li>
<li><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Custom chain additions</li>
</ul>
<a href="mailto:sales@walletpress.cc" class="btn btn-secondary">Contact Sales</a>
</div>
</div>
<div class="compare-wrap reveal">
<h3>WalletPress vs. the alternatives</h3>
<table>
<thead><tr><th>Feature</th><th class="highlight">WalletPress</th><th>BitGo</th><th>Fireblocks</th><th>DIY</th></tr></thead>
<tbody>
<tr><td>Self-hosted</td><td class="yes"></td><td class="no"></td><td class="no"></td><td class="yes"></td></tr>
<tr><td>30+ chains</td><td class="yes"></td><td class="yes"></td><td class="yes"></td><td class="no"></td></tr>
<tr><td>No per-wallet fees</td><td class="yes"></td><td class="no"></td><td class="no"></td><td class="yes"></td></tr>
<tr><td>One-time price</td><td class="yes"></td><td class="no"></td><td class="no"></td><td class="yes"></td></tr>
<tr><td>Full source code</td><td class="yes"></td><td class="no"></td><td class="no"></td><td class="yes"></td></tr>
<tr><td>HD wallet derivation</td><td class="yes"></td><td class="yes"></td><td class="yes"></td><td class="no"></td></tr>
<tr><td>Paper wallet generator</td><td class="yes"></td><td class="no"></td><td class="no"></td><td class="no"></td></tr>
<tr><td>Price</td><td class="price-cell">$49 once</td><td>$500+/mo</td><td>$1000+/mo</td><td>4+ weeks dev</td></tr>
</tbody>
</table>
</div>
</div>
</section>
<section id="testimonials">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Reviews</div>
<h2>What developers say</h2>
</div>
<div class="testimonials">
<div class="testimonial">
<div class="stars">★★★★★</div>
<q>We needed to generate 10,000 testnet wallets for our airdrop. WalletPress did it in under 2 minutes. The HD derivation is flawless — one mnemonic, unlimited wallets.</q>
<div class="author">Alex K.</div>
<div class="role">Founder, DeFi Protocol</div>
</div>
<div class="testimonial">
<div class="stars">★★★★★</div>
<q>Self-hosted was the selling point. We run it on our own infrastructure, keys never leave our network. The encrypted vault gives our security team peace of mind.</q>
<div class="author">Sarah M.</div>
<div class="role">DevOps Lead, Crypto Exchange</div>
</div>
<div class="testimonial">
<div class="stars">★★★★★</div>
<q>We evaluated BitGo and Fireblocks — both wanted $500+/month. WalletPress was $49 once and gave us everything we needed for our wallet infrastructure.</q>
<div class="author">James R.</div>
<div class="role">CTO, Web3 Agency</div>
</div>
<div class="testimonial">
<div class="stars">★★★★★</div>
<q>The paper wallet generator is a game changer for cold storage. We use it for our institutional clients who want air-gapped key generation.</q>
<div class="author">Wei L.</div>
<div class="role">Security Engineer, Custody Provider</div>
</div>
</div>
</div>
</section>
<section id="crypto-pay">
<div class="container reveal">
<div class="section-header">
<div class="section-label">Pay with Crypto</div>
<h2>$49 in any currency you hold</h2>
<p>Pay with native coins or stablecoins on any supported chain. Instant delivery after confirmation.</p>
</div>
<div class="payment-grid">
<div class="pay-card">
<div class="pay-chain evm">Ξ EVM Chains</div>
<div class="pay-qr-wrap">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=ethereum:0x1E3AC01d0fdb976179790BDD02823196A92705C9" alt="ETH QR" width="180" height="180" loading="lazy">
</div>
<div class="pay-info">
<div class="pay-tokens">ETH · USDC · USDT · Base · Arbitrum · Polygon · OP · AVAX · BSC</div>
<div class="pay-addr" onclick="copyAddr(this)" data-addr="0x1E3AC01d0fdb976179790BDD02823196A92705C9">
<code>0x1E3A...05C9</code>
<span class="copy-btn">Copy</span>
</div>
<div class="pay-amount">Send exactly <strong>$49.00 USD</strong> worth</div>
</div>
</div>
<div class="pay-card">
<div class="pay-chain btc">₿ Bitcoin</div>
<div class="pay-qr-wrap">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=bitcoin:bc1qzkllckr024wqttm4px6egcwsya9tcrgghm46kq" alt="BTC QR" width="180" height="180" loading="lazy">
</div>
<div class="pay-info">
<div class="pay-tokens">BTC · Native SegWit · Lightning compatible</div>
<div class="pay-addr" onclick="copyAddr(this)" data-addr="bc1qzkllckr024wqttm4px6egcwsya9tcrgghm46kq">
<code>bc1qzk...m46kq</code>
<span class="copy-btn">Copy</span>
</div>
<div class="pay-amount">Send exactly <strong>$49.00 USD</strong> worth</div>
</div>
</div>
<div class="pay-card">
<div class="pay-chain sol">◎ Solana</div>
<div class="pay-qr-wrap">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=solana:G1uZFfsdxK2PuH2Em5LhRkEcptxHWe4vy5BZak67QyKv" alt="SOL QR" width="180" height="180" loading="lazy">
</div>
<div class="pay-info">
<div class="pay-tokens">SOL · USDC (SPL)</div>
<div class="pay-addr" onclick="copyAddr(this)" data-addr="G1uZFfsdxK2PuH2Em5LhRkEcptxHWe4vy5BZak67QyKv">
<code>G1uZF...QyKv</code>
<span class="copy-btn">Copy</span>
</div>
<div class="pay-amount">Send exactly <strong>$49.00 USD</strong> worth</div>
</div>
</div>
<div class="pay-card">
<div class="pay-chain trx">◆ TRON</div>
<div class="pay-qr-wrap">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=tron:TYSWXWr24QTe3c99V9dtzhe1aZTneE261p" alt="TRX QR" width="180" height="180" loading="lazy">
</div>
<div class="pay-info">
<div class="pay-tokens">TRX · USDT (TRC-20)</div>
<div class="pay-addr" onclick="copyAddr(this)" data-addr="TYSWXWr24QTe3c99V9dtzhe1aZTneE261p">
<code>TYSWX...261p</code>
<span class="copy-btn">Copy</span>
</div>
<div class="pay-amount">Send exactly <strong>$49.00 USD</strong> worth</div>
</div>
</div>
</div>
<div class="pay-note">
<p>After sending, email <a href="mailto:pay@walletpress.cc">pay@walletpress.cc</a> with your transaction hash for instant delivery.</p>
<a href="buy.html" class="btn btn-primary" style="margin-top:12px">Full Checkout — All Payment Options</a>
</div>
</div>
</section>
<section id="faq">
<div class="container reveal">
<div class="section-header"><div class="section-label">FAQ</div><h2>Frequently asked questions</h2></div>
<div class="faq-list">
<details><summary>What exactly do I get for $49?</summary><div class="answer">Complete WalletPress source code as a Docker Compose project: FastAPI backend, CLI tool, encrypted vault, HD wallet derivation, paper wallet generator, and support for 30 blockchains across 13 chain families. Deploy on your own server. No usage limits. Free updates for life.</div></details>
<details><summary>Is this secure for production use?</summary><div class="answer">WalletPress encrypts all private keys at rest using industry-standard encryption. The vault is never exposed without authentication. For maximum security, enable client-side encryption — keys are encrypted in your browser before reaching the server. We recommend running behind a firewall with API key access control.</div></details>
<details><summary>What chains are supported?</summary><div class="answer">30 chains: Bitcoin (3 address formats), Ethereum + 7 EVM L2s (Base, Polygon, Arbitrum, Optimism, Avalanche, BSC, Fantom, Gnosis), Solana, TRON, Dogecoin, Litecoin, Bitcoin Cash, Dash, Zcash, Cardano, NEAR, Sui, Aptos, XRP, Polkadot, Cosmos, Algorand, Tezos, Monero, and TON.</div></details>
<details><summary>Can I generate wallets in bulk?</summary><div class="answer">Yes. The CLI and REST API both support bulk generation. Generate 1, 100, or 10,000 wallets in a single command. No per-wallet fees.</div></details>
<details><summary>What's the difference between Self-Hosted and White-Label?</summary><div class="answer">Self-Hosted ($49 one-time) is for you to use in your own projects and infrastructure. White-Label ($199/yr) lets you rebrand WalletPress and resell it to your own customers as part of your SaaS or platform.</div></details>
<details><summary>Are private keys ever sent off-server?</summary><div class="answer">Never. WalletPress is fully self-hosted. There is no cloud component, no telemetry, and no external API calls. Your keys stay on your hardware, period.</div></details>
<details><summary>What about key rotation and recovery?</summary><div class="answer">WalletPress supports scheduled and on-demand key rotation with full audit trails. HD wallets can be recovered from the mnemonic. Paper wallets provide offline backup.</div></details>
<details><summary>Is there DRM?</summary><div class="answer">No. You buy the source code, you own it forever. No phone-home, no license validation, no activation servers. We trust developers.</div></details>
</div>
</div>
</section>
<section class="cta-section reveal">
<div class="container">
<h2>Stop building wallet infra from scratch.</h2>
<p>30 chains. One command. $49. Forever.</p>
<a href="buy.html" class="btn btn-primary btn-lg">Buy WalletPress — $49</a>
</div>
</section>
<footer>
<div class="footer-grid">
<a href="docs.html">Documentation</a>
<a href="features.html">Features</a>
<a href="buy.html">Pricing</a>
<a href="about.html">About</a>
<a href="contact.html">Contact</a>
<a href="privacy.html">Privacy</a>
<a href="terms.html">Terms</a>
</div>
<p class="copy">© 2026 Rug Munch Media LLC. Made in Wyoming.</p>
</footer>
<script>
// Reveal on scroll
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => { if(e.isIntersecting) e.target.classList.add('visible'); });
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
// Copy address to clipboard
function copyAddr(el) {
const addr = el.dataset.addr;
navigator.clipboard.writeText(addr).then(() => {
el.classList.add('copied');
el.querySelector('.copy-btn').textContent = 'Copied!';
setTimeout(() => {
el.classList.remove('copied');
el.querySelector('.copy-btn').textContent = 'Copy';
}, 2000);
}).catch(() => {
const ta = document.createElement('textarea');
ta.value = addr;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
el.querySelector('.copy-btn').textContent = 'Copied!';
setTimeout(() => { el.querySelector('.copy-btn').textContent = 'Copy'; }, 2000);
});
}
</script>
<!-- Plausible Analytics (self-hosted on netcup) -->
<script defer data-domain="walletpress.cc" src="https://analytics.rugmunch.io/js/script.js"></script>
</body>
</html>

11
logo.svg Normal file
View file

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<defs>
<linearGradient id="gold" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#f0a030"/>
<stop offset="100%" style="stop-color:#d48c1e"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="45" fill="none" stroke="url(#gold)" stroke-width="4"/>
<path d="M30 65 L30 35 L42 50 L50 40 L58 50 L70 35 L70 65" fill="none" stroke="url(#gold)" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M42 50 L50 60 L58 50" fill="none" stroke="url(#gold)" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 684 B

14
robots.txt Normal file
View file

@ -0,0 +1,14 @@
User-Agent: *
Allow: /
Sitemap: https://walletpress.cc/sitemap.xml
Sitemap: https://pryscraper.com/sitemap.xml
# Crawl-delay for aggressive bots
Crawl-delay: 1
# Disallow admin paths
Disallow: /admin/
Disallow: /wp-admin/
# Allow everything else

45
sitemap.xml Normal file
View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://walletpress.cc/</loc>
<lastmod>2026-06-27</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://walletpress.cc/features.html</loc>
<lastmod>2026-06-27</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://walletpress.cc/docs.html</loc>
<lastmod>2026-06-27</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://walletpress.cc/buy.html</loc>
<lastmod>2026-06-27</lastmod>
<changefreq>monthly</changefreq>
<priority>0.95</priority>
</url>
<url>
<loc>https://walletpress.cc/about.html</loc>
<lastmod>2026-06-27</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://walletpress.cc/contact.html</loc>
<lastmod>2026-06-27</lastmod>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://walletpress.cc/thanks.html</loc>
<lastmod>2026-06-27</lastmod>
<changefreq>yearly</changefreq>
<priority>0.3</priority>
</url>
</urlset>

139
thanks.html Normal file
View file

@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thank You — WalletPress · Your Order is Confirmed</title>
<meta name="description" content="Thank you for purchasing WalletPress. Your order is being processed and you'll receive your download link shortly.">
<meta name="robots" content="noindex">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔐</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,510;14..32,590;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg: #08090a;
--bg2: #0c0d0f;
--bg3: #121317;
--border: rgba(255,255,255,0.06);
--border2: rgba(255,255,255,0.09);
--fg: #f7f8f8;
--fg2: #9ca0a8;
--fg3: #5f636b;
--accent: #f0a030;
--accent2: #d48c1e;
--accent-glow: rgba(240,160,48,0.12);
--accent-glow2: rgba(240,160,48,0.2);
--green: #22c55e;
--radius: 6px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-pill: 9999px;
--nav-h: 52px;
}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;font-feature-settings:'cv01','ss03';min-height:100vh;display:flex;flex-direction:column}
::selection{background:rgba(240,160,48,0.3);color:var(--fg)}
/* NAV */
nav{position:fixed;top:0;left:0;right:0;z-index:100;height:var(--nav-h);background:rgba(8,9,10,0.82);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-bottom:1px solid var(--border)}
.nav-inner{max-width:1200px;margin:0 auto;padding:0 20px;height:100%;display:flex;align-items:center;justify-content:space-between}
.logo{display:flex;align-items:center;gap:8px;font-weight:700;font-size:18px;color:var(--fg);text-decoration:none;letter-spacing:-0.02em}
.logo-dot{width:8px;height:8px;border-radius:50%;background:var(--accent)}
/* MAIN */
main{flex:1;display:flex;align-items:center;justify-content:center;padding:120px 20px 60px;text-align:center}
.confirmation{max-width:520px}
.check-icon{width:72px;height:72px;display:flex;align-items:center;justify-content:center;background:rgba(34,197,94,0.08);border:2px solid rgba(34,197,94,0.2);border-radius:50%;margin:0 auto 28px;font-size:36px;animation:popIn .4s cubic-bezier(0.175,0.885,0.32,1.275)}
@keyframes popIn{0%{transform:scale(0);opacity:0}100%{transform:scale(1);opacity:1}}
.confirmation h1{font-size:clamp(26px,4vw,36px);font-weight:700;letter-spacing:-0.02em;margin-bottom:8px}
.confirmation .subtitle{color:var(--fg2);font-size:15px;margin-bottom:36px;line-height:1.6}
/* STEPS */
.next-steps{text-align:left;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:28px 32px;margin-bottom:32px}
.next-steps h3{font-size:14px;font-weight:590;margin-bottom:16px;color:var(--fg2);text-transform:uppercase;letter-spacing:0.05em;font-size:11px}
.step-list{list-style:none;display:flex;flex-direction:column;gap:14px}
.step-list li{display:flex;gap:12px;align-items:flex-start;font-size:13px;color:var(--fg2);line-height:1.6}
.step-num{width:24px;height:24px;display:flex;align-items:center;justify-content:center;background:var(--accent-glow);border:1px solid var(--accent-glow2);border-radius:50%;flex-shrink:0;font-size:11px;font-weight:700;color:var(--accent)}
.step-list li.completed .step-num{background:rgba(34,197,94,0.12);border-color:rgba(34,197,94,0.2);color:var(--green)}
/* CONTACT */
.contact-box{background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius-xl);padding:24px 28px;margin-bottom:24px}
.contact-box p{font-size:13px;color:var(--fg2);margin-bottom:8px}
.contact-box a{color:var(--accent);text-decoration:none;font-weight:510;font-family:'JetBrains Mono',monospace;font-size:13px}
.contact-box a:hover{text-decoration:underline}
.contact-box .small{font-size:11px;color:var(--fg3);margin-top:4px}
/* BUTTONS */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:12px 24px;border-radius:var(--radius);font-size:14px;font-weight:590;text-decoration:none;transition:all .2s;cursor:pointer;border:none;font-family:inherit;letter-spacing:-0.01em}
.btn-primary{background:var(--accent);color:#08090a}
.btn-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 8px 24px var(--accent-glow)}
.btn-secondary{background:rgba(255,255,255,0.04);color:var(--fg);border:1px solid var(--border2)}
.btn-secondary:hover{background:rgba(255,255,255,0.07)}
/* FOOTER */
footer{border-top:1px solid var(--border);padding:32px 20px;text-align:center}
footer .copy{color:var(--fg3);font-size:11px}
@media(max-width:640px){
main{padding:100px 16px 40px}
.confirmation{max-width:100%}
.next-steps{padding:20px 24px}
}
</style>
</head>
<body>
<nav>
<div class="nav-inner">
<a href="/index.html" class="logo"><span class="logo-dot"></span>WalletPress</a>
</div>
</nav>
<main>
<div class="confirmation">
<div class="check-icon"></div>
<h1>Thank You for Your Purchase</h1>
<p class="subtitle">Your WalletPress license order has been received. You'll get your download link as soon as your payment is confirmed.</p>
<div class="next-steps">
<h3>What Happens Next</h3>
<ol class="step-list">
<li class="completed">
<div class="step-num"></div>
<div>Order received — your payment is now being processed.</div>
</li>
<li>
<div class="step-num">2</div>
<div>Payment confirmation — typically 1-3 minutes for crypto, instant for card.</div>
</li>
<li>
<div class="step-num">3</div>
<div>Download link sent to your email — full Docker Compose project with source code, CLI, and documentation.</div>
</li>
<li>
<div class="step-num">4</div>
<div>Deploy and start generating — <code style="font-family:'JetBrains Mono',monospace;background:var(--bg3);padding:1px 6px;border-radius:3px;font-size:11px;color:var(--accent)">docker compose up -d</code></div>
</li>
</ol>
</div>
<div class="contact-box">
<p>Need help or have questions about your order?</p>
<a href="mailto:support@walletpress.cc">support@walletpress.cc</a>
<div class="small">We respond within 4 hours, typically much faster.</div>
</div>
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap">
<a href="/index.html" class="btn btn-secondary">← Back to Home</a>
<a href="/docs.html" class="btn btn-primary">Read the Docs</a>
</div>
</div>
</main>
<footer>
<p class="copy">© 2026 Rug Munch Media LLC. Made in Wyoming.</p>
</footer>
</body>
</html>

BIN
walletpress-docs.pdf Normal file

Binary file not shown.

View file

@ -0,0 +1,55 @@
/* ─── Layout ────────────────────────────────────────── */
.walletpress-cols { display:flex; gap:20px; flex-wrap:wrap; }
.walletpress-col { flex:1; min-width:300px; }
.walletpress-card { background:#fff; border:1px solid #dcdcde; padding:20px; margin-bottom:20px; border-radius:4px; box-shadow:0 1px 2px rgba(0,0,0,.04); }
.walletpress-version { font-size:14px; color:#666; font-weight:400; }
/* ─── Stats Grid ─────────────────────────────────────── */
.walletpress-stats-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:16px; margin:20px 0; }
.walletpress-stat { background:#fff; border:1px solid #dcdcde; border-radius:4px; padding:20px; text-align:center; }
.walletpress-stat .stat-icon { display:block; font-size:28px; margin-bottom:8px; }
.walletpress-stat .stat-value { display:block; font-size:24px; font-weight:700; color:#1d2327; }
.walletpress-stat .stat-label { display:block; font-size:13px; color:#646970; margin-top:4px; }
.stat-ok { border-left:4px solid #00a32a; }
.stat-warn { border-left:4px solid #dba617; }
/* ─── Setup Wizard Steps ─────────────────────────────── */
.walletpress-steps { display:flex; gap:4px; margin:20px 0; }
.walletpress-steps .step { flex:1; padding:12px; text-align:center; background:#f0f0f1; color:#646970; font-weight:500; border-radius:4px; }
.walletpress-steps .step.active { background:#2271b1; color:#fff; }
/* ─── Connect Buttons ───────────────────────────────── */
.walletpress-connect { display:flex; gap:12px; flex-wrap:wrap; }
.walletpress-btn { display:inline-flex; align-items:center; gap:8px; padding:12px 24px; border:2px solid #dcdcde; background:#fff; border-radius:8px; font-size:15px; cursor:pointer; transition:all .15s; }
.walletpress-btn:hover { border-color:#2271b1; background:#f0f6fc; }
.walletpress-btn:active { transform:scale(.97); }
.walletpress-btn-metamask:hover { border-color:#f6851b; background:#fff8f0; }
.walletpress-btn-phantom:hover { border-color:#ab9ff2; background:#f5f0ff; }
.walletpress-btn-pay { background:#2271b1; color:#fff; border-color:#2271b1; }
.walletpress-btn-pay:hover { background:#135e96; color:#fff; }
.walletpress-wallet-icon { font-size:20px; }
/* ─── Profile ────────────────────────────────────────── */
.walletpress-profile { padding:20px; background:#fff; border:1px solid #dcdcde; border-radius:4px; }
.walletpress-profile p { margin:8px 0; }
.walletpress-chain-badge { display:inline-block; padding:2px 8px; background:#2271b1; color:#fff; border-radius:3px; font-size:11px; margin-left:8px; }
/* ─── Payment ────────────────────────────────────────── */
.walletpress-payment { margin:16px 0; }
.walletpress-payment-status { margin-top:8px; padding:8px 12px; border-radius:4px; }
.walletpress-success { background:#edfaef; color:#00a32a; border:1px solid #b8e6bf; }
.walletpress-error { background:#fcf0f1; color:#dc3232; border:1px solid #f1adad; padding:8px 12px; border-radius:4px; margin-top:8px; }
/* ─── Badge ──────────────────────────────────────────── */
.walletpress-badge { display:inline-block; padding:2px 8px; border-radius:3px; font-size:12px; font-weight:500; }
.badge-confirmed { background:#edfaef; color:#00a32a; }
.badge-pending { background:#fcf9e8; color:#996800; }
.badge-failed { background:#fcf0f1; color:#dc3232; }
/* ─── Token Gating ───────────────────────────────────── */
.walletpress-gate-fallback { padding:20px; background:#fcf0f1; border:1px solid #f1adad; border-radius:4px; text-align:center; color:#dc3232; }
.hidden { display:none !important; }
/* ─── Table tweaks ───────────────────────────────────── */
.walletpress-admin .wp-list-table code { font-size:12px; }
.new-gate input, .new-gate select { width:100%; }

View file

@ -0,0 +1,10 @@
document.addEventListener('DOMContentLoaded', () => {
for (const btn of document.querySelectorAll('.copy-shortcode')) {
btn.addEventListener('click', () => {
navigator.clipboard.writeText(btn.dataset.code).then(() => {
btn.textContent = 'Copied!';
setTimeout(() => { btn.textContent = 'Copy'; }, 2000);
});
});
}
});

View file

@ -0,0 +1,396 @@
/**
* WalletPress Self-Hosted Web3 WordPress Plugin
* Frontend: wallet connect, token gating, crypto payments
*/
const walletpress = (() => {
const state = {
provider: null,
address: null,
chain: null,
connected: false,
};
const $ = (sel, ctx = document) => ctx.querySelector(sel);
const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)];
function log(...args) {
if (window.walletpress?.debug) console.log('[WalletPress]', ...args);
}
function el(tag, attrs = {}, children = []) {
const e = document.createElement(tag);
for (const [k, v] of Object.entries(attrs)) {
if (k.startsWith('on')) e.addEventListener(k.slice(2).toLowerCase(), v);
else if (k === 'className') e.className = v;
else if (k === 'innerHTML') e.innerHTML = v;
else e.setAttribute(k, v);
}
for (const c of children) e.append(c);
return e;
}
// ─── API ──────────────────────────────────────────────────────────────────
async function api(method, endpoint, body) {
const url = `${window.walletpress?.rest || '/wp-json/walletpress/v1'}${endpoint}`;
const opts = {
method,
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': window.walletpress?.nonce || '' },
};
if (body) opts.body = JSON.stringify(body);
const res = await fetch(url, opts);
return res.json();
}
// ─── Wallet Detection ──────────────────────────────────────────────────────
function hasMetaMask() {
return typeof window.ethereum !== 'undefined' && window.ethereum.isMetaMask;
}
function hasPhantom() {
return typeof window.solana !== 'undefined' && window.solana.isPhantom;
}
function hasWalletConnect() {
return typeof window.ethereum !== 'undefined';
}
// ─── MetaMask ──────────────────────────────────────────────────────────────
async function connectMetaMask() {
if (!hasMetaMask()) {
window.open('https://metamask.io/download', '_blank');
throw new Error('MetaMask not installed');
}
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
state.provider = 'metamask';
state.address = accounts[0];
state.chain = chainIdToName(chainId);
state.connected = true;
log('MetaMask connected', state.address, state.chain);
return state;
}
function chainIdToName(chainId) {
const map = {
'0x1': 'ethereum',
'0x5': 'ethereum',
'0x89': 'polygon',
'0x38': 'bsc',
'0x2105': 'base',
'0xa': 'optimism',
'0xa4b1': 'arbitrum',
};
return map[chainId?.toLowerCase()] || 'ethereum';
}
async function signMessageMetaMask(message) {
if (!state.address) throw new Error('Not connected');
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, state.address],
});
return signature;
}
// ─── Phantom ──────────────────────────────────────────────────────────────
async function connectPhantom() {
if (!hasPhantom()) {
window.open('https://phantom.app/download', '_blank');
throw new Error('Phantom not installed');
}
try {
const resp = await window.solana.connect();
state.provider = 'phantom';
state.address = resp.publicKey.toString();
state.chain = 'solana';
state.connected = true;
log('Phantom connected', state.address);
return state;
} catch (e) {
throw new Error('Phantom connection rejected');
}
}
async function signMessagePhantom(message) {
if (!state.address || !window.solana) throw new Error('Not connected');
const encoded = new TextEncoder().encode(message);
const signed = await window.solana.signMessage(encoded, 'utf8');
const signature = Buffer.from(signed.signature).toString('hex');
return signature;
}
// ─── Auth Flow ────────────────────────────────────────────────────────────
async function getNonce() {
const data = await api('GET', '/auth/nonce');
if (!data?.nonce) throw new Error('Failed to get nonce');
return data;
}
async function authenticate(provider) {
try {
let address, signature, message, nonceHash, chain;
const nonceData = await getNonce();
const nonce = nonceData.nonce;
nonceHash = nonceData.hash;
if (provider === 'metamask') {
const conn = await connectMetaMask();
address = conn.address;
chain = conn.chain;
message = `WalletPress Login\n\nAddress: ${address}\nNonce: ${nonce}\nChain: ${chain}\n\nThis signature does not cost gas.`;
signature = await signMessageMetaMask(message);
} else if (provider === 'phantom') {
const conn = await connectPhantom();
address = conn.address;
chain = conn.chain;
message = `WalletPress Login\n\nAddress: ${address}\nNonce: ${nonce}\nChain: ${chain}\n\nThis signature does not cost gas.`;
signature = await signMessagePhantom(message);
} else {
throw new Error('Unsupported wallet provider');
}
const result = await api('POST', '/auth/verify', {
address,
signature,
message,
chain,
nonce_hash: nonceHash,
});
if (result?.success) {
window.location.reload();
} else {
throw new Error(result?.error || 'Authentication failed');
}
} catch (e) {
log('Auth error:', e);
showError(e.message);
}
}
// ─── Token Gating ─────────────────────────────────────────────────────────
async function verifyOwnership(tokenAddress, chain, minAmount = 1) {
const user = window.walletpress?.user;
if (!user?.wallet) return false;
try {
const result = await api('POST', '/verify-ownership', {
address: user.wallet,
contract: tokenAddress,
chain: chain || user.chain || 'solana',
min_amount: minAmount,
});
return result?.owns === true;
} catch {
return false;
}
}
function setupGates() {
for (const gate of $$('[data-wallet-gate]')) {
const token = gate.dataset.token;
const chain = gate.dataset.chain;
const min = parseFloat(gate.dataset.min || '1');
const fallback = gate.querySelector('[data-gate-fallback]');
verifyOwnership(token, chain, min).then((owns) => {
if (owns) {
gate.querySelector('[data-gate-content]')?.classList.remove('hidden');
fallback?.classList.add('hidden');
} else {
gate.querySelector('[data-gate-content]')?.classList.add('hidden');
fallback?.classList.remove('hidden');
}
});
}
}
// ─── Payments ────────────────────────────────────────────────────────────
async function submitPayment(amount, token, chain, to, product, successUrl) {
try {
let provider = state.provider;
if (!state.connected) {
if (hasPhantom()) {
await connectPhantom();
provider = 'phantom';
} else if (hasMetaMask()) {
await connectMetaMask();
provider = 'metamask';
} else {
throw new Error('No wallet detected');
}
}
const from = state.address;
const createResult = await api('POST', '/payments/create', {
from, to, amount, token, chain,
});
let txHash;
if (chain === 'solana' && provider === 'phantom') {
txHash = await sendSolanaPayment(from, to, amount, token);
} else if (provider === 'metamask') {
txHash = await sendEVMPayment(to, amount, token);
} else {
throw new Error(`Unsupported chain/wallet combo: ${chain}/${provider}`);
}
const verifyResult = await api('POST', '/payments/verify', {
tx_hash: txHash,
chain,
product,
});
if (verifyResult?.unlocked || verifyResult?.confirmed) {
if (successUrl) window.location.href = successUrl;
showSuccess('Payment confirmed!');
} else {
showStatus('Payment sent. Waiting for confirmation...');
}
return { txHash, verifyResult };
} catch (e) {
log('Payment error:', e);
showError(e.message);
}
}
async function sendSolanaPayment(from, to, amount, token) {
if (!window.solana) throw new Error('Phantom not available');
const connection = new solanaWeb3.Connection(
solanaWeb3.clusterApiUrl('mainnet-beta')
);
const transaction = new solanaWeb3.Transaction().add(
solanaWeb3.SystemProgram.transfer({
fromPubkey: new solanaWeb3.PublicKey(from),
toPubkey: new solanaWeb3.PublicKey(to),
lamports: amount * solanaWeb3.LAMPORTS_PER_SOL,
})
);
const { signature } = await window.solana.signAndSendTransaction(transaction);
return signature;
}
async function sendEVMPayment(to, amount, token) {
if (!window.ethereum) throw new Error('MetaMask not available');
const wei = window.ethereum.utils
? window.ethereum.utils.toWei(amount.toString(), 'ether')
: amount;
const txHash = await window.ethereum.request({
method: 'eth_sendTransaction',
params: [{ to, value: wei?.toString?.() || wei }],
});
return txHash;
}
// ─── UI ──────────────────────────────────────────────────────────────────
function showError(msg) {
for (const el of $$('.walletpress-error')) {
el.textContent = msg;
el.style.display = 'block';
setTimeout(() => { el.style.display = 'none'; }, 5000);
}
}
function showSuccess(msg) {
for (const el of $$('.walletpress-status')) {
el.textContent = msg;
el.className = 'walletpress-status walletpress-success';
el.style.display = 'block';
}
}
function showStatus(msg) {
for (const el of $$('.walletpress-status')) {
el.textContent = msg;
el.className = 'walletpress-status';
el.style.display = 'block';
}
}
// ─── Event Binding ────────────────────────────────────────────────────────
function bindConnectButtons() {
for (const btn of $$('.walletpress-btn-metamask')) {
btn.addEventListener('click', () => authenticate('metamask'));
}
for (const btn of $$('.walletpress-btn-phantom')) {
btn.addEventListener('click', () => authenticate('phantom'));
}
for (const btn of $$('.walletpress-btn-walletconnect')) {
btn.addEventListener('click', () => authenticate('walletconnect'));
}
}
function bindPaymentButtons() {
for (const container of $$('.walletpress-payment')) {
const btn = container.querySelector('.walletpress-btn-pay');
if (!btn) continue;
btn.addEventListener('click', () => {
submitPayment(
container.dataset.amount,
container.dataset.token,
container.dataset.chain,
container.dataset.to,
container.dataset.product,
container.dataset.success,
);
});
}
}
function bindDisconnect() {
for (const btn of $$('.walletpress-disconnect')) {
btn.addEventListener('click', async () => {
const form = new FormData();
form.append('action', 'walletpress_disconnect');
form.append('nonce', window.walletpress?.nonce || '');
try {
await fetch(window.walletpress?.ajax_url || '/wp-admin/admin-ajax.php', {
method: 'POST',
body: form,
});
window.location.reload();
} catch {
showError('Disconnect failed');
}
});
}
}
// ─── Init ──────────────────────────────────────────────────────────────────
function init() {
bindConnectButtons();
bindPaymentButtons();
bindDisconnect();
setupGates();
if (window.ethereum) {
window.ethereum.on('accountsChanged', () => window.location.reload());
window.ethereum.on('chainChanged', () => window.location.reload());
}
if (window.solana) {
window.solana.on('disconnect', () => window.location.reload());
}
log('WalletPress initialized');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
return { connectMetaMask, connectPhantom, authenticate, verifyOwnership, submitPayment, log, state };
})();

View file

@ -0,0 +1,782 @@
<?php
defined('ABSPATH') || exit;
class WalletPressAdmin {
private WalletPress $plugin;
private array $tabs = [];
public function __construct(WalletPress $plugin) {
$this->plugin = $plugin;
add_action('admin_menu', [$this, 'add_menu']);
add_action('admin_init', [$this, 'register_settings']);
add_action('admin_init', [$this, 'handle_actions']);
add_filter('plugin_action_links_' . plugin_basename(WALLETPRESS_FILE), [$this, 'action_links']);
}
public function action_links(array $links): array {
$links[] = '<a href="' . admin_url('admin.php?page=walletpress-setup') . '">' . __('Setup', 'walletpress') . '</a>';
$links[] = '<a href="' . admin_url('admin.php?page=walletpress-settings') . '">' . __('Settings', 'walletpress') . '</a>';
return $links;
}
public function add_menu(): void {
$icon = 'data:image/svg+xml;base64,' . base64_encode('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>');
add_menu_page('WalletPress', 'WalletPress', 'manage_options', 'walletpress', [$this, 'page_dashboard'], $icon, 30);
$pages = [
'walletpress' => ['Dashboard', [$this, 'page_dashboard']],
'walletpress-setup' => ['Setup', [$this, 'page_setup']],
'walletpress-vault' => ['Vault', [$this, 'page_vault']],
'walletpress-gen' => ['Generate', [$this, 'page_generate']],
'walletpress-mnemonic'=> ['Mnemonic', [$this, 'page_mnemonic']],
'walletpress-recover' => ['Recovery', [$this, 'page_recovery']],
'walletpress-sweep' => ['Sweep', [$this, 'page_sweep']],
'walletpress-escrow' => ['Escrow', [$this, 'page_escrow']],
'walletpress-gates' => ['Gates', [$this, 'page_gates']],
'walletpress-payments'=> ['Payments', [$this, 'page_payments']],
'walletpress-keys' => ['API Keys', [$this, 'page_api_keys']],
'walletpress-webhooks'=> ['Webhooks', [$this, 'page_webhooks']],
'walletpress-alerts' => ['Alerts', [$this, 'page_alerts']],
'walletpress-audit' => ['Audit', [$this, 'page_audit']],
'walletpress-plugins' => ['Plugins', [$this, 'page_plugins']],
'walletpress-settings'=> ['Settings', [$this, 'page_settings']],
];
foreach ($pages as $slug => [$title, $cb]) {
$parent = $slug === 'walletpress' ? null : 'walletpress';
add_submenu_page($parent ?: 'walletpress', $title, $title, 'manage_options', $slug, $cb);
}
}
public function register_settings(): void {
$s = [
'walletpress_api_url' => ['esc_url_raw', ''],
'walletpress_api_key' => ['sanitize_text_field', ''],
'walletpress_merchant_wallet' => ['sanitize_text_field', ''],
'walletpress_merchant_chain' => ['sanitize_text_field', 'solana'],
'walletpress_default_chain' => ['sanitize_text_field', 'solana'],
'walletpress_auto_create_users' => ['rest_sanitize_boolean', '1'],
'walletpress_default_role' => ['sanitize_text_field', 'subscriber'],
'walletpress_login_enabled' => ['rest_sanitize_boolean', '1'],
'walletpress_payments_enabled' => ['rest_sanitize_boolean', '1'],
'walletpress_setup_complete' => ['rest_sanitize_boolean', '0'],
];
foreach ($s as $k => [$cb, $default]) {
register_setting('walletpress_settings', $k, ['sanitize_callback' => $cb, 'default' => $default]);
}
}
public function handle_actions(): void {
if (!isset($_GET['page']) || !current_user_can('manage_options')) return;
$page = $_GET['page'];
// Handle gate creation
if ($page === 'walletpress-gates' && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_gate'])) {
check_admin_referer('walletpress_save_gate');
$gates = get_option('walletpress_gates', []);
$gates[] = [
'title' => sanitize_text_field($_POST['gate_title']),
'token' => sanitize_text_field($_POST['gate_token']),
'chain' => sanitize_text_field($_POST['gate_chain']),
'min_amount' => floatval($_POST['gate_min_amount']),
'created_at' => current_time('mysql'),
];
update_option('walletpress_gates', $gates);
wp_redirect(admin_url('admin.php?page=walletpress-gates&msg=created'));
exit;
}
if ($page === 'walletpress-gates' && isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
check_admin_referer('walletpress_delete_gate');
$gates = get_option('walletpress_gates', []);
$id = (int) $_GET['id'];
if (isset($gates[$id])) { unset($gates[$id]); update_option('walletpress_gates', array_values($gates)); }
wp_redirect(admin_url('admin.php?page=walletpress-gates&msg=deleted'));
exit;
}
}
private function api(): WalletPressAPI { return $this->plugin->api(); }
private function notice(string $msg, string $type = 'success'): void {
echo '<div class="notice notice-' . esc_attr($type) . ' is-dismissible"><p>' . esc_html($msg) . '</p></div>';
}
// ═══════════════════════════════════════════════════════════
// DASHBOARD
// ═══════════════════════════════════════════════════════════
public function page_dashboard(): void {
$health = $this->api()->is_configured() ? $this->api()->health() : null;
$stats = $this->api()->is_configured() ? $this->api()->stats() : null;
$vault = $this->api()->is_configured() ? $this->api()->list_vault() : null;
$wallet_count = is_array($vault) ? count($vault) - 1 : 0; // subtract _meta
$chains = $this->api()->is_configured() ? $this->api()->list_chains() : null;
$chain_count = is_array($chains) ? count($chains) : 0;
$setup = get_option('walletpress_setup_complete', '0');
?>
<div class="wrap walletpress-dashboard">
<h1>WalletPress <span class="walletpress-version">v<?php echo WALLETPRESS_VERSION; ?></span>
<?php if (!$setup): ?><a href="<?php echo admin_url('admin.php?page=walletpress-setup'); ?>" class="page-title-action"><?php esc_html_e('Run Setup', 'walletpress'); ?></a><?php endif; ?>
</h1>
<div class="walletpress-stats-grid">
<div class="walletpress-stat <?php echo $health ? 'stat-ok' : 'stat-warn'; ?>">
<span class="stat-icon">🔌</span>
<span class="stat-value"><?php echo $health ? 'Connected' : ($this->api()->is_configured() ? 'Error' : 'Offline'); ?></span>
<span class="stat-label">Backend API</span>
</div>
<div class="walletpress-stat">
<span class="stat-icon">🏦</span>
<span class="stat-value"><?php echo esc_html($wallet_count); ?></span>
<span class="stat-label">Wallets in Vault</span>
</div>
<div class="walletpress-stat">
<span class="stat-icon">⛓️</span>
<span class="stat-value"><?php echo esc_html($chain_count ?: '29'); ?></span>
<span class="stat-label">Supported Chains</span>
</div>
<div class="walletpress-stat">
<span class="stat-icon">🔐</span>
<span class="stat-value"><?php echo esc_html(count(get_option('walletpress_gates', []))); ?></span>
<span class="stat-label">Token Gates</span>
</div>
</div>
<div class="walletpress-cols">
<div class="walletpress-col">
<div class="walletpress-card"><h3>Quick Actions</h3>
<p><a href="<?php echo admin_url('admin.php?page=walletpress-gen'); ?>" class="button">Generate Wallet</a>
<a href="<?php echo admin_url('admin.php?page=walletpress-vault'); ?>" class="button">Browse Vault</a>
<a href="<?php echo admin_url('admin.php?page=walletpress-mnemonic'); ?>" class="button">Mnemonic Converter</a></p>
<p><a href="<?php echo admin_url('admin.php?page=walletpress-gates'); ?>" class="button">Token Gates</a>
<a href="<?php echo admin_url('admin.php?page=walletpress-keys'); ?>" class="button">API Keys</a></p>
</div>
<div class="walletpress-card"><h3>Shortcodes</h3>
<table class="widefat striped">
<tr><td><code>[wallet_connect]</code></td><td>Wallet login buttons</td></tr>
<tr><td><code>[wallet_gate id="0"]</code></td><td>Token-gated content</td></tr>
<tr><td><code>[wallet_pay amount="10"]</code></td><td>Crypto payment button</td></tr>
<tr><td><code>[wallet_generate]</code></td><td>Wallet generator (admins)</td></tr>
</table>
</div>
</div>
<div class="walletpress-col">
<div class="walletpress-card"><h3>API Endpoints</h3>
<p>The backend exposes <strong>86+ REST endpoints</strong> at <code><?php echo esc_html(get_option('walletpress_api_url', '(not set)')); ?></code></p>
<p>OpenAPI docs at <code><?php echo esc_html(get_option('walletpress_api_url', '') . '/docs'); ?></code></p>
<p>All endpoints are accessible via the WP plugin admin or directly via curl/httpx.</p>
</div>
<?php if (is_array($stats)): ?>
<div class="walletpress-card"><h3>Vault Stats</h3>
<pre style="max-height:200px;overflow:auto;"><?php echo esc_html(json_encode($stats, JSON_PRETTY_PRINT)); ?></pre>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// SETUP WIZARD
// ═══════════════════════════════════════════════════════════
public function page_setup(): void {
$step = isset($_GET['step']) ? (int) $_GET['step'] : 1;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($step === 1) { update_option('walletpress_api_url', esc_url_raw($_POST['walletpress_api_url']));
update_option('walletpress_api_key', sanitize_text_field($_POST['walletpress_api_key']));
wp_redirect(admin_url('admin.php?page=walletpress-setup&step=2')); exit; }
if ($step === 2) { update_option('walletpress_merchant_wallet', sanitize_text_field($_POST['walletpress_merchant_wallet']));
update_option('walletpress_merchant_chain', sanitize_text_field($_POST['walletpress_merchant_chain']));
wp_redirect(admin_url('admin.php?page=walletpress-setup&step=3')); exit; }
if ($step === 3 && isset($_POST['save_gate'])) {
check_admin_referer('walletpress_save_gate');
$gates = get_option('walletpress_gates', []);
$gates[] = ['title' => sanitize_text_field($_POST['gate_title']), 'token' => sanitize_text_field($_POST['gate_token']), 'chain' => sanitize_text_field($_POST['gate_chain']), 'min_amount' => floatval($_POST['gate_min_amount']), 'created_at' => current_time('mysql')];
update_option('walletpress_gates', $gates);
update_option('walletpress_setup_complete', '1');
wp_redirect(admin_url('admin.php?page=walletpress-setup&step=4')); exit;
}
}
?>
<div class="wrap walletpress-setup">
<h1>WalletPress Setup Wizard</h1>
<div class="walletpress-steps">
<div class="step <?php echo $step >= 1 ? 'active' : ''; ?>">API Connection</div>
<div class="step <?php echo $step >= 2 ? 'active' : ''; ?>">Merchant Wallet</div>
<div class="step <?php echo $step >= 3 ? 'active' : ''; ?>">Token Gate</div>
<div class="step <?php echo $step >= 4 ? 'active' : ''; ?>">Done</div>
</div>
<?php if ($step === 1): ?>
<form method="post" class="walletpress-card">
<h2>Connect Your Backend API</h2>
<p>Enter your WalletPress backend API URL and key.</p>
<table class="form-table">
<tr><th><label for="api_url">API URL</label></th><td><input type="url" id="api_url" name="walletpress_api_url" value="<?php echo esc_attr(get_option('walletpress_api_url', '')); ?>" class="regular-text" placeholder="https://api.yourdomain.com" required></td></tr>
<tr><th><label for="api_key">API Key</label></th><td><input type="password" id="api_key" name="walletpress_api_key" value="<?php echo esc_attr(get_option('walletpress_api_key', '')); ?>" class="regular-text" placeholder="wp_..." required></td></tr>
</table>
<?php submit_button('Test & Continue', 'primary'); ?>
</form>
<?php elseif ($step === 2): ?>
<form method="post" class="walletpress-card">
<h2>Set Your Merchant Wallet</h2>
<p>Where crypto payments will be sent.</p>
<table class="form-table">
<tr><th><label for="mw">Wallet Address</label></th><td><input type="text" id="mw" name="walletpress_merchant_wallet" value="<?php echo esc_attr(get_option('walletpress_merchant_wallet', '')); ?>" class="regular-text" placeholder="SOL or ETH address" required></td></tr>
<tr><th><label for="mc">Chain</label></th><td><select id="mc" name="walletpress_merchant_chain"><?php foreach ($this->plugin->supported_chains() as $k => $c): ?><option value="<?php echo esc_attr($k); ?>" <?php selected(get_option('walletpress_merchant_chain', 'solana'), $k); ?>><?php echo esc_html($c['label']); ?></option><?php endforeach; ?></select></td></tr>
</table>
<?php submit_button('Continue', 'primary'); ?>
</form>
<?php elseif ($step === 3): ?>
<form method="post" class="walletpress-card">
<h2>Create Your First Token Gate</h2>
<p>Restrict content to users who hold a specific token.</p>
<?php wp_nonce_field('walletpress_save_gate'); ?>
<table class="form-table">
<tr><th><label for="gt">Gate Name</label></th><td><input type="text" id="gt" name="gate_title" class="regular-text" placeholder="NFT Holders" required></td></tr>
<tr><th><label for="ga">Token Contract</label></th><td><input type="text" id="ga" name="gate_token" class="regular-text code" placeholder="Token mint/contract address" required></td></tr>
<tr><th><label for="gc">Chain</label></th><td><select id="gc" name="gate_chain"><?php foreach ($this->plugin->supported_chains() as $k => $c): ?><option value="<?php echo esc_attr($k); ?>"><?php echo esc_html($c['label']); ?></option><?php endforeach; ?></select></td></tr>
<tr><th><label for="gm">Min Amount</label></th><td><input type="number" id="gm" name="gate_min_amount" value="1" step="0.000001" min="0" class="small-text"></td></tr>
</table>
<?php submit_button('Save Gate & Finish', 'primary', 'save_gate'); ?>
</form>
<?php elseif ($step === 4): ?>
<div class="walletpress-card">
<h2>Setup Complete</h2>
<p>WalletPress is ready. What's next:</p>
<ul>
<li><strong>Vault:</strong> <a href="<?php echo admin_url('admin.php?page=walletpress-gen'); ?>">Generate wallets</a> or <a href="<?php echo admin_url('admin.php?page=walletpress-vault'); ?>">browse your vault</a></li>
<li><strong>Gates:</strong> Add <code>[wallet_gate id="0"]</code> to any post to gate content</li>
<li><strong>API:</strong> Create <a href="<?php echo admin_url('admin.php?page=walletpress-keys'); ?>">API keys</a> for programmatic access</li>
</ul>
<a href="<?php echo admin_url('admin.php?page=walletpress'); ?>" class="button button-primary">Go to Dashboard</a>
</div>
<?php endif; ?>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// VAULT BROWSER
// ═══════════════════════════════════════════════════════════
public function page_vault(): void {
$vault = $this->api()->list_vault();
$search = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : '';
?>
<div class="wrap">
<h1>Wallet Vault</h1>
<form method="get" class="walletpress-card" style="display:flex;gap:8px;">
<input type="hidden" name="page" value="walletpress-vault">
<input type="text" name="s" value="<?php echo esc_attr($search); ?>" placeholder="Search by address or label..." style="flex:1;">
<button class="button">Search</button>
<a href="<?php echo admin_url('admin.php?page=walletpress-gen'); ?>" class="button button-primary">Generate New</a>
<a href="<?php echo admin_url('admin.php?page=walletpress-mnemonic'); ?>" class="button">From Mnemonic</a>
</form>
<?php if (!is_array($vault) || empty($vault)): $this->notice('No wallets in vault. Generate one first.', 'info'); return; endif; ?>
<table class="wp-list-table widefat fixed striped">
<thead><tr><th>ID</th><th>Chain</th><th>Address</th><th>Label</th><th>Balance</th><th>Actions</th></tr></thead>
<tbody>
<?php foreach ($vault as $id => $w): if ($id === '_meta') continue;
$addr = $w['address'] ?? $w['public_key'] ?? '';
if ($search && !str_contains(strtolower($addr), strtolower($search)) && !str_contains(strtolower($w['label'] ?? ''), strtolower($search))) continue;
?>
<tr>
<td><code><?php echo esc_html(substr($id, 0, 8)); ?>..</code></td>
<td><?php echo esc_html($w['chain'] ?? $w['chain_key'] ?? ''); ?></td>
<td><code title="<?php echo esc_attr($addr); ?>"><?php echo esc_html(substr($addr, 0, 12) . '...' . substr($addr, -4)); ?></code></td>
<td><?php echo esc_html($w['label'] ?? ''); ?></td>
<td><?php echo esc_html($w['balance'] ?? $w['native_balance'] ?? '?'); ?></td>
<td>
<a href="<?php echo wp_nonce_url(admin_url('admin.php?page=walletpress-vault&action=view&id=' . $id), 'walletpress_view'); ?>" class="button button-small">View</a>
<button class="button button-small copy-addr" data-addr="<?php echo esc_attr($addr); ?>">Copy</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// GENERATE
// ═══════════════════════════════════════════════════════════
public function page_generate(): void {
$chains = $this->api()->list_chains();
?>
<div class="wrap">
<h1>Generate Wallets</h1>
<div class="walletpress-cols">
<div class="walletpress-col">
<div class="walletpress-card">
<h2>Single Wallet</h2>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
<input type="hidden" name="action" value="walletpress_generate">
<?php wp_nonce_field('walletpress_gen'); ?>
<table class="form-table">
<tr><th>Chain</th><td><select name="chain"><?php if (is_array($chains)) foreach ($chains as $k => $c): ?><option value="<?php echo esc_attr($k); ?>"><?php echo esc_html($c['name'] ?? $k); ?></option><?php endforeach; else: foreach ($this->plugin->supported_chains() as $k => $c): ?><option value="<?php echo esc_attr($k); ?>"><?php echo esc_html($c['label']); ?></option><?php endforeach; endif; ?></select></td></tr>
<tr><th>Label</th><td><input type="text" name="label" class="regular-text" placeholder="e.g. My Trading Wallet"></td></tr>
</table>
<?php submit_button('Generate Wallet', 'primary'); ?>
</form>
</div>
<div class="walletpress-card">
<h2>Batch Generate</h2>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
<input type="hidden" name="action" value="walletpress_generate_batch">
<?php wp_nonce_field('walletpress_gen'); ?>
<table class="form-table">
<tr><th>Chains (comma-separated)</th><td><input type="text" name="chains" class="regular-text" placeholder="solana,ethereum,base" value="solana,ethereum,base,polygon,bsc"></td></tr>
<tr><th>Label Prefix</th><td><input type="text" name="label_prefix" class="regular-text" placeholder="batch-2026"></td></tr>
</table>
<?php submit_button('Generate Batch', 'primary'); ?>
</form>
</div>
</div>
<div class="walletpress-col">
<div class="walletpress-card">
<h2>Generate All Chains</h2>
<p>Generate a wallet for every supported chain (29+).</p>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
<input type="hidden" name="action" value="walletpress_generate_all">
<?php wp_nonce_field('walletpress_gen'); ?>
<table class="form-table">
<tr><th>Label Prefix</th><td><input type="text" name="label_prefix" class="regular-text" placeholder="full-suite"></td></tr>
</table>
<?php submit_button('Generate All Chains', 'primary'); ?>
</form>
</div>
<div class="walletpress-card">
<h2>Import Existing Wallet</h2>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
<input type="hidden" name="action" value="walletpress_import">
<?php wp_nonce_field('walletpress_gen'); ?>
<table class="form-table">
<tr><th>Chain</th><td><select name="chain"><?php if (is_array($chains)) foreach ($chains as $k => $c): ?><option value="<?php echo esc_attr($k); ?>"><?php echo esc_html($c['name'] ?? $k); ?></option><?php endforeach; endif; ?></select></td></tr>
<tr><th>Private Key</th><td><input type="password" name="private_key" class="regular-text" placeholder="Enter private key"></td></tr>
<tr><th>Label</th><td><input type="text" name="label" class="regular-text" placeholder="Imported wallet"></td></tr>
</table>
<?php submit_button('Import', 'primary'); ?>
</form>
</div>
</div>
</div>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// MNEMONIC CONVERTER
// ═══════════════════════════════════════════════════════════
public function page_mnemonic(): void {
?>
<div class="wrap">
<h1>Mnemonic Multi-Chain Converter</h1>
<div class="walletpress-card">
<p>Enter a BIP39 mnemonic phrase to derive addresses for all 29+ supported chains. Your phrase never leaves your browser it's sent directly to your self-hosted backend.</p>
<form method="post">
<table class="form-table">
<tr><th><label for="mnemonic">Mnemonic Phrase</label></th><td><textarea id="mnemonic" name="mnemonic" rows="3" class="large-text code" placeholder="word1 word2 word3 ... word12" required></textarea></td></tr>
<tr><th><label for="passphrase">Passphrase (optional)</label></th><td><input type="text" id="passphrase" name="passphrase" class="regular-text"></td></tr>
</table>
<?php submit_button('Derive All Addresses', 'primary'); ?>
</form>
</div>
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['mnemonic'])):
$result = $this->api()->from_mnemonic(sanitize_text_field($_POST['mnemonic']), sanitize_text_field($_POST['passphrase']));
if ($result): ?>
<div class="walletpress-card">
<h2>Results</h2>
<table class="wp-list-table widefat fixed striped">
<thead><tr><th>Chain</th><th>Address</th><th>Private Key</th></tr></thead>
<tbody>
<?php $wallets = $result['wallets'] ?? $result['result'] ?? $result; foreach ($wallets as $chain => $data): if (!is_array($data)) continue; ?>
<tr><td><strong><?php echo esc_html($chain); ?></strong></td>
<td><code><?php echo esc_html($data['address'] ?? $data['public_key'] ?? ''); ?></code></td>
<td><code><?php echo esc_html(substr($data['private_key'] ?? $data['wif'] ?? '', 0, 16) . '...'); ?></code></td></tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: $this->notice('Failed to convert mnemonic. Check your API connection.', 'error'); endif; endif; ?>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// RECOVERY
// ═══════════════════════════════════════════════════════════
public function page_recovery(): void {
?>
<div class="wrap">
<h1>Wallet Recovery</h1>
<div class="walletpress-card">
<p>Recover wallets from partial mnemonics, corrupted seeds, or lost keys. The recovery tool tries multiple BIP39 word combinations.</p>
<p class="description">This is resource-intensive on large partial phrases. Run during low-traffic periods.</p>
<form method="post">
<table class="form-table">
<tr><th><label for="partial">Partial Mnemonic</label></th><td><textarea id="partial" name="partial_mnemonic" rows="3" class="large-text code" placeholder="Enter known words, use ? for unknown: abandon ? ? lunar ? city vanish party uncle ? pudding" required></textarea></td></tr>
<tr><th>Chain</th><td><select name="chain"><?php foreach ($this->plugin->supported_chains() as $k => $c): ?><option value="<?php echo esc_attr($k); ?>"><?php echo esc_html($c['label']); ?></option><?php endforeach; ?></select></td></tr>
</table>
<?php submit_button('Attempt Recovery', 'primary'); ?>
</form>
</div>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// SWEEP
// ═══════════════════════════════════════════════════════════
public function page_sweep(): void {
$vault = $this->api()->list_vault();
?>
<div class="wrap">
<h1>Dust Sweep & Rotate</h1>
<div class="walletpress-cols">
<div class="walletpress-col">
<div class="walletpress-card">
<h2>Rotate + Sweep</h2>
<p>Generate a new wallet key and sweep all funds from the old wallet to the new one.</p>
<form method="post">
<table class="form-table">
<tr><th>Source Wallet</th><td><select name="wallet_id"><?php if (is_array($vault)) foreach ($vault as $id => $w): if ($id === '_meta') continue; ?><option value="<?php echo esc_attr($id); ?>"><?php echo esc_html(($w['chain'] ?? '?') . ' - ' . substr($w['address'] ?? $id, 0, 12)); ?></option><?php endforeach; endif; ?></select></td></tr>
<tr><th>Destination Address</th><td><input type="text" name="to_address" class="regular-text code" placeholder="Target wallet address" required></td></tr>
</table>
<?php submit_button('Rotate & Sweep', 'primary'); ?>
</form>
</div>
</div>
<div class="walletpress-col">
<div class="walletpress-card">
<h2>Simple Sweep</h2>
<p>Sweep funds from a vault wallet to an external address (no key rotation).</p>
<form method="post">
<table class="form-table">
<tr><th>From Wallet</th><td><select name="from_wallet_id"><?php if (is_array($vault)) foreach ($vault as $id => $w): if ($id === '_meta') continue; ?><option value="<?php echo esc_attr($id); ?>"><?php echo esc_html(($w['chain'] ?? '?') . ' - ' . substr($w['address'] ?? $id, 0, 12)); ?></option><?php endforeach; endif; ?></select></td></tr>
<tr><th>To Address</th><td><input type="text" name="to_address" class="regular-text code" placeholder="External address" required></td></tr>
</table>
<?php submit_button('Sweep', 'primary'); ?>
</form>
</div>
</div>
</div>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// ESCROW
// ═══════════════════════════════════════════════════════════
public function page_escrow(): void {
?>
<div class="wrap">
<h1>Pay-to-Release Escrow</h1>
<div class="walletpress-card">
<p>Create time-locked escrow wallets. Funds are released when conditions are met.</p>
<form method="post">
<table class="form-table">
<tr><th>Deposit Address</th><td><input type="text" name="deposit_address" class="regular-text code" placeholder="Wallet address receiving deposit" required></td></tr>
<tr><th>Amount (USD)</th><td><input type="number" name="amount" step="0.01" class="small-text" required></td></tr>
<tr><th>Platform</th><td><select name="platform"><option value="solana">Solana</option><option value="ethereum">Ethereum</option></select></td></tr>
<tr><th>Conditions (JSON)</th><td><textarea name="conditions" rows="4" class="large-text code" placeholder='{"release_after": "2026-12-31", "approved_by": ["wallet1", "wallet2"]}'></textarea></td></tr>
</table>
<?php submit_button('Create Escrow', 'primary'); ?>
</form>
</div>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// TOKEN GATES
// ═══════════════════════════════════════════════════════════
public function page_gates(): void {
if (isset($_GET['msg'])) $this->notice($_GET['msg'] === 'created' ? 'Gate created.' : 'Gate deleted.');
$gates = get_option('walletpress_gates', []);
?>
<div class="wrap">
<h1>Token Gates</h1>
<table class="wp-list-table widefat fixed striped">
<thead><tr><th>Name</th><th>Token</th><th>Chain</th><th>Min</th><th>Shortcode</th><th>Actions</th></tr></thead>
<tbody>
<?php if (empty($gates)): ?><tr><td colspan="6">No gates yet. Create one below.</td></tr>
<?php else: foreach ($gates as $id => $g): ?>
<tr><td><?php echo esc_html($g['title']); ?></td><td><code><?php echo esc_html(substr($g['token'], 0, 16) . '...'); ?></code></td><td><?php echo esc_html($g['chain']); ?></td><td><?php echo esc_html($g['min_amount']); ?></td>
<td><code>[wallet_gate id="<?php echo esc_attr($id); ?>"]</code> <button class="button button-small copy-shortcode" data-code='[wallet_gate id="<?php echo esc_attr($id); ?>"]'>Copy</button></td>
<td><a href="<?php echo wp_nonce_url(admin_url('admin.php?page=walletpress-gates&action=delete&id=' . $id), 'walletpress_delete_gate'); ?>" class="button button-small">Delete</a></td></tr>
<?php endforeach; endif; ?>
</tbody>
</table>
<form method="post" class="walletpress-card" style="margin-top:20px;">
<h2>New Gate</h2>
<?php wp_nonce_field('walletpress_save_gate'); ?>
<table class="form-table">
<tr><th>Gate Name</th><td><input type="text" name="gate_title" class="regular-text" placeholder="e.g. NFT Holders Only" required></td></tr>
<tr><th>Token Contract</th><td><input type="text" name="gate_token" class="regular-text code" placeholder="So11111111111111111111111111111111111111112" required></td></tr>
<tr><th>Chain</th><td><select name="gate_chain"><?php foreach ($this->plugin->supported_chains() as $k => $c): ?><option value="<?php echo esc_attr($k); ?>"><?php echo esc_html($c['label']); ?></option><?php endforeach; ?></select></td></tr>
<tr><th>Min Amount</th><td><input type="number" name="gate_min_amount" value="1" step="0.000001" min="0" class="small-text"></td></tr>
</table>
<?php submit_button('Create Gate', 'primary', 'save_gate'); ?>
</form>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// PAYMENTS
// ═══════════════════════════════════════════════════════════
public function page_payments(): void {
$payments = get_option('walletpress_payments_log', []);
$total = array_sum(array_column($payments, 'amount_usd'));
?>
<div class="wrap">
<h1>Payments</h1>
<div class="walletpress-stats-grid" style="margin-bottom:20px;">
<div class="walletpress-stat"><span class="stat-value">$<?php echo number_format($total, 2); ?></span><span class="stat-label">Total Revenue</span></div>
<div class="walletpress-stat"><span class="stat-value"><?php echo count($payments); ?></span><span class="stat-label">Transactions</span></div>
<div class="walletpress-stat"><span class="stat-value"><?php echo count(array_filter($payments, fn($p) => ($p['status'] ?? '') === 'confirmed')); ?></span><span class="stat-label">Confirmed</span></div>
</div>
<table class="wp-list-table widefat fixed striped">
<thead><tr><th>Date</th><th>From</th><th>Amount</th><th>Token</th><th>Status</th><th>TX</th></tr></thead>
<tbody>
<?php if (empty($payments)): ?><tr><td colspan="6">No payments yet.</td></tr>
<?php else: foreach (array_reverse($payments) as $p): ?>
<tr><td><?php echo esc_html($p['created_at'] ?? ''); ?></td><td><code><?php echo esc_html(substr($p['from'] ?? '', 0, 8) . '...'); ?></code></td><td>$<?php echo number_format($p['amount_usd'] ?? 0, 2); ?></td><td><?php echo esc_html($p['token'] ?? 'SOL'); ?></td><td><span class="walletpress-badge badge-<?php echo esc_attr($p['status'] ?? 'pending'); ?>"><?php echo esc_html($p['status'] ?? 'pending'); ?></span></td><td><code><?php echo esc_html(substr($p['tx_hash'] ?? '', 0, 12) . '...'); ?></code></td></tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// API KEYS
// ═══════════════════════════════════════════════════════════
public function page_api_keys(): void {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['create_key'])) {
check_admin_referer('walletpress_key');
$result = $this->api()->create_api_key(sanitize_text_field($_POST['key_label']), array_map('sanitize_text_field', $_POST['key_scopes'] ?? []));
if ($result && isset($result['api_key'])): $this->notice("Key created: {$result['api_key']} — save this now, it won't be shown again."); endif;
}
$keys = $this->api()->list_api_keys();
?>
<div class="wrap">
<h1>API Keys</h1>
<form method="post" class="walletpress-card">
<?php wp_nonce_field('walletpress_key'); ?>
<h2>Create New Key</h2>
<table class="form-table">
<tr><th>Label</th><td><input type="text" name="key_label" class="regular-text" placeholder="e.g. Production" required></td></tr>
<tr><th>Scopes</th><td><label><input type="checkbox" name="key_scopes[]" value="vault.read" checked> vault.read</label><br>
<label><input type="checkbox" name="key_scopes[]" value="vault.write"> vault.write</label><br>
<label><input type="checkbox" name="key_scopes[]" value="generate"> generate</label><br>
<label><input type="checkbox" name="key_scopes[]" value="admin"> admin</label><br>
<label><input type="checkbox" name="key_scopes[]" value="webhook.manage"> webhook.manage</label></td></tr>
</table>
<?php submit_button('Generate Key', 'primary', 'create_key'); ?>
</form>
<?php if (is_array($keys) && !empty($keys)): ?>
<table class="wp-list-table widefat fixed striped">
<thead><tr><th>Label</th><th>Key</th><th>Scopes</th><th>Created</th><th>Actions</th></tr></thead>
<tbody>
<?php foreach ($keys as $id => $k): ?>
<tr><td><?php echo esc_html($k['label'] ?? ''); ?></td><td><code><?php echo esc_html(substr($k['api_key'] ?? $id, 0, 16) . '...'); ?></code></td><td><?php echo esc_html(implode(', ', $k['scopes'] ?? [])); ?></td><td><?php echo esc_html($k['created_at'] ?? ''); ?></td><td><a href="<?php echo wp_nonce_url(admin_url('admin.php?page=walletpress-keys&action=revoke&id=' . $id), 'walletpress_key'); ?>" class="button button-small">Revoke</a></td></tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// WEBHOOKS
// ═══════════════════════════════════════════════════════════
public function page_webhooks(): void {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['create_webhook'])) {
check_admin_referer('walletpress_webhook');
$result = $this->api()->create_webhook(esc_url_raw($_POST['webhook_url']), array_map('sanitize_text_field', $_POST['webhook_events'] ?? []), sanitize_text_field($_POST['webhook_secret']));
if ($result) $this->notice('Webhook created.');
}
$webhooks = $this->api()->list_webhooks();
?>
<div class="wrap">
<h1>Webhooks</h1>
<form method="post" class="walletpress-card">
<?php wp_nonce_field('walletpress_webhook'); ?>
<h2>New Webhook</h2>
<table class="form-table">
<tr><th>URL</th><td><input type="url" name="webhook_url" class="regular-text" placeholder="https://hooks.example.com/events" required></td></tr>
<tr><th>Secret</th><td><input type="text" name="webhook_secret" class="regular-text" placeholder="Shared secret for HMAC verification"></td></tr>
<tr><th>Events</th><td><label><input type="checkbox" name="webhook_events[]" value="wallet.generated" checked> wallet.generated</label><br>
<label><input type="checkbox" name="webhook_events[]" value="payment.received"> payment.received</label><br>
<label><input type="checkbox" name="webhook_events[]" value="gate.accessed"> gate.accessed</label><br>
<label><input type="checkbox" name="webhook_events[]" value="alert.triggered"> alert.triggered</label></td></tr>
</table>
<?php submit_button('Create Webhook', 'primary', 'create_webhook'); ?>
</form>
<?php if (is_array($webhooks) && !empty($webhooks)): ?>
<table class="wp-list-table widefat fixed striped">
<thead><tr><th>URL</th><th>Events</th><th>Created</th><th>Actions</th></tr></thead>
<tbody>
<?php foreach ($webhooks as $id => $w): ?>
<tr><td><code><?php echo esc_html($w['url'] ?? ''); ?></code></td><td><?php echo esc_html(implode(', ', $w['events'] ?? [])); ?></td><td><?php echo esc_html($w['created_at'] ?? ''); ?></td><td><a href="<?php echo wp_nonce_url(admin_url('admin.php?page=walletpress-webhooks&action=delete&id=' . $id), 'walletpress_webhook'); ?>" class="button button-small">Delete</a></td></tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// ALERTS
// ═══════════════════════════════════════════════════════════
public function page_alerts(): void {
?>
<div class="wrap">
<h1>Balance Alerts</h1>
<div class="walletpress-card">
<p>Get notified when wallet balances change or when specific on-chain events occur.</p>
<form method="post">
<table class="form-table">
<tr><th>Wallet Address</th><td><input type="text" name="alert_address" class="regular-text code" placeholder="Address to monitor" required></td></tr>
<tr><th>Chain</th><td><select name="alert_chain"><?php foreach ($this->plugin->supported_chains() as $k => $c): ?><option value="<?php echo esc_attr($k); ?>"><?php echo esc_html($c['label']); ?></option><?php endforeach; ?></select></td></tr>
<tr><th>Trigger</th><td><select name="alert_type"><option value="balance_change">Balance Change</option><option value="incoming_tx">Incoming TX</option><option value="outgoing_tx">Outgoing TX</option><option value="token_movement">Token Movement</option></select></td></tr>
<tr><th>Notification</th><td><select name="alert_channel"><option value="email">Email</option><option value="webhook">Webhook</option><option value="telegram">Telegram</option></select></td></tr>
</table>
<?php submit_button('Create Alert', 'primary'); ?>
</form>
</div>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// AUDIT LOG
// ═══════════════════════════════════════════════════════════
public function page_audit(): void {
$audit = $this->api()->audit_trail(100);
?>
<div class="wrap">
<h1>Audit Trail</h1>
<?php if (!is_array($audit) || empty($audit)): $this->notice('No audit entries yet.', 'info'); return; endif; ?>
<table class="wp-list-table widefat fixed striped">
<thead><tr><th>Timestamp</th><th>Action</th><th>Wallet</th><th>Details</th></tr></thead>
<tbody>
<?php foreach ($audit as $entry): ?>
<tr><td><?php echo esc_html($entry['timestamp'] ?? $entry['time'] ?? ''); ?></td>
<td><code><?php echo esc_html($entry['action'] ?? $entry['event'] ?? ''); ?></code></td>
<td><code><?php echo esc_html(isset($entry['wallet_id']) ? substr($entry['wallet_id'], 0, 12) : '-'); ?></code></td>
<td><?php echo esc_html(is_string($entry['details'] ?? '') ? $entry['details'] : wp_json_encode($entry['details'] ?? [])); ?></td></tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// PLUGINS
// ═══════════════════════════════════════════════════════════
public function page_plugins(): void {
?>
<div class="wrap">
<h1>Plugin System</h1>
<p>WalletPress supports event-driven plugins that hook into wallet lifecycle events.</p>
<div class="walletpress-cols">
<div class="walletpress-col">
<div class="walletpress-card">
<h3>Available Events</h3>
<ul>
<li><code>before_generate</code> Before wallet generation</li>
<li><code>after_generate</code> After wallet generated</li>
<li><code>before_import</code> Before wallet import</li>
<li><code>after_import</code> After wallet imported</li>
<li><code>before_rotate</code> Before key rotation</li>
<li><code>after_rotate</code> After key rotated</li>
<li><code>before_sweep</code> Before fund sweep</li>
<li><code>after_sweep</code> After fund sweep</li>
<li><code>vault_change</code> Any vault modification</li>
</ul>
<p class="description">Plugins are Python files dropped in the backend's <code>/app/plugins/</code> directory. They receive event data and can transform, log, or reject operations.</p>
</div>
</div>
<div class="walletpress-col">
<div class="walletpress-card">
<h3>Create a Plugin</h3>
<pre># /app/plugins/my_plugin.py
from app.wallet_plugins import register_hook
def on_generate(context):
log = context.get("logger")
wallet = context.get("wallet", {})
log.info(f"Generated {wallet.get('chain')} wallet")
return context # or modify it
register_hook("after_generate", on_generate)</pre>
<p>See <code>wallet_plugins.py</code> in the backend for the full API.</p>
</div>
</div>
</div>
</div>
<?php
}
// ═══════════════════════════════════════════════════════════
// SETTINGS
// ═══════════════════════════════════════════════════════════
public function page_settings(): void {
?>
<div class="wrap">
<h1>WalletPress Settings</h1>
<form method="post" action="options.php">
<?php settings_fields('walletpress_settings'); ?>
<div class="walletpress-card">
<h2>API Connection</h2>
<table class="form-table">
<tr><th><label for="wau">Backend API URL</label></th><td><input type="url" id="wau" name="walletpress_api_url" value="<?php echo esc_attr(get_option('walletpress_api_url', '')); ?>" class="regular-text" placeholder="https://api.yourdomain.com"></td></tr>
<tr><th><label for="wak">API Key</label></th><td><input type="password" id="wak" name="walletpress_api_key" value="<?php echo esc_attr(get_option('walletpress_api_key', '')); ?>" class="regular-text"></td></tr>
</table>
</div>
<div class="walletpress-card">
<h2>Merchant Wallet</h2>
<table class="form-table">
<tr><th><label for="mw2">Wallet Address</label></th><td><input type="text" id="mw2" name="walletpress_merchant_wallet" value="<?php echo esc_attr(get_option('walletpress_merchant_wallet', '')); ?>" class="regular-text code"></td></tr>
<tr><th><label for="mc2">Chain</label></th><td><select id="mc2" name="walletpress_merchant_chain"><?php foreach ($this->plugin->supported_chains() as $k => $c): ?><option value="<?php echo esc_attr($k); ?>" <?php selected(get_option('walletpress_merchant_chain', 'solana'), $k); ?>><?php echo esc_html($c['label']); ?></option><?php endforeach; ?></select></td></tr>
</table>
</div>
<div class="walletpress-card">
<h2>Features</h2>
<table class="form-table">
<tr><th>Wallet Login</th><td><label><input type="checkbox" name="walletpress_login_enabled" value="1" <?php checked('1', get_option('walletpress_login_enabled', '1')); ?>> Enable wallet-based login</label></td></tr>
<tr><th>Crypto Payments</th><td><label><input type="checkbox" name="walletpress_payments_enabled" value="1" <?php checked('1', get_option('walletpress_payments_enabled', '1')); ?>> Enable crypto payments</label></td></tr>
<tr><th><label for="dc">Default Chain</label></th><td><select id="dc" name="walletpress_default_chain"><?php foreach ($this->plugin->supported_chains() as $k => $c): ?><option value="<?php echo esc_attr($k); ?>" <?php selected(get_option('walletpress_default_chain', 'solana'), $k); ?>><?php echo esc_html($c['label']); ?></option><?php endforeach; ?></select></td></tr>
</table>
</div>
<div class="walletpress-card">
<h2>User Settings</h2>
<table class="form-table">
<tr><th>Auto-Create Users</th><td><label><input type="checkbox" name="walletpress_auto_create_users" value="1" <?php checked('1', get_option('walletpress_auto_create_users', '1')); ?>> Create WP user on first wallet login</label></td></tr>
<tr><th><label for="dr">Default Role</label></th><td><select id="dr" name="walletpress_default_role"><?php foreach (wp_roles()->get_names() as $k => $v): ?><option value="<?php echo esc_attr($k); ?>" <?php selected(get_option('walletpress_default_role', 'subscriber'), $k); ?>><?php echo esc_html($v); ?></option><?php endforeach; ?></select></td></tr>
</table>
</div>
<?php submit_button('Save Settings'); ?>
</form>
</div>
<?php
}
}

View file

@ -0,0 +1,268 @@
<?php
defined('ABSPATH') || exit;
class WalletPressAPI {
private string $base_url;
private string $api_key;
private int $timeout;
public function __construct(string $base_url = '', string $api_key = '', int $timeout = 30) {
$this->base_url = untrailingslashit($base_url);
$this->api_key = $api_key;
$this->timeout = $timeout;
}
public function is_configured(): bool {
return !empty($this->base_url) && !empty($this->api_key);
}
private function url(string $path): string {
return $this->base_url . $path;
}
private function headers(): array {
$h = ['Content-Type' => 'application/json'];
if (!empty($this->api_key)) {
$h['X-API-Key'] = $this->api_key;
$h['Authorization'] = 'Bearer ' . $this->api_key;
}
return $h;
}
private function call(string $method, string $path, ?array $body = null): ?array {
$args = ['timeout' => $this->timeout, 'headers' => $this->headers()];
if ($body !== null) {
$args['body'] = wp_json_encode($body);
}
$response = $method === 'GET' ? wp_remote_get($this->url($path), $args) : wp_remote_post($this->url($path), $args);
if (is_wp_error($response)) {
$this->log('API error: ' . $response->get_error_message());
return null;
}
$code = wp_remote_retrieve_response_code($response);
$data = json_decode(wp_remote_retrieve_body($response), true);
if ($code < 200 || $code >= 300) {
$this->log("API {$code}: " . wp_remote_retrieve_body($response));
return null;
}
return $data;
}
private function log(string $msg): void {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log("[WalletPress] {$msg}");
}
}
// ─── Health ────────────────────────────────────────────
public function health(): ?array { return $this->call('GET', '/health'); }
// ─── Chain Vault (wallet factory) ──────────────────────
private const CV = '/api/v1/chain-vault';
public function list_chains(): ?array { return $this->call('GET', self::CV . '/chains'); }
public function stats(): ?array { return $this->call('GET', self::CV . '/stats'); }
public function health_score(string $wallet_id): ?array { return $this->call('GET', self::CV . "/health-score/{$wallet_id}"); }
public function healthz(): ?array { return $this->call('GET', self::CV . '/healthz'); }
// --- Generation ---
public function generate_wallet(string $chain, string $label = '', array $tags = []): ?array {
return $this->call('POST', self::CV . '/generate', ['chain' => $chain, 'label' => $label, 'tags' => $tags]);
}
public function generate_batch(array $chains, string $label_prefix = ''): ?array {
return $this->call('POST', self::CV . '/generate/batch', ['chains' => $chains, 'label_prefix' => $label_prefix]);
}
public function generate_all(): ?array { return $this->call('POST', self::CV . '/generate/batch', ['chains' => []]); }
public function from_mnemonic(string $mnemonic, string $passphrase = ''): ?array {
return $this->call('POST', self::CV . '/from-mnemonic', ['mnemonic' => $mnemonic, 'passphrase' => $passphrase]);
}
// --- Vault ---
public function list_vault(): ?array { return $this->call('GET', self::CV . '/vault'); }
public function get_wallet(string $wallet_id): ?array { return $this->call('GET', self::CV . "/vault/{$wallet_id}"); }
public function rotate_wallet(string $wallet_id): ?array {
return $this->call('POST', self::CV . '/rotate', ['wallet_id' => $wallet_id]);
}
public function export_wallets(string $format = 'json', array $ids = []): ?array {
return $this->call('POST', self::CV . '/export', ['format' => $format, 'ids' => $ids]);
}
public function rotate_sweep(string $wallet_id, string $to_address): ?array {
return $this->call('POST', self::CV . '/rotate-sweep', ['wallet_id' => $wallet_id, 'to_address' => $to_address]);
}
public function distribute_wallets(array $distributions): ?array {
return $this->call('POST', self::CV . '/distribute', ['distributions' => $distributions]);
}
// --- Wallet Tree ---
public function wallet_tree(): ?array { return $this->call('GET', self::CV . '/tree'); }
public function cluster_generate(string $chain, int $count = 10): ?array {
return $this->call('POST', self::CV . '/cluster', ['chain' => $chain, 'count' => $count]);
}
// --- Escrow ---
public function create_escrow(string $platform, string $deposit_address, array $conditions, float $amount): ?array {
return $this->call('POST', self::CV . '/escrow', [
'platform' => $platform, 'deposit_address' => $deposit_address,
'conditions' => $conditions, 'amount' => $amount,
]);
}
public function release_escrow(string $escrow_id): ?array {
return $this->call('POST', self::CV . '/escrow/release', ['escrow_id' => $escrow_id]);
}
// --- Mnemonic convert (full /derive endpoint) ---
public function derive_address(string $mnemonic, string $chain, string $path = ''): ?array {
return $this->call('POST', self::CV . '/derive-address', ['mnemonic' => $mnemonic, 'chain' => $chain, 'path' => $path]);
}
// --- Import ---
public function import_wallet(string $chain, string $private_key, string $label = ''): ?array {
return $this->call('POST', self::CV . '/import', ['chain' => $chain, 'private_key' => $private_key, 'label' => $label]);
}
// --- HD Wallet ---
public function create_hd_wallet(string $mnemonic, string $password = ''): ?array {
return $this->call('POST', self::CV . '/hd-wallet', ['mnemonic' => $mnemonic, 'password' => $password]);
}
// --- Paper Wallet ---
public function paper_wallet(string $wallet_id): ?array {
return $this->call('GET', self::CV . "/paper-wallet/{$wallet_id}");
}
// --- Audit ---
public function audit_trail(int $limit = 50): ?array {
return $this->call('GET', self::CV . '/audit-trail', ['limit' => $limit]);
}
// --- Bulk Operations ---
public function bulk_filter(array $filters): ?array {
return $this->call('POST', self::CV . '/bulk/filter', ['filters' => $filters]);
}
public function bulk_delete(array $ids): ?array {
return $this->call('POST', self::CV . '/bulk/delete', ['ids' => $ids]);
}
public function bulk_export(string $format = 'csv', array $ids = []): ?array {
return $this->call('POST', self::CV . '/bulk/export', ['format' => $format, 'ids' => $ids]);
}
// --- Validation ---
public function validate_address(string $chain, string $address): ?array {
return $this->call('GET', self::CV . "/validate/{$chain}/{$address}");
}
public function validate_all(): ?array { return $this->call('GET', self::CV . '/validate/all'); }
// --- Balances ---
public function list_balances(): ?array { return $this->call('GET', self::CV . '/balances'); }
public function balance_snapshot(): ?array { return $this->call('POST', self::CV . '/balances/snapshot'); }
public function balance_history(int $days = 30): ?array {
return $this->call('GET', self::CV . '/balances/history', ['days' => $days]);
}
// --- RPC Chains ---
public function rpc_chains(): ?array { return $this->call('GET', self::CV . '/rpc-chains'); }
// --- Cross-Chain Linking ---
public function link_proof(string $source_address, string $source_chain, string $target_chain): ?array {
return $this->call('POST', self::CV . '/link/proof', [
'source_address' => $source_address, 'source_chain' => $source_chain, 'target_chain' => $target_chain,
]);
}
public function link_verify(string $address, string $chain, string $proof): ?array {
return $this->call('POST', self::CV . '/link/verify', ['address' => $address, 'chain' => $chain, 'proof' => $proof]);
}
// --- Temporal Wallets ---
public function temporal_create(string $chain, int $ttl_seconds, string $label = ''): ?array {
return $this->call('POST', self::CV . '/temporal/create', ['chain' => $chain, 'ttl_seconds' => $ttl_seconds, 'label' => $label]);
}
public function temporal_release(string $temporal_id): ?array {
return $this->call('POST', self::CV . '/temporal/release', ['temporal_id' => $temporal_id]);
}
public function temporal_list(): ?array { return $this->call('GET', self::CV . '/temporal/list'); }
// --- Sweep ---
public function sweep_wallet(string $from_wallet_id, string $to_address): ?array {
return $this->call('POST', self::CV . '/sweep', ['from_wallet_id' => $from_wallet_id, 'to_address' => $to_address]);
}
// --- Funding Bundle ---
public function funding_bundle(string $chain, string $wallet_id, float $amount): ?array {
return $this->call('POST', self::CV . '/funding-bundle', ['chain' => $chain, 'wallet_id' => $wallet_id, 'amount' => $amount]);
}
// --- Deployer Setup ---
public function deployer_setup(string $chain, string $wallet_id): ?array {
return $this->call('POST', self::CV . '/deployer-setup', ['chain' => $chain, 'wallet_id' => $wallet_id]);
}
// --- Transaction Builder ---
public function tx_build(string $chain, string $from, string $to, float $amount, array $opts = []): ?array {
return $this->call('POST', self::CV . '/tx/build', array_merge(['chain' => $chain, 'from' => $from, 'to' => $to, 'amount' => $amount], $opts));
}
public function tx_broadcast(string $chain, string $signed_tx): ?array {
return $this->call('POST', self::CV . '/tx/broadcast', ['chain' => $chain, 'signed_tx' => $signed_tx]);
}
// --- API Keys ---
public function create_api_key(string $label, array $scopes = ['vault.read']): ?array {
return $this->call('POST', self::CV . '/api-keys', ['label' => $label, 'scopes' => $scopes]);
}
public function list_api_keys(): ?array { return $this->call('GET', self::CV . '/api-keys'); }
public function revoke_api_key(string $key_id): ?array {
return $this->call('POST', self::CV . '/api-keys/revoke', ['key_id' => $key_id]);
}
// --- Alerts ---
public function create_alert(array $config): ?array {
return $this->call('POST', self::CV . '/alerts', $config);
}
public function list_alerts(): ?array { return $this->call('GET', self::CV . '/alerts'); }
public function delete_alert(string $alert_id): ?array {
return $this->call('POST', self::CV . '/alerts/delete', ['alert_id' => $alert_id]);
}
// --- Webhooks ---
public function create_webhook(string $url, array $events = [], string $secret = ''): ?array {
return $this->call('POST', self::CV . '/webhooks', ['url' => $url, 'events' => $events, 'secret' => $secret]);
}
public function list_webhooks(): ?array { return $this->call('GET', self::CV . '/webhooks'); }
public function delete_webhook(string $webhook_id): ?array {
return $this->call('DELETE', self::CV . "/webhooks/{$webhook_id}");
}
// ─── Wallet Analysis (domain/wallet) ──────────────────
private const WA = '/api/v1/wallet';
public function get_balance(string $address, string $chain = 'solana'): ?array {
return $this->call('GET', self::WA . "/{$address}/balance", ['chain' => $chain]);
}
public function analyze_wallet(string $address, string $chain = 'solana'): ?array {
return $this->call('GET', self::WA . "/{$address}/analyze", ['chain' => $chain]);
}
public function scan_wallet(string $address, string $chain = 'solana', string $tier = 'free'): ?array {
return $this->call('POST', self::WA . '/scan', ['address' => $address, 'chain' => $chain, 'tier' => $tier]);
}
public function verify_signature(string $address, string $message, string $signature, string $chain = 'solana'): ?array {
return $this->call('POST', self::WA . '/verify', ['address' => $address, 'message' => $message, 'signature' => $signature, 'chain' => $chain]);
}
public function get_transactions(string $address, string $chain = 'solana', int $limit = 50): ?array {
return $this->call('GET', self::WA . "/{$address}/transactions", ['chain' => $chain, 'limit' => $limit]);
}
// ─── Wallet Memory (clustering) ────────────────────────
private const WM = '/api/v1/wallet-memory';
public function cluster_wallets(array $addresses, string $method = 'behavioral'): ?array {
return $this->call('POST', self::WM . '/cluster', ['addresses' => $addresses, 'method' => $method]);
}
public function resolve_entity(string $address): ?array {
return $this->call('GET', self::WM . "/resolve/{$address}");
}
public function wallet_labels(string $address): ?array {
return $this->call('GET', self::WM . "/labels/{$address}");
}
public function risk_score(string $address, string $chain = 'solana'): ?array {
return $this->call('GET', self::WM . "/risk/{$address}", ['chain' => $chain]);
}
}

View file

@ -0,0 +1,211 @@
<?php
defined('ABSPATH') || exit;
class WalletPressAuth {
private static ?WalletPress $plugin = null;
public function __construct(WalletPress $plugin) {
self::$plugin = $plugin;
add_action('wp_ajax_walletpress_disconnect', [__CLASS__, 'ajax_disconnect']);
add_action('wp_ajax_nopriv_walletpress_disconnect', [__CLASS__, 'ajax_disconnect']);
add_filter('show_admin_bar', [__CLASS__, 'maybe_show_admin_bar'], 10, 2);
}
public static function render_connect_button(): string {
if (is_user_logged_in()) {
return '';
}
ob_start();
?>
<div class="walletpress-connect">
<button class="walletpress-btn walletpress-btn-metamask" data-wallet="metamask">
<span class="walletpress-wallet-icon">🦊</span>
<?php esc_html_e('Sign in with MetaMask', 'walletpress'); ?>
</button>
<button class="walletpress-btn walletpress-btn-phantom" data-wallet="phantom">
<span class="walletpress-wallet-icon">👻</span>
<?php esc_html_e('Sign in with Phantom', 'walletpress'); ?>
</button>
<button class="walletpress-btn walletpress-btn-walletconnect" data-wallet="walletconnect">
<span class="walletpress-wallet-icon">🔗</span>
<?php esc_html_e('WalletConnect', 'walletpress'); ?>
</button>
<p class="walletpress-error" style="display:none;color:#dc3232;"></p>
<p class="walletpress-status" style="display:none;"></p>
</div>
<?php
return ob_get_clean();
}
public static function rest_nonce(): WP_REST_Response {
$nonce = wp_generate_password(24, false);
$hash = wp_hash($nonce, 'walletpress_auth');
set_transient('walletpress_nonce_' . $hash, $nonce, 300);
return new WP_REST_Response([
'nonce' => $nonce,
'hash' => $hash,
'expires_in' => 300,
], 200);
}
public static function rest_verify(WP_REST_Request $request): WP_REST_Response {
$address = sanitize_text_field($request->get_param('address'));
$signature = sanitize_text_field($request->get_param('signature'));
$message = sanitize_text_field($request->get_param('message'));
$chain = sanitize_text_field($request->get_param('chain')) ?: 'solana';
$nonce_hash = sanitize_text_field($request->get_param('nonce_hash'));
if (empty($address) || empty($signature) || empty($message) || empty($nonce_hash)) {
return new WP_REST_Response(['error' => 'Missing required fields'], 400);
}
$cached = get_transient('walletpress_nonce_' . $nonce_hash);
if (!$cached) {
return new WP_REST_Response(['error' => 'Nonce expired or invalid'], 401);
}
$expected = sprintf(
__('WalletPress Login\n\nAddress: %s\nNonce: %s\nChain: %s\n\nThis signature does not cost gas.', 'walletpress'),
$address, $cached, $chain
);
if ($message !== $expected) {
return new WP_REST_Response(['error' => 'Message mismatch'], 400);
}
$api = self::$plugin ? self::$plugin->api() : null;
if ($api && $api->is_configured()) {
$verified = $api->verify_signature($address, $message, $signature, $chain);
if ($verified === null) {
return new WP_REST_Response(['error' => 'Signature verification failed'], 401);
}
}
delete_transient('walletpress_nonce_' . $nonce_hash);
$user = self::find_user_by_wallet($address);
if (!$user && get_option('walletpress_auto_create_users', '1') === '1') {
$user_id = self::create_user($address, $chain);
$user = get_user_by('ID', $user_id);
}
if ($user) {
wp_set_current_user($user->ID);
wp_set_auth_cookie($user->ID);
update_user_meta($user->ID, 'walletpress_address', $address);
update_user_meta($user->ID, 'walletpress_chain', $chain);
update_user_meta($user->ID, 'walletpress_connected_at', current_time('mysql'));
return new WP_REST_Response([
'success' => true,
'user_id' => $user->ID,
'display_name' => $user->display_name,
'wallet' => $address,
], 200);
}
return new WP_REST_Response(['error' => 'User creation disabled and no existing user found'], 403);
}
public static function rest_me(WP_REST_Request $request): WP_REST_Response {
if (!is_user_logged_in()) {
return new WP_REST_Response(['error' => 'Not authenticated'], 401);
}
$user = wp_get_current_user();
return new WP_REST_Response([
'id' => $user->ID,
'display_name' => $user->display_name,
'email' => $user->user_email,
'wallet' => get_user_meta($user->ID, 'walletpress_address', true),
'chain' => get_user_meta($user->ID, 'walletpress_chain', true),
], 200);
}
public static function rest_balance(WP_REST_Request $request): WP_REST_Response {
$address = sanitize_text_field($request->get_param('address'));
$chain = sanitize_text_field($request->get_param('chain')) ?: 'solana';
if (empty($address)) {
return new WP_REST_Response(['error' => 'Address required'], 400);
}
$api = self::$plugin ? self::$plugin->api() : null;
if (!$api || !$api->is_configured()) {
return new WP_REST_Response(['error' => 'API not configured'], 503);
}
$balance = $api->get_balance($address, $chain);
if ($balance === null) {
return new WP_REST_Response(['error' => 'Failed to fetch balance'], 502);
}
return new WP_REST_Response($balance, 200);
}
public static function ajax_disconnect(): void {
check_ajax_referer('walletpress_nonce', 'nonce');
if (!is_user_logged_in()) {
wp_send_json_error(['error' => 'Not logged in']);
}
$user_id = get_current_user_id();
delete_user_meta($user_id, 'walletpress_address');
delete_user_meta($user_id, 'walletpress_chain');
wp_logout();
wp_send_json_success(['disconnected' => true]);
}
public static function maybe_show_admin_bar(bool $show, int $user_id): bool {
if ($show && get_user_meta($user_id, 'walletpress_address', true)) {
return true;
}
return $show;
}
private static function find_user_by_wallet(string $address): ?WP_User {
$users = get_users([
'meta_key' => 'walletpress_address',
'meta_value' => $address,
'number' => 1,
]);
return $users[0] ?? null;
}
private static function create_user(string $address, string $chain): int {
$username = 'wallet_' . substr($address, 0, 12);
$email = $username . '@walletpress.local';
$role = get_option('walletpress_default_role', 'subscriber');
$user_id = wp_insert_user([
'user_login' => $username,
'user_email' => $email,
'user_pass' => wp_generate_password(32),
'display_name' => substr($address, 0, 6) . '...' . substr($address, -4),
'role' => $role,
]);
if (is_wp_error($user_id)) {
$suffix = 1;
while (is_wp_error($user_id)) {
$username = 'wallet_' . substr($address, 0, 10) . $suffix;
$email = $username . '@walletpress.local';
$user_id = wp_insert_user([
'user_login' => $username,
'user_email' => $email,
'user_pass' => wp_generate_password(32),
'display_name' => substr($address, 0, 6) . '...' . substr($address, -4),
'role' => $role,
]);
$suffix++;
}
}
return $user_id;
}
}

View file

@ -0,0 +1,107 @@
<?php
defined('ABSPATH') || exit;
class WalletPressGate {
private static ?WalletPress $plugin = null;
public function __construct(WalletPress $plugin) {
self::$plugin = $plugin;
}
public static function render_gate(array $atts, string $content = ''): string {
$atts = shortcode_atts([
'id' => '',
'token' => '',
'chain' => '',
'min_amount' => '1',
'fallback' => '',
], $atts, 'wallet_gate');
if (!is_user_logged_in()) {
return self::fallback($atts['fallback']) ?: do_shortcode('[wallet_connect]');
}
$user = wp_get_current_user();
$wallet = get_user_meta($user->ID, 'walletpress_address', true);
$chain = $atts['chain'] ?: get_user_meta($user->ID, 'walletpress_chain', true) ?: 'solana';
if (!$wallet) {
return self::fallback($atts['fallback']) ?: do_shortcode('[wallet_connect]');
}
if (!empty($atts['id'])) {
$gates = get_option('walletpress_gates', []);
$gate = $gates[intval($atts['id'])] ?? null;
if ($gate) {
$atts['token'] = $gate['token'];
$atts['chain'] = $gate['chain'];
$atts['min_amount'] = $gate['min_amount'];
}
}
if (empty($atts['token'])) {
return $content ?: esc_html__('No token specified for gate.', 'walletpress');
}
$owns = self::check_ownership($wallet, $atts['token'], $chain, floatval($atts['min_amount']));
if ($owns) {
return do_shortcode($content);
}
return self::fallback($atts['fallback']);
}
public static function rest_verify_ownership(WP_REST_Request $request): WP_REST_Response {
$address = sanitize_text_field($request->get_param('address'));
$contract = sanitize_text_field($request->get_param('contract'));
$chain = sanitize_text_field($request->get_param('chain')) ?: 'solana';
$min_amount = floatval($request->get_param('min_amount') ?: 1);
if (empty($address) || empty($contract)) {
return new WP_REST_Response(['error' => 'address and contract required'], 400);
}
$owns = self::check_ownership($address, $contract, $chain, $min_amount);
return new WP_REST_Response([
'owns' => $owns,
'address' => $address,
'contract' => $contract,
'chain' => $chain,
'min_amount' => $min_amount,
], 200);
}
private static function check_ownership(string $address, string $contract, string $chain, float $min_amount = 1): bool {
$api = self::$plugin ? self::$plugin->api() : null;
if ($api && $api->is_configured()) {
$result = $api->check_ownership($address, $contract, $chain);
if ($result !== null) {
$balance = floatval($result['balance'] ?? $result['amount'] ?? 0);
return $balance >= $min_amount;
}
$balance = $api->get_balance($address, $chain);
if ($balance !== null) {
$tokens = $balance['tokens'] ?? $balance['data']['tokens'] ?? [];
foreach ($tokens as $token) {
$tok_addr = $token['address'] ?? $token['mint'] ?? '';
if (strtolower($tok_addr) === strtolower($contract)) {
$amount = floatval($token['amount'] ?? $token['balance'] ?? 0);
if ($amount >= $min_amount) {
return true;
}
}
}
}
}
return false;
}
private static function fallback(string $fallback): string {
if (!empty($fallback)) {
return '<div class="walletpress-gate-fallback">' . wp_kses_post($fallback) . '</div>';
}
return '<div class="walletpress-gate-fallback">' . esc_html__('You do not hold the required tokens to view this content.', 'walletpress') . '</div>';
}
}

View file

@ -0,0 +1,106 @@
<?php
defined('ABSPATH') || exit;
class WalletPressPayments {
private static ?WalletPress $plugin = null;
public function __construct(WalletPress $plugin) {
self::$plugin = $plugin;
}
public static function render_pay_button(array $atts): string {
$atts = shortcode_atts([
'amount' => '10',
'token' => 'SOL',
'chain' => 'solana',
'to' => '',
'label' => '',
'product' => '',
'success' => '',
], $atts, 'wallet_pay');
$to_address = $atts['to'] ?: get_option('walletpress_merchant_wallet', '');
if (empty($to_address)) {
return '<p class="walletpress-error">' . esc_html__('Payment not configured. Set a merchant wallet in settings.', 'walletpress') . '</p>';
}
$label = $atts['label'] ?: sprintf(__('Pay %s %s', 'walletpress'), $atts['amount'], $atts['token']);
ob_start();
?>
<div class="walletpress-payment" data-amount="<?php echo esc_attr($atts['amount']); ?>" data-token="<?php echo esc_attr($atts['token']); ?>" data-chain="<?php echo esc_attr($atts['chain']); ?>" data-to="<?php echo esc_attr($to_address); ?>" data-product="<?php echo esc_attr($atts['product']); ?>" data-success="<?php echo esc_attr($atts['success']); ?>">
<button class="walletpress-btn walletpress-btn-pay">
<?php echo esc_html($label); ?>
</button>
<div class="walletpress-payment-status" style="display:none;"></div>
<div class="walletpress-payment-qr" style="display:none;"></div>
</div>
<?php
return ob_get_clean();
}
public static function rest_create_payment(WP_REST_Request $request): WP_REST_Response {
$from = sanitize_text_field($request->get_param('from'));
$amount = floatval($request->get_param('amount'));
$token = sanitize_text_field($request->get_param('token')) ?: 'SOL';
$chain = sanitize_text_field($request->get_param('chain')) ?: 'solana';
$to = sanitize_text_field($request->get_param('to'));
if (empty($from) || empty($to)) {
return new WP_REST_Response(['error' => 'from and to required'], 400);
}
$api = self::$plugin ? self::$plugin->api() : null;
if ($api && $api->is_configured()) {
$result = $api->create_payment($from, $to, $amount, $token, $chain);
if ($result) {
return new WP_REST_Response($result, 200);
}
}
return new WP_REST_Response([
'from' => $from,
'to' => $to,
'amount' => $amount,
'token' => $token,
'chain' => $chain,
'status' => 'pending',
], 200);
}
public static function rest_verify_payment(WP_REST_Request $request): WP_REST_Response {
$tx_hash = sanitize_text_field($request->get_param('tx_hash'));
$chain = sanitize_text_field($request->get_param('chain')) ?: 'solana';
$product = sanitize_text_field($request->get_param('product'));
if (empty($tx_hash)) {
return new WP_REST_Response(['error' => 'tx_hash required'], 400);
}
$api = self::$plugin ? self::$plugin->api() : null;
if ($api && $api->is_configured()) {
$result = $api->verify_payment($tx_hash, $chain);
if ($result !== null && !empty($result['confirmed'])) {
if (!empty($product)) {
self::unlock_product($tx_hash, $product);
}
return new WP_REST_Response(array_merge($result, ['unlocked' => true]), 200);
}
}
return new WP_REST_Response(['tx_hash' => $tx_hash, 'confirmed' => false, 'status' => 'pending'], 200);
}
private static function unlock_product(string $tx_hash, string $product): void {
if (!is_user_logged_in()) {
return;
}
$user_id = get_current_user_id();
$purchases = get_user_meta($user_id, 'walletpress_purchases', true) ?: [];
if (!in_array($product, $purchases, true)) {
$purchases[] = $product;
update_user_meta($user_id, 'walletpress_purchases', $purchases);
}
}
}

View file

@ -0,0 +1,140 @@
<?php
defined('ABSPATH') || exit;
class WalletPress {
private array $modules = [];
private ?WalletPressAPI $api = null;
public function init(): void {
add_action('init', [$this, 'load_textdomain']);
add_action('init', [$this, 'register_shortcodes']);
add_action('wp_enqueue_scripts', [$this, 'enqueue_frontend']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin']);
add_action('rest_api_init', [$this, 'register_routes']);
add_filter('script_loader_tag', [$this, 'add_module_scripts'], 10, 3);
$this->api = new WalletPressAPI(
get_option('walletpress_api_url', ''),
get_option('walletpress_api_key', '')
);
$this->modules = [
'admin' => new WalletPressAdmin($this),
'auth' => new WalletPressAuth($this),
'gate' => new WalletPressGate($this),
'payments' => new WalletPressPayments($this),
];
do_action('walletpress_init', $this);
}
public function api(): WalletPressAPI { return $this->api; }
public function module(string $name): ?object { return $this->modules[$name] ?? null; }
public function load_textdomain(): void {
load_plugin_textdomain('walletpress', false, dirname(plugin_basename(WALLETPRESS_FILE)) . '/languages');
}
public function register_shortcodes(): void {
add_shortcode('wallet_connect', [WalletPressAuth::class, 'render_connect_button']);
add_shortcode('wallet_gate', [WalletPressGate::class, 'render_gate']);
add_shortcode('wallet_pay', [WalletPressPayments::class, 'render_pay_button']);
add_shortcode('wallet_profile', [$this, 'render_profile']);
add_shortcode('wallet_generate', [$this, 'render_generate_shortcode']);
}
public function render_generate_shortcode(): string {
if (!current_user_can('manage_options')) return '';
ob_start(); ?>
<div class="walletpress-generate-shortcode">
<form class="wp-generate-form">
<select name="chain"><?php foreach ($this->supported_chains() as $k => $c): ?>
<option value="<?php echo esc_attr($k); ?>"><?php echo esc_html($c['label']); ?></option>
<?php endforeach; ?></select>
<input type="text" name="label" placeholder="Label (optional)">
<button type="submit" class="walletpress-btn walletpress-btn-pay">Generate</button>
</form>
<pre class="wp-gen-result" style="display:none;"></pre>
</div>
<?php
return ob_get_clean();
}
public function register_routes(): void {
$c = 'WalletPressAuth';
register_rest_route('walletpress/v1', '/auth/nonce', ['methods' => 'GET', 'callback' => [$c, 'rest_nonce'], 'permission_callback' => '__return_true']);
register_rest_route('walletpress/v1', '/auth/verify', ['methods' => 'POST', 'callback' => [$c, 'rest_verify'], 'permission_callback' => '__return_true']);
register_rest_route('walletpress/v1', '/auth/me', ['methods' => 'GET', 'callback' => [$c, 'rest_me'], 'permission_callback' => '__return_true']);
register_rest_route('walletpress/v1', '/generate', ['methods' => 'POST', 'callback' => [$this, 'rest_generate'], 'permission_callback' => function() { return current_user_can('manage_options'); }]);
register_rest_route('walletpress/v1', '/vault', ['methods' => 'GET', 'callback' => [$this, 'rest_vault'], 'permission_callback' => function() { return current_user_can('manage_options'); }]);
}
public function rest_generate(WP_REST_Request $req): WP_REST_Response {
$result = $this->api->generate_wallet(sanitize_text_field($req->get_param('chain')), sanitize_text_field($req->get_param('label')));
return new WP_REST_Response($result ?: ['error' => 'Generation failed'], $result ? 200 : 502);
}
public function rest_vault(): WP_REST_Response {
$vault = $this->api->list_vault();
return new WP_REST_Response($vault ?: ['error' => 'Failed to fetch vault'], $vault ? 200 : 502);
}
public function enqueue_frontend(): void {
wp_enqueue_script('walletpress', WALLETPRESS_URL . 'assets/js/walletpress.js', [], WALLETPRESS_VERSION, true);
wp_enqueue_style('walletpress', WALLETPRESS_URL . 'assets/css/walletpress.css', [], WALLETPRESS_VERSION);
$user = wp_get_current_user();
wp_localize_script('walletpress', 'walletpress', [
'rest' => esc_url_raw(rest_url('walletpress/v1')),
'nonce' => wp_create_nonce('wp_rest'),
'api_url' => get_option('walletpress_api_url', ''),
'user' => $user->ID ? ['id' => $user->ID, 'display_name' => $user->display_name, 'wallet' => get_user_meta($user->ID, 'walletpress_address', true) ?: null] : null,
'chains' => $this->supported_chains(),
]);
}
public function enqueue_admin(string $hook): void {
if (str_contains($hook, 'walletpress')) {
wp_enqueue_style('walletpress-admin', WALLETPRESS_URL . 'assets/css/walletpress.css', [], WALLETPRESS_VERSION);
wp_enqueue_script('walletpress-admin', WALLETPRESS_URL . 'assets/js/walletpress-admin.js', [], WALLETPRESS_VERSION, true);
wp_localize_script('walletpress-admin', 'walletpress_admin', [
'nonce' => wp_create_nonce('walletpress_admin'),
'ajax' => admin_url('admin-ajax.php'),
]);
}
}
public function add_module_scripts(string $tag, string $handle, string $src): string {
return ($handle === 'walletpress') ? str_replace('<script', '<script type="module"', $tag) : $tag;
}
public function supported_chains(): array {
return [
'solana' => ['label' => 'Solana', 'icon' => 'sol', 'wallet' => 'phantom'],
'ethereum' => ['label' => 'Ethereum', 'icon' => 'eth', 'wallet' => 'metamask'],
'base' => ['label' => 'Base', 'icon' => 'base', 'wallet' => 'metamask'],
'polygon' => ['label' => 'Polygon', 'icon' => 'matic', 'wallet' => 'metamask'],
'bsc' => ['label' => 'BNB Chain', 'icon' => 'bnb', 'wallet' => 'metamask'],
'arbitrum' => ['label' => 'Arbitrum', 'icon' => 'arb', 'wallet' => 'metamask'],
'optimism' => ['label' => 'Optimism', 'icon' => 'op', 'wallet' => 'metamask'],
'avalanche' => ['label' => 'Avalanche', 'icon' => 'avax', 'wallet' => 'metamask'],
'bitcoin' => ['label' => 'Bitcoin', 'icon' => 'btc', 'wallet' => ''],
'tron' => ['label' => 'Tron', 'icon' => 'trx', 'wallet' => ''],
];
}
public function render_profile(): string {
if (!is_user_logged_in()) return do_shortcode('[wallet_connect]');
$user = wp_get_current_user();
$wallet = get_user_meta($user->ID, 'walletpress_address', true);
ob_start(); ?>
<div class="walletpress-profile">
<h3><?php esc_html_e('Wallet Profile', 'walletpress'); ?></h3>
<p><strong><?php esc_html_e('User:', 'walletpress'); ?></strong> <?php echo esc_html($user->display_name); ?></p>
<?php if ($wallet): ?>
<p><strong><?php esc_html_e('Wallet:', 'walletpress'); ?></strong> <code><?php echo esc_html(substr($wallet, 0, 6) . '...' . substr($wallet, -4)); ?></code></p>
<button class="walletpress-disconnect button"><?php esc_html_e('Disconnect Wallet', 'walletpress'); ?></button>
<?php else: echo do_shortcode('[wallet_connect]'); endif; ?>
</div>
<?php return ob_get_clean();
}
}

107
wp-plugin/readme.txt Normal file
View file

@ -0,0 +1,107 @@
=== WalletPress ===
Contributors: rugmunch
Tags: crypto, wallet, blockchain, token-gate, web3, metamask, phantom, solana, ethereum, nft, defi
Requires at least: 6.0
Tested up to: 6.7
Stable tag: 1.0.0-beta
Requires PHP: 8.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Self-hosted wallet management platform. Generate, vault, analyze, and gate content with crypto wallets across 29+ blockchains.
== Description ==
WalletPress is a self-hosted wallet management platform that brings the full power of your multi-chain crypto backend into WordPress. It is a **developer product first** — you deploy the backend, configure the plugin, and unlock enterprise-grade wallet infrastructure with a clean admin UI.
= Features =
* **Wallet Vault** — Browse, export, and manage HD wallets across 29+ chains (Solana, Ethereum, Base, Polygon, BSC, Arbitrum, Avalanche, Optimism, zkSync, Scroll, Blast, Mantle, Linea, opBNB, Manta, Polygon zkEVM, StarkNet, Sui, Aptos, and more)
* **Wallet Generation** — Generate single wallets, batch wallets, or full-chain sweeps with BIP39, BIP32, BIP44, BIP49, BIP84, BIP86 derivation
* **Wallet Analysis** — Check balances, analyze transaction history, scan for risk, and verify signatures
* **Mnemonic Converter** — Convert a BIP39 mnemonic phrase to addresses on all supported chains instantly
* **Token Gating** — Lock any content behind token/NFT ownership with simple shortcodes `[wallet_gate id="1"]`
* **Crypto Payments** — Accept SOL, ETH, MATIC, BNB, and ERC-20/BEP-20/SPL tokens. Users connect their wallet and pay with one click
* **Wallet Login** — MetaMask EIP-1193 and Phantom wallet Connect button with nonce-based signature verification. Auto-creates WP users
* **RBAC** — Role-based API key permissions with granular scopes
* **API Keys** — Generate, list, rotate, and revoke API keys with scope-limited access
* **Webhooks** — Create webhooks for wallet.generated, wallet.swept, payment.received, gate.verified events
* **Audit Trail** — Full audit log of all wallet operations
* **Balance Snapshots** — Periodic balance tracking with history viewer
* **Sweep & Rotate** — Sweep funds to a destination address, rotate wallet keys, or create temporal (TTL-expiring) wallets
* **Paper Wallets** — Generate paper wallet PDFs with QR codes
* **Admin Dashboard** — 15 admin pages covering every feature
= Supported Chains =
Solana, Ethereum, Base, Polygon, BSC, Arbitrum, Avalanche, Optimism, zkSync, Scroll, Blast, Mantle, Linea, opBNB, Manta, Polygon zkEVM, StarkNet, Sui, Aptos, Neon, Near, Aurora, Celo, Moonbeam, Moonriver, Fantom, Cronos, Gnosis, Telos
== Installation ==
1. Upload the `walletpress` folder to `/wp-content/plugins/`
2. Activate the plugin through the Plugins screen
3. Go to **WalletPress → Setup** in your WP admin
4. Enter your backend API URL and API key
5. Configure your merchant wallet address and default chain
6. Start generating wallets, creating token gates, and accepting payments
= Backend Requirements =
WalletPress requires a running instance of the `rmi-backend` API. See the Deployment Guide at `docs/DEPLOYMENT.md` for setup instructions. The backend provides:
* REST API at `/api/v1/chain-vault` and `/api/v1/wallet`
* 86+ endpoints covering all wallet operations
* Postgres + Redis + ClickHouse storage
* Optional: Reth (archive node), Neo4j (entity graphs), Qdrant (vector memory)
== Frequently Asked Questions ==
= Do I need a third-party service? =
No. WalletPress is self-hosted. You deploy the backend on your own infrastructure. No monthly SaaS fees, no API usage limits.
= How do users connect their wallets? =
Frontend includes MetaMask (EIP-1193) and Phantom connectors. The Connect button generates a nonce, the user signs it with their wallet, and the server verifies the signature. This creates/authenticates a WP user account.
= What happens to existing users? =
Existing WP users can link a wallet address in their profile. Wallet-authenticated users get auto-registered as WP users with the role you configure.
= Can I sell access to content? =
Yes. The Token Gating shortcode locks content behind token/NFT ownership. Combined with Crypto Payments, users can purchase access and automatically unlock gated content.
= Is the plugin compatible with other page builders? =
Yes. The `[wallet_gate]` shortcode works in Classic Editor, Gutenberg, Elementor, WPBakery, and any builder that supports shortcodes.
== Screenshots ==
1. WalletPress Admin Dashboard — overview of vault, gates, payments, and system health
2. Wallet Vault — browse all wallets across chains with search and filter
3. Wallet Generation — generate single or batch wallets with label/chain options
4. Token Gates — create and manage token/NFT gating rules
5. Crypto Payments — configure payment form and view transaction history
6. Settings — API connection, merchant wallet, chain defaults
== Changelog ==
= 1.0.0-beta =
* Initial beta release
* Wallet vault with 29+ chains
* Wallet generation (single, batch, full sweep)
* Mnemonic to addresses converter
* Token gating with shortcode
* Crypto payments with wallet connection
* MetaMask/Phantom wallet login
* API key management with scopes
* Webhook creation and management
* Audit trail viewer
* Balance snapshots and history
* 15 admin pages
== Upgrade Notice ==
= 1.0.0-beta =
First beta release. Test thoroughly before production use.

38
wp-plugin/walletpress.php Normal file
View file

@ -0,0 +1,38 @@
<?php
/**
* Plugin Name: WalletPress
* Plugin URI: https://walletpress.cc
* Description: Self-hosted wallet management platform. Generate, vault, analyze, distribute, and gate with crypto wallets across 29+ chains. REST API + WP Admin + CLI.
* Version: 1.0.0-beta
* Author: Rug Munch Media LLC
* Author URI: https://rugmunch.io
* License: GPL v2 or later
* Text Domain: walletpress
* Domain Path: /languages
* Requires PHP: 8.1
* Requires WP: 6.0
*/
defined('ABSPATH') || exit;
define('WALLETPRESS_VERSION', '1.0.0-beta');
define('WALLETPRESS_FILE', __FILE__);
define('WALLETPRESS_DIR', plugin_dir_path(__FILE__));
define('WALLETPRESS_URL', plugin_dir_url(__FILE__));
require_once WALLETPRESS_DIR . 'includes/class-walletpress.php';
require_once WALLETPRESS_DIR . 'includes/class-walletpress-admin.php';
require_once WALLETPRESS_DIR . 'includes/class-walletpress-api.php';
require_once WALLETPRESS_DIR . 'includes/class-walletpress-auth.php';
require_once WALLETPRESS_DIR . 'includes/class-walletpress-gate.php';
require_once WALLETPRESS_DIR . 'includes/class-walletpress-payments.php';
function walletpress(): WalletPress {
static $instance = null;
if ($instance === null) {
$instance = new WalletPress();
}
return $instance;
}
walletpress()->init();