PeakeCoin AtomPEK Swaps - JavaScript Updates

On the route to greatness you often use a lot of swear words. I had a lot of issues trying to get this whole Atomic Swap function. I have it working on the sell of each token, but the buys don't seem to be working.

I have created a he_cors_proxy.py to the CORS work around and have launched it on a free service. I'll write that up in the next few days.

Have I eliminated the CORS problems, yes. Does it work right now? Not exactly sure.


script.js - Main entry point. Wires up UI, handles form input, and triggers the swap logic.

import { fetchWithBackups, getHiveBlockNumberForTxId } from './api.js';
import { getSwapHivePayoutForTx, performSwap } from './swapLogic.js';
import { updateRateDisplay, setSwapResult } from './ui.js';
import { logDebug } from './utils.js';

logDebug('App loaded.');

async function handleRateDisplay() {
    const token = document.getElementById('hiveToken').value;
    const tokenAmount = parseFloat(document.getElementById('hiveAmount').value);
    if (tokenAmount > 0) {
        const swapHiveRate = await fetchSwapHiveRate(token);
        updateRateDisplay(swapHiveRate, tokenAmount, token);
    } else {
        updateRateDisplay(null, 0, token);
    }
}
document.getElementById('hiveAmount').addEventListener('input', handleRateDisplay);
document.getElementById('hiveToken').addEventListener('change', handleRateDisplay);
window.addEventListener('DOMContentLoaded', handleRateDisplay);

document.getElementById('swapKeychain').addEventListener('click', function(e) {
    e.preventDefault();
    logDebug('swapKeychain button clicked');
    performSwap(true);
});
document.getElementById('swapHivesigner').addEventListener('click', function(e) {
    e.preventDefault();
    logDebug('swapHivesigner button clicked');
    performSwap(false);
});

swapLogic.js - Orchestrates the swap. Validates input, kicks off sell, polls for payout, and triggers buy PEK.


import { fetchWithBackups, getHiveBlockNumberForTxId } from './api.js';
import { logDebug } from './utils.js';
import { performKeychainSell, performKeychainBuy } from './keychain.js';

export async function performSwap(useKeychain) {
    const account = document.getElementById('hiveSender').value.trim();
    const symbol = document.getElementById('hiveToken').value;
    const quantity = parseFloat(document.getElementById('hiveAmount').value);
    const swapResult = document.getElementById('swapResult');
    logDebug(`Swap requested: account=${account}, symbol=${symbol}, quantity=${quantity}, useKeychain=${useKeychain}`);
    swapResult.innerHTML = '';
    if (!account || !symbol || !quantity || quantity <= 0) {
        swapResult.innerHTML = "Please fill in all fields.";
        logDebug('Swap aborted: missing fields.');
        return;
    }
    if (symbol === 'SCALA') {
        logDebug('Scala swap selected.');
        swapResult.innerHTML = 'Scala swap not implemented in this module.';
        return;
    }
    if (useKeychain) {
        performKeychainSell(account, symbol, quantity, swapResult, getSwapHivePayoutForTx, getLastSwapHivePayout, performBuyPEK);
    } else {
        logDebug('Opening Hivesigner for marketSell.');
        swapResult.innerHTML = "Hivesigner flow not implemented in this module.";
    }
}

keychain.js
Handles signing and broadcasting Hive Keychain transactions, both for sell and buy. Polls for payout and triggers buy automatically.


import { logDebug } from './utils.js';

export function performKeychainSell(account, symbol, quantity, swapResult, getSwapHivePayoutForTx, getLastSwapHivePayout, performBuyPEK) {
    if (!window.hive_keychain) {
        swapResult.innerHTML = "Hive Keychain extension not detected.";
        logDebug('Hive Keychain not detected.');
        return;
    }
    const sellJson = {
        contractName: "market",
        contractAction: "marketSell",
        contractPayload: {
            symbol: symbol,
            quantity: String(quantity)
        }
    };
    logDebug('Requesting Keychain signature for marketSell...');
    window.hive_keychain.requestCustomJson(
        account,
        'ssc-mainnet-hive',
        'Active',
        JSON.stringify(sellJson),
        `Sell ${quantity} ${symbol} for SWAP.HIVE`,
        function(response) {
            logDebug('Keychain response: ' + JSON.stringify(response));
            if (response.success) {
                swapResult.innerHTML = "Sell order broadcasted! Waiting for your SWAP.HIVE payout...";
                let payout = 0;
                let pollCount = 0;
                let lastPayout = 0;
                const txId = response.result && response.result.tx_id ? response.result.tx_id : null;
                const pollPayout = async function() {
                    payout = txId ? await getSwapHivePayoutForTx(account, symbol, txId) : 0;
                    if (!payout || payout <= 0) {
                        payout = await getLastSwapHivePayout(account, symbol);
                    }
                    logDebug(`Polling payout (txId=${txId}): ${payout}`);
                    if (payout > lastPayout + 0.0000001) {
                        lastPayout = payout;
                        swapResult.innerHTML += '<br>SWAP.HIVE payout detected! Waiting 10 seconds before buying PEK...';
                        logDebug('SWAP.HIVE payout detected. Waiting 10 seconds before auto-buying PEK.');
                        setTimeout(function() {
                            logDebug('Auto-buying PEK after 10s delay.');
                            performBuyPEK(account, payout, true);
                        }, 10000);
                    } else if (++pollCount < 30) {
                        setTimeout(pollPayout, 2000);
                    } else {
                        swapResult.innerHTML = "No new SWAP.HIVE payout detected from your sale after 60 seconds. Please check your wallet and try again.";
                        logDebug('Payout polling timed out.');
                    }
                };
                setTimeout(pollPayout, 2000);
            } else {
                swapResult.innerHTML = "Keychain error: " + (response.message || "Unknown error");
                logDebug('Keychain error: ' + (response.message || 'Unknown error'));
            }
        }
    );
}

export function performKeychainBuy(account, swapHiveAmount, swapResult) {
    const MULTI_TX_FEE = 0.001;
    let buyAmount = swapHiveAmount - MULTI_TX_FEE;
    logDebug(`Preparing to buy PEK: swapHiveAmount=${swapHiveAmount}, buyAmount=${buyAmount}, useKeychain=true`);
    if (buyAmount <= 0) {
        swapResult.innerHTML = "Insufficient SWAP.HIVE amount after fee deduction.";
        logDebug('Buy aborted: insufficient SWAP.HIVE after fee.');
        return;
    }
    const buyJson = {
        contractName: "market",
        contractAction: "marketBuy",
        contractPayload: {
            symbol: 'PEK',
            quantity: String(buyAmount)
        }
    };
    logDebug('Requesting Keychain signature for marketBuy...');
    window.hive_keychain.requestCustomJson(
        account,
        'ssc-mainnet-hive',
        'Active',
        JSON.stringify(buyJson),
        `Buy PEK with ${buyAmount} SWAP.HIVE`,
        function(response) {
            logDebug('Keychain response (buy): ' + JSON.stringify(response));
            if (response.success) {
                swapResult.innerHTML = "Buy order broadcasted!";
            } else {
                swapResult.innerHTML = "Keychain error: " + (response.message || "Unknown error");
                logDebug('Keychain error (buy): ' + (response.message || 'Unknown error'));
            }
        }
    );
}

api.js - Handles network logic for Hive Engine and Hive, with endpoint failover.

export const HIVE_ENGINE_APIS = [
    'https://peake-swap.onrender.com/he-proxy'
];

export const CORS_PROXY = 'https://corsproxy.io/?';

export async function fetchWithBackups(options) {
    for (let i = 0; i < HIVE_ENGINE_APIS.length; i++) {
        try {
            const res = await fetch(HIVE_ENGINE_APIS[i], options);
            if (res.ok) return await res.json();
        } catch (e) {}
    }
    for (let i = 0; i < HIVE_ENGINE_APIS.length; i++) {
        try {
            const res = await fetch(CORS_PROXY + HIVE_ENGINE_APIS[i], options);
            if (res.ok) return await res.json();
        } catch (e) {}
    }
    return null;
}

ui.js- Updates the DOM for rates and swap results.

import { logDebug } from './utils.js';

export function updateRateDisplay(rate, tokenAmount, token) {
    const rateDisplay = document.getElementById('rateDisplay');
    if (rate && tokenAmount > 0) {
        const pekAmount = tokenAmount * rate;
        rateDisplay.innerHTML = `Estimated: <b>${pekAmount.toFixed(6)} PEK</b> for <b>${tokenAmount} ${token}</b><br><span style='font-size:0.95em;color:#fff;'>Final swap rate is determined by the market at the time of each transaction.</span>`;
    } else {
        rateDisplay.textContent = 'Unable to fetch live SWAP.HIVE rate.';
    }
}

export function setSwapResult(msg) {
    document.getElementById('swapResult').innerHTML = msg;
}

utils.js - General helpers and debug logging.

js

export function logDebug(msg) {
    const el = document.getElementById('debugLogContent');
    if (el) {
        const now = new Date().toLocaleTimeString();
        const entry = `<span style="font-size:0.82em;line-height:1.5;display:block;margin-bottom:2px;">[${now}] ${msg}</span>`;
        el.innerHTML += entry;
        el.scrollTop = el.scrollHeight;
    }
}


0
0
0.000
0 comments