Having KeyChain Problems

avatar
(Edited)


I've been hard at trying to make money off of money. I'm stuck on some JavaScript keychain interactions. I've been at it for days, and I'm burnt out. Any help would be appreciated.

Thinking 'bouts once I figure out this keychain operation to incorporate PIMP @enginewitty and maybe some tokens of @ecoinstant

Please be my guest and try https://geocities.ws/peakecoin/pekbet/ and let me know whats working and what isn't.

I want to make some improvements. If you have any input @thecrazygm I just want it to be able to log in.

// PeakeCoin Betting JavaScript with Hive Keychain Integration 
class PeakeCoinBetting {
  constructor() {
    this.apiUrl = 'http://server';
    this.currentUser = null;
    this.userBalance = 0;
    this.selectedChoice = null;
    this.keychainAvailable = false;
    this._lastKCText = '';
    this.tokenPrecision = 3; // set from backend if available

    this.init();
  }

  init() {
    this.setupEventListeners();
    this.waitForKeychain();
    this.loadStoredUser();
  }

  // ---------- Keychain detection ----------
  waitForKeychain() {
    this.updateKeychainStatus('Checking...', 'disconnected');

    let attempts = 0;
    const maxAttempts = 30;
    const checkInterval = 500;

    const checkKeychain = () => {
      attempts++;

      const keychainExists = (
        typeof window.hive_keychain !== 'undefined' &&
        typeof window.hive_keychain.requestSendToken === 'function'
      );

      if (keychainExists) {
        this.keychainAvailable = true;
        this.updateKeychainStatus('Detected', 'connected');
        this.showNotification('Hive Keychain detected successfully!', 'success');
        this.testKeychainFunctionality();
      } else if (attempts < maxAttempts) {
        this.updateKeychainStatus(`Searching... (${attempts}/${maxAttempts})`, 'disconnected');
        setTimeout(checkKeychain, checkInterval);
      } else {
        this.keychainAvailable = false;
        this.updateKeychainStatus('Not Found', 'disconnected');
        this.showKeychainInstallationModal();
      }
    };

    checkKeychain();
  }

  testKeychainFunctionality() {
    try {
      if (typeof window.hive_keychain?.requestSendToken === 'function') {
        this.showNotification('Keychain is ready for transactions!', 'success');
      } else {
        throw new Error('requestSendToken function not available');
      }
    } catch (e) {
      this.showNotification('Keychain detected but may not be functional. Try refreshing.', 'warning');
    }
  }

  // ---------- UI helpers ----------
  updateKeychainStatus(status, statusClass) {
    if (status === this._lastKCText) return; // debounce repeated writes
    this._lastKCText = status;

    const statusElement = document.getElementById('keychainStatusText');
    const containerElement = document.getElementById('keychainStatus');
    if (statusElement) statusElement.textContent = status;
    if (containerElement) containerElement.className = `keychain-status ${statusClass}`;
  }

  showModal(title, content) {
    const modal = document.createElement('div');
    modal.style.cssText = `
      position: fixed; inset: 0; background: rgba(0,0,0,.7); z-index: 10000;
      display: flex; align-items: center; justify-content: center; padding: 20px;
    `;
    const contentDiv = document.createElement('div');
    contentDiv.style.cssText = `
      background: #fff; color: #333; padding: 30px; border-radius: 14px;
      max-width: 800px; max-height: 80vh; overflow-y: auto; box-shadow: 0 10px 30px rgba(0,0,0,.3);
    `;
    contentDiv.innerHTML = `
      <h2 style="margin-top:0">${title}</h2>
      ${content}
      <div style="text-align:center; margin-top: 24px;">
        <button style="background:#e53e3e;color:#fff;border:none;padding:10px 18px;border-radius:8px;cursor:pointer;margin-right:8px"
                onclick="this.closest('div[style*=&quot;position: fixed&quot;]').remove()">Close</button>
        <button style="background:#38a169;color:#fff;border:none;padding:10px 18px;border-radius:8px;cursor:pointer"
                onclick="window.bettingApp.waitForKeychain(); this.closest('div[style*=&quot;position: fixed&quot;]').remove()">Try Again</button>
      </div>
    `;
    modal.appendChild(contentDiv);
    document.body.appendChild(modal);
  }

  showKeychainInstallationModal() {
    const html = `
      <div style="text-align:center;max-width:560px">
        <h3>🔐 Hive Keychain Required</h3>
        <p>Install the Hive Keychain browser extension to place PEK bets.</p>
        <ol style="text-align:left">
          <li>Install the extension (link below)</li>
          <li>Refresh this page</li>
          <li>Import your Hive account keys into Keychain</li>
        </ol>
        <div style="margin:16px 0">
          <a href="https://chrome.google.com/webstore/detail/hive-keychain/jcacnejopjdphbnjgfaaobbfafkihpep" target="_blank"
             style="display:inline-block;background:#4285f4;color:#fff;padding:10px 16px;border-radius:8px;margin:4px;text-decoration:none">📥 Chrome</a>
          <a href="https://addons.mozilla.org/en-US/firefox/addon/hive-keychain/" target="_blank"
             style="display:inline-block;background:#ff9500;color:#fff;padding:10px 16px;border-radius:8px;margin:4px;text-decoration:none">🦊 Firefox</a>
        </div>
        <small>You can view balances/history without Keychain, but you need it to bet.</small>
      </div>
    `;
    this.showModal('Keychain Installation Required', html);
  }

  showNotification(message, type = 'info') {
    const box = document.createElement('div');
    box.className = `notification ${type} fade-in`;
    Object.assign(box.style, {
      position: 'fixed', top: '20px', right: '20px', padding: '12px 16px',
      borderRadius: '8px', color: '#fff', zIndex: 1000, fontWeight: 600,
      boxShadow: '0 4px 12px rgba(0,0,0,.15)', maxWidth: '420px'
    });
    box.style.backgroundColor = { success:'#38a169', error:'#e53e3e', info:'#3182ce', warning:'#d69e2e' }[type] || '#3182ce';
    box.textContent = message;
    document.body.appendChild(box);
    setTimeout(() => { box.style.opacity = '0'; setTimeout(() => box.remove(), 300); }, 4000);
  }

  setLoadingState(loading) {
    const btn = document.getElementById('connectBtn');
    if (!btn) return;
    btn.textContent = loading ? 'Connecting...' : 'Connect with Keychain';
    btn.disabled = !!loading;
  }

  // ---------- Events ----------
  setupEventListeners() {
    const byId = id => document.getElementById(id);

    byId('connectBtn')?.addEventListener('click', () => this.connectWithKeychain());
    byId('refreshKeychainBtn')?.addEventListener('click', () => this.waitForKeychain());
    byId('debugKeychainBtn')?.addEventListener('click', () => this.debugKeychain());
    byId('testKeychainBtn')?.addEventListener('click', () => this.performKeychainTest());

    document.querySelectorAll('.choice-btn').forEach(btn => {
      btn.addEventListener('click', (e) => this.selectChoice(e.currentTarget));
    });

    byId('usernameInput')?.addEventListener('keypress', (e) => {
      if (e.key === 'Enter') this.connectWithKeychain();
    });
  }

  // ---------- Auth ----------
  async connectWithKeychain() {
    if (typeof window.hive_keychain === 'undefined') {
      this.showNotification('Hive Keychain not found. Install and refresh.', 'error');
      return;
    }
    const username = (document.getElementById('usernameInput')?.value || '').trim();
    if (!/^[a-z0-9.-]{3,16}$/.test(username)) {
      this.showNotification('Enter a valid Hive username (lowercase, 3–16 chars).', 'error');
      return;
    }
    try {
      this.setLoadingState(true);
      await this.authenticateWithKeychain(username);
    } catch (e) {
      this.showNotification(`Connection failed: ${e.message}`, 'error');
    } finally {
      this.setLoadingState(false);
    }
  }

  authenticateWithKeychain(username) {
    return new Promise((resolve, reject) => {
      window.hive_keychain.requestVerifyKey(
        username,
        'PeakeCoin betting auth',
        'Posting',
        async (res) => {
          if (res?.success) {
            await this.handleSuccessfulSignIn(username, res);
            resolve(res);
          } else {
            reject(new Error(res?.message || res?.error || 'Authentication failed'));
          }
        }
      );
    });
  }

  async handleSuccessfulSignIn(username) {
    try {
      const balance = await this.fetchUserBalance(username);
      if (balance === null || balance === undefined) throw new Error('Could not fetch PEK balance');
      this.currentUser = username;
      this.userBalance = balance;
      this.keychainAvailable = typeof window.hive_keychain !== 'undefined';

      localStorage.setItem('peakecoin_user', username);
      this.updateBalanceDisplay(balance);
      this.showConnectionStatus(username);
      this.updateKeychainStatus(this.keychainAvailable ? 'Authenticated' : 'Install Keychain to Bet',
                                this.keychainAvailable ? 'connected' : 'disconnected');
      this.loadBettingHistory();
      this.showNotification(`Signed in as ${username}. Balance: ${this.formatAmt(balance)} PEK`, 'success');
    } catch (e) {
      this.showNotification(`Sign-in failed: ${e.message}`, 'error');
    }
  }

  loadStoredUser() {
    const stored = localStorage.getItem('peakecoin_user');
    if (stored) {
      const inp = document.getElementById('usernameInput');
      if (inp) inp.value = stored;
      // don't auto-auth pop a Keychain modal; just pull their balance + mark UI
      this.fetchUserBalance(stored).then(bal => {
        if (bal !== null) {
          this.currentUser = stored;
          this.userBalance = bal;
          this.updateBalanceDisplay(bal);
          this.showConnectionStatus(stored);
        }
      });
    }
  }

  // ---------- Balance / History ----------
  async fetchUserBalance(username) {
    try {
      const res = await fetch(`${this.apiUrl}/balance/${username}`);
      const data = await res.json();
      if (res.ok) {
        if (typeof data.precision === 'number') this.tokenPrecision = data.precision;
        const val = (typeof data.pek_balance === 'number' ? data.pek_balance : data.balance);
        return typeof val === 'number' ? val : parseFloat(val);
      }
      return null;
    } catch (e) {
      console.error('fetchUserBalance error', e);
      return null;
    }
  }

  // use this helper everywhere instead of toFixed(3)
  formatAmt(n) { 
    return Number(n).toFixed(this.tokenPrecision); 
  }

  async loadBettingHistory() {
    if (!this.currentUser) return;
    try {
      const res = await fetch(`${this.apiUrl}/bets/${this.currentUser}`);
      const data = await res.json();
      if (res.ok && Array.isArray(data.bets)) this.displayBettingHistory(data.bets);
    } catch (e) {
      console.error('history error', e);
    }
  }

  displayBettingHistory(bets) {
    const el = document.getElementById('bettingHistory');
    if (!el) return;
    if (!bets?.length) {
      el.innerHTML = '<p class="no-bets">No bets placed yet.</p>';
      return;
    }
    el.innerHTML = bets.map(b => {
      const d = new Date(b.created_at);
      const stamp = `${d.toLocaleDateString()} ${d.toLocaleTimeString()}`;
      return `
        <div class="history-item fade-in">
          <div class="bet-info">
            <div class="bet-type">${this.formatBetType(b.bet_type)}</div>
            <div class="bet-details">${b.amount} PEK • ${stamp}${b.payout>0?` • Won ${b.payout} PEK`:''}</div>
          </div>
          <div class="bet-status ${b.status}">${b.status}</div>
        </div>
      `;
    }).join('');
  }

  formatBetType(t) {
    const map = {
      'coin_flip_heads':'🪙 Coin Flip (Heads)',
      'coin_flip_tails':'🪙 Coin Flip (Tails)',
      'dice_roll_1':'🎲 Dice Roll (1)',
      'dice_roll_2':'🎲 Dice Roll (2)',
      'dice_roll_3':'🎲 Dice Roll (3)',
      'dice_roll_4':'🎲 Dice Roll (4)',
      'dice_roll_5':'🎲 Dice Roll (5)',
      'dice_roll_6':'🎲 Dice Roll (6)',
    };
    if (t?.startsWith('lottery_')) return `🎫 Lottery (${t.split('_')[1]} tickets)`;
    return map[t] || t || '';
  }

  updateBalanceDisplay(balance) {
    const el = document.getElementById('balanceAmount');
    if (el) el.textContent = this.formatAmt(balance);
  }

  showConnectionStatus(username) {
    const s = document.getElementById('connectionStatus');
    const u = document.getElementById('connectedUser');
    if (u) u.textContent = username;
    if (s) s.classList.remove('hidden');
  }

  // ---------- Game actions ----------
  selectChoice(button) {
    document.querySelectorAll('.choice-btn').forEach(b => b.classList.remove('selected'));
    button.classList.add('selected');
    this.selectedChoice = button.dataset.choice;
  }

  async placeCoinFlipBet() {
    if (!this._preBetChecks()) return;

    const amount = parseFloat(document.getElementById('coinFlipAmount')?.value || '0');
    if (!amount || amount <= 0) return this.showNotification('Enter a valid bet amount', 'error');
    if (amount > this.userBalance) return this.showNotification(`Insufficient PEK. You have ${this.formatAmt(this.userBalance)}`, 'error');
    if (!this.selectedChoice) return this.showNotification('Select Heads or Tails', 'error');

    const betId = this.generateBetId();
    const memo = `coinflip:${this.selectedChoice}:${betId}`;
    const ok = confirm(
      `🪙 COIN FLIP BET\n\nAmount: ${this.formatAmt(amount)} PEK\nChoice: ${this.selectedChoice.toUpperCase()}\nPotential Win: ${this.formatAmt(amount*2)} PEK\n\nSend ${this.formatAmt(amount)} PEK to peakecoin.matic?`
    );
    if (!ok) return;

    this._sendToken(amount, memo, (resp) => {
      if (resp?.success) {
        this.showNotification('✅ Transaction sent! Processing bet...', 'success');
        this.handleBetTransaction(resp, 'coin_flip', amount, this.selectedChoice);
      } else {
        this._handleTxError(resp);
      }
    });
  }

  async placeDiceBet() {
    if (!this._preBetChecks()) return;

    const amount = parseFloat(document.getElementById('diceAmount')?.value || '0');
    const target = parseInt(document.getElementById('diceTarget')?.value || '0', 10);
    if (!amount || amount <= 0) return this.showNotification('Enter a valid bet amount', 'error');
    if (amount > this.userBalance) return this.showNotification('Insufficient PEK balance', 'error');
    if (!(target >= 1 && target <= 6)) return this.showNotification('Target must be 1–6', 'error');

    const betId = this.generateBetId();
    const memo = `dice:${target}:${betId}`;
    if (!confirm(`Place ${this.formatAmt(amount)} PEK on rolling ${target}? Potential Win: ${this.formatAmt(amount*6)} PEK`)) return;

    this._sendToken(amount, memo, (resp) => {
      if (resp?.success) {
        this.handleBetTransaction(resp, 'dice_roll', amount, target);
      } else {
        this._handleTxError(resp);
      }
    });
  }

  async buyLotteryTickets() {
    if (!this._preBetChecks()) return;

    const tickets = parseInt(document.getElementById('lotteryTickets')?.value || '0', 10);
    const total = tickets * 1;
    if (!tickets || tickets <= 0) return this.showNotification('Enter # of tickets', 'error');
    if (total > this.userBalance) return this.showNotification('Insufficient PEK balance', 'error');

    const betId = this.generateBetId();
    const memo = `lottery:${tickets}:${betId}`;
    if (!confirm(`Buy ${tickets} ticket(s) for ${this.formatAmt(total)} PEK?`)) return;

    this._sendToken(total, memo, (resp) => {
      if (resp?.success) {
        this.handleBetTransaction(resp, 'lottery', total, tickets);
      } else {
        this._handleTxError(resp);
      }
    });
  }

  _preBetChecks() {
    if (!this.currentUser) { this.showNotification('Connect your account first', 'error'); return false; }
    if (typeof window.hive_keychain === 'undefined') { this.showNotification('Hive Keychain is required to bet', 'error'); return false; }
    return true;
  }

  _setBettingDisabled(disabled) {
    document.querySelectorAll('button[data-pek-bet]').forEach(btn => btn.disabled = disabled);
  }

  _sendToken(amount, memo, cb) {
    this._setBettingDisabled(true);
    this.showNotification('⏳ Check Keychain popup...', 'info');
    
    const hasRST = typeof window.hive_keychain?.requestSendToken === 'function';
    if (hasRST) {
      return window.hive_keychain.requestSendToken(
        this.currentUser,
        'peakecoin.matic',
        this.formatAmt(amount),
        memo,
        'PEK',
        (resp) => { this._setBettingDisabled(false); cb(resp); }
      );
    }

    // Fallback using tokens.transfer (ssc-mainnet-hive)
    const json = {
      contractName: 'tokens',
      contractAction: 'transfer',
      contractPayload: {
        symbol: 'PEK',
        to: 'peakecoin.matic',
        quantity: this.formatAmt(amount),
        memo
      }
    };
    window.hive_keychain.requestCustomJson(
      this.currentUser,
      'ssc-mainnet-hive',
      'Active',              // token transfers require Active
      JSON.stringify(json),
      'Send PEK',
      (resp) => { this._setBettingDisabled(false); cb(resp); }
    );
  }

  _handleTxError(response) {
    const msg = response?.message || response?.error || 'Unknown error';
    if (/User cancel/i.test(msg)) this.showNotification('Transaction cancelled', 'warning');
    else if (/not found|does not exist/i.test(msg)) this.showNotification('Account not found in Keychain', 'error');
    else if (/key|permission/i.test(msg)) this.showNotification('Key/permission issue. Check Keychain setup.', 'error');
    else this.showNotification(`Transaction failed: ${msg}`, 'error');
    console.error('TX fail', response);
  }

  async handleBetTransaction(keychainResponse, gameType, amount, choice) {
    try {
      const tx = keychainResponse?.result || keychainResponse?.data || {};
      const betData = {
        user_id: this.currentUser,
        amount,
        bet_type: `${gameType}_${choice}`,
        transaction_id: tx.id || tx.trx_id || tx.tx_id || null,
        block_num: tx.block_num || tx.block || null
      };
      const res = await fetch(`${this.apiUrl}/bet/keychain`, {
        method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(betData)
      });
      const result = await res.json();
      if (res.ok && result.success) {
        this.showNotification(
          `Bet placed! ${result.outcome ? 'You '+result.outcome+'!' : 'Processing...'}`,
          result.won ? 'success' : 'info'
        );
        this.clearGameForms();
        this.loadBettingHistory();
        
        // Wait a bit before refreshing balance (HE indexing can lag)
        setTimeout(async () => {
          const fresh = await this.fetchUserBalance(this.currentUser);
          if (fresh !== null) { 
            this.userBalance = fresh; 
            this.updateBalanceDisplay(fresh); 
          }
        }, 1200);
        
        if (result.payout > 0) setTimeout(() => this.showNotification(`Payout ${this.formatAmt(result.payout)} PEK sent!`, 'success'), 1200);
      } else {
        this.showNotification('Bet processing failed: ' + (result.error || 'Unknown error'), 'error');
      }
    } catch (e) {
      console.error('bet process error', e);
      this.showNotification('Failed to process bet. Contact support.', 'error');
    }
  }

  clearGameForms() {
    const byId = id => document.getElementById(id);
    byId('coinFlipAmount') && (byId('coinFlipAmount').value = '');
    byId('diceAmount') && (byId('diceAmount').value = '');
    byId('lotteryTickets') && (byId('lotteryTickets').value = '1');
    this.selectedChoice = null;
    document.querySelectorAll('.choice-btn').forEach(b => b.classList.remove('selected'));
  }

  // ---------- Diagnostics ----------
  performKeychainTest() {
    if (typeof window.hive_keychain === 'undefined') return this.showNotification('Keychain not detected', 'error');
    if (!this.currentUser) return this.showNotification('Connect your account first', 'warning');

    const msg = `Test message ${Date.now()}`;
    this.showNotification('🧪 Testing Keychain... Check popup!', 'info');
    window.hive_keychain.requestEncodeMessage(
      this.currentUser, msg, 'Posting',
      (r) => {
        if (r?.success) this.showNotification('✅ Keychain test successful!', 'success');
        else if (/User cancel/i.test(r?.message)) this.showNotification('Popup worked; you cancelled (OK)', 'info');
        else this.showNotification(`⚠️ Test failed: ${r?.message || 'Unknown'}`, 'warning');
      }
    );
  }

  debugKeychain() {
    const info = {
      ua: navigator.userAgent,
      hive_keychain: typeof window.hive_keychain,
      requestSendToken: typeof window.hive_keychain?.requestSendToken,
      requestVerifyKey: typeof window.hive_keychain?.requestVerifyKey,
      ts: new Date().toISOString()
    };
    console.log('🐛 Keychain Debug:', info);
    alert(`Keychain Debug\n\n${Object.entries(info).map(([k,v])=>`${k}: ${v}`).join('\n')}`);
    setTimeout(() => this.waitForKeychain(), 500);
  }

  // ---------- Util ----------
  generateBetId() {
    return Date.now().toString(36) + Math.random().toString(36).slice(2);
  }
}

// Global onclick helpers (as you had)
function placeCoinFlipBet() { window.bettingApp.placeCoinFlipBet(); }
function placeDiceBet()     { window.bettingApp.placeDiceBet(); }
function buyLotteryTickets(){ window.bettingApp.buyLotteryTickets(); }

document.addEventListener('DOMContentLoaded', () => { window.bettingApp = new PeakeCoinBetting(); });



0
0
0.000
5 comments
avatar

Will stop over and check it out !PIMP !PAKX !UNI

0
0
0.000
avatar

I'm doing a final update on there, and then I'm crashing.

0
0
0.000
avatar

You are probably going to kick yourself in the butt, but here is what is wrong:
change

        window.hive_keychain.requestVerifyKey(

to

        window.hive_keychain.requestSignBuffer(

and the login works fine

image.png

0
0
0.000
avatar

I was working on this one for days...lol. I always appreciate the help.

0
0
0.000