QuantumPay Wallet
https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js
:root {
–bg-dark: #0b0e14;
–card-bg: rgba(23, 28, 41, 0.7);
–primary: #00f3ff; /* Cyan */
–secondary: #bd00ff; /* Purple */
–text-main: #ffffff;
–text-muted: #94a3b8;
–success: #00ff9d;
–danger: #ff4757;
–glass-border: 1px solid rgba(255, 255, 255, 0.1);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: ‘Segoe UI’, Roboto, Helvetica, Arial, sans-serif;
}
body {
background-color: var(–bg-dark);
background-image:
radial-gradient(circle at 10% 20%, rgba(189, 0, 255, 0.15) 0%, transparent 40%),
radial-gradient(circle at 90% 80%, rgba(0, 243, 255, 0.1) 0%, transparent 40%);
color: var(–text-main);
min-height: 100vh;
display: flex;
justify-content: center;
padding: 20px;
}
.app-container {
width: 100%;
max-width: 480px;
display: flex;
flex-direction: column;
gap: 20px;
}
/* — Header — */
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
background: linear-gradient(90deg, var(–primary), var(–secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
letter-spacing: 1px;
}
/* — Cards — */
.card {
background: var(–card-bg);
backdrop-filter: blur(10px);
border: var(–glass-border);
border-radius: 16px;
padding: 20px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
transition: transform 0.2s;
}
/* — Balance Section — */
.balance-card {
text-align: center;
position: relative;
overflow: hidden;
}
.balance-card::before {
content: ”;
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: conic-gradient(transparent, rgba(0, 243, 255, 0.1), transparent 30%);
animation: rotate 10s linear infinite;
pointer-events: none;
}
@keyframes rotate {
100% { transform: rotate(360deg); }
}
.token-amount {
font-size: 2.5rem;
font-weight: 700;
margin: 10px 0;
}
.token-symbol {
color: var(–primary);
font-size: 1rem;
}
.address-pill {
background: rgba(0, 0, 0, 0.3);
padding: 8px 16px;
border-radius: 20px;
font-family: monospace;
font-size: 0.9rem;
color: var(–text-muted);
display: inline-flex;
align-items: center;
gap: 8px;
cursor: pointer;
border: 1px solid transparent;
transition: all 0.2s;
}
.address-pill:hover {
border-color: var(–primary);
color: var(–primary);
}
/* — Action Tabs — */
.tabs {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.tab-btn {
flex: 1;
background: transparent;
border: var(–glass-border);
color: var(–text-muted);
padding: 10px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
}
.tab-btn.active {
background: rgba(0, 243, 255, 0.1);
border-color: var(–primary);
color: var(–text-main);
}
.section {
display: none;
animation: fadeIn 0.3s ease;
}
.section.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(5px); }
to { opacity: 1; transform: translateY(0); }
}
/* — Forms — */
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 8px;
color: var(–text-muted);
font-size: 0.9rem;
}
input {
width: 100%;
background: rgba(0, 0, 0, 0.3);
border: var(–glass-border);
padding: 12px;
border-radius: 8px;
color: #fff;
outline: none;
transition: border-color 0.3s;
}
input:focus {
border-color: var(–secondary);
}
button.action-btn {
width: 100%;
background: linear-gradient(90deg, var(–secondary), var(–primary));
border: none;
padding: 14px;
border-radius: 8px;
color: #000;
font-weight: 700;
cursor: pointer;
transition: opacity 0.3s;
}
button.action-btn:hover {
opacity: 0.9;
}
/* — Receive Section — */
.qr-container {
background: #fff;
padding: 15px;
border-radius: 12px;
width: fit-content;
margin: 0 auto 20px auto;
}
/* — History — */
.history-list {
display: flex;
flex-direction: column;
gap: 10px;
max-height: 300px;
overflow-y: auto;
}
.history-item {
background: rgba(255, 255, 255, 0.03);
padding: 12px;
border-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.tx-info {
display: flex;
flex-direction: column;
gap: 4px;
}
.tx-type {
font-size: 0.85rem;
font-weight: 600;
}
.tx-date {
font-size: 0.75rem;
color: var(–text-muted);
}
.tx-amount {
font-weight: 700;
}
.tx-amount.plus { color: var(–success); }
.tx-amount.minus { color: var(–danger); }
/* — Settings — */
.settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.settings-btn {
background: rgba(255, 255, 255, 0.05);
border: var(–glass-border);
color: var(–text-muted);
padding: 12px;
border-radius: 8px;
cursor: pointer;
text-align: center;
}
.settings-btn:hover {
background: rgba(255, 255, 255, 0.1);
}
/* — Notifications — */
#notification {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: var(–card-bg);
border: var(–glass-border);
padding: 10px 20px;
border-radius: 30px;
display: none;
z-index: 100;
backdrop-filter: blur(10px);
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
}
Total Balance
0.00
QSAM
Loading…
Send QSAM
Recipient Address
Amount
Receive QSAM
Your Wallet Address
IBM Quantum Rewards
Submit your completed workload ID to verify computational contribution and receive QSAM tokens.
Workload ID / Job ID
Wallet Management
Import JSON
Action Successful
// — State Management —
const STORAGE_KEY = ‘quantumpay_wallet_v1’;
let wallet = {
address: ”,
balance: 5000,
transactions: [], // { type: ‘send’|’receive’|’reward’, amount, to/from, date, id }
claimedWorkloads: []
};
// — Initialization —
document.addEventListener(‘DOMContentLoaded’, () => {
loadWallet();
updateUI();
});
function loadWallet() {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
wallet = JSON.parse(stored);
} else {
createWallet();
}
}
function createWallet() {
// Generate a random hex address simulating Ethereum/Quantum style
const randomBytes = new Uint8Array(20);
window.crypto.getRandomValues(randomBytes);
const address = ‘0x’ + Array.from(randomBytes).map(b => b.toString(16).padStart(2, ‘0’)).join(”);
wallet = {
address: address,
balance: 5000,
transactions: [{
type: ‘system’,
amount: 5000,
label: ‘Initial Grant’,
date: new Date().toISOString(),
id: generateId()
}],
claimedWorkloads: []
};
saveWallet();
}
function saveWallet() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(wallet));
updateUI();
}
function generateId() {
return Math.random().toString(36).substr(2, 9);
}
// — UI Updates —
function updateUI() {
// Balance
document.getElementById(‘balance-display’).innerText = wallet.balance.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
// Address
const shortAddr = wallet.address.substring(0, 6) + ‘…’ + wallet.address.substring(wallet.address.length – 4);
document.getElementById(‘short-address’).innerText = shortAddr;
document.getElementById(‘full-address’).value = wallet.address;
// QR Code
const qrContainer = document.getElementById(‘qrcode’);
qrContainer.innerHTML = ”; // Clear previous
new QRCode(qrContainer, {
text: wallet.address,
width: 128,
height: 128,
colorDark : “#000000”,
colorLight : “#ffffff”,
correctLevel : QRCode.CorrectLevel.H
});
// History
renderHistory();
}
function renderHistory() {
const list = document.getElementById(‘history-list’);
list.innerHTML = ”;
if (wallet.transactions.length === 0) {
list.innerHTML = ‘
No transactions yet
‘;
return;
}
// Sort by date desc
const sortedTx = […wallet.transactions].sort((a, b) => new Date(b.date) – new Date(a.date));
sortedTx.forEach(tx => {
const isPlus = [‘receive’, ‘reward’, ‘system’].includes(tx.type);
const colorClass = isPlus ? ‘plus’ : ‘minus’;
const sign = isPlus ? ‘+’ : ‘-‘;
const icon = getIconForType(tx.type);
const dateObj = new Date(tx.date);
const dateStr = dateObj.toLocaleDateString() + ‘ ‘ + dateObj.toLocaleTimeString([], {hour: ‘2-digit’, minute:’2-digit’});
const html = `
${capitalize(tx.type)}
${dateStr}
${sign}${tx.amount} QSAM
`;
list.innerHTML += html;
});
}
function getIconForType(type) {
switch(type) {
case ‘send’: return ‘fa-solid fa-arrow-up’;
case ‘receive’: return ‘fa-solid fa-arrow-down’;
case ‘reward’: return ‘fa-solid fa-microchip’;
case ‘system’: return ‘fa-solid fa-gift’;
default: return ‘fa-solid fa-circle’;
}
}
// — User Actions —
function sendTransaction() {
const addrInput = document.getElementById(‘send-address’);
const amtInput = document.getElementById(‘send-amount’);
const amount = parseFloat(amtInput.value);
if (!addrInput.value.startsWith(‘0x’) || addrInput.value.length < 10) {
showNotify("Invalid address format", true);
return;
}
if (isNaN(amount) || amount wallet.balance) {
showNotify(“Insufficient funds”, true);
return;
}
// Execute Send
wallet.balance -= amount;
wallet.transactions.unshift({
type: ‘send’,
amount: amount,
to: addrInput.value,
date: new Date().toISOString(),
id: generateId()
});
saveWallet();
addrInput.value = ”;
amtInput.value = ”;
showNotify(`Sent ${amount} QSAM!`);
// Simulate visual loading/mining
document.querySelector(‘.balance-card’).style.opacity = ‘0.7’;
setTimeout(() => document.querySelector(‘.balance-card’).style.opacity = ‘1’, 300);
}
function submitWorkload() {
const input = document.getElementById(‘workload-id’);
const val = input.value.trim();
if (val.length {
const reward = Math.random() > 0.5 ? 20 : 10;
wallet.balance += reward;
wallet.claimedWorkloads.push(val);
wallet.transactions.unshift({
type: ‘reward’,
amount: reward,
label: ‘IBM Quantum Workload’,
date: new Date().toISOString(),
id: generateId()
});
saveWallet();
input.value = ”;
btn.innerText = originalText;
btn.disabled = false;
showNotify(`Verified! Received ${reward} QSAM`);
}, 1500);
}
// — Helper Functions —
function showTab(tabId) {
document.querySelectorAll(‘.section’).forEach(el => el.classList.remove(‘active’));
document.querySelectorAll(‘.tab-btn’).forEach(el => el.classList.remove(‘active’));
document.getElementById(tabId).classList.add(‘active’);
// Find button that triggers this
const btns = document.getElementsByClassName(‘tab-btn’);
for(let btn of btns) {
if(btn.onclick.toString().includes(tabId)) {
btn.classList.add(‘active’);
}
}
}
function copyAddress() {
navigator.clipboard.writeText(wallet.address).then(() => {
showNotify(“Address Copied!”);
});
}
function showNotify(msg, isError = false) {
const el = document.getElementById(‘notification’);
el.innerText = msg;
el.style.border = isError ? ‘1px solid #ff4757’ : ‘1px solid #00ff9d’;
el.style.color = isError ? ‘#ff4757’ : ‘#00ff9d’;
el.style.display = ‘block’;
setTimeout(() => {
el.style.display = ‘none’;
}, 3000);
}
function capitalize(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
}
// — Import / Export —
function exportWallet() {
const dataStr = “data:text/json;charset=utf-8,” + encodeURIComponent(JSON.stringify(wallet));
const downloadAnchorNode = document.createElement(‘a’);
downloadAnchorNode.setAttribute(“href”, dataStr);
downloadAnchorNode.setAttribute(“download”, “quantumpay_wallet.json”);
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
function importWallet(input) {
const file = input.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const json = JSON.parse(e.target.result);
if (json.address && json.balance !== undefined && Array.isArray(json.transactions)) {
wallet = json;
saveWallet();
showNotify(“Wallet imported successfully”);
input.value = ”; // Reset input
} else {
throw new Error(“Invalid structure”);
}
} catch (err) {
showNotify(“Invalid Wallet File”, true);
}
};
reader.readAsText(file);
}
function resetWallet() {
if(confirm(“Are you sure? This will delete your current wallet and keys permanently.”)) {
localStorage.removeItem(STORAGE_KEY);
location.reload();
}
}