Atompek Swap & CORs

in Synergy Builders4 days ago

When I'm trying to make swaps work... but I keep getting CORs errors.

Minimal fun.

So these gosh darn CORs's, lol.

Am I not calling for it correctly? It's been a few months of work to build this thing. I'm a bit of an ADD type of character, so I am all over the place but still trying to get shit done.

Help!!

I may end up having this fixed, but right now I'm at my wits' end. So slide some knowledge on me, folks.

// script.js - Handles the frontend logic for atomic swap UI (Keychain & Hivesigner DEX version)

// List of Hive Engine API endpoints (primary and backups)
const HIVE_ENGINE_APIS = [
    'https://api.hive-engine.com/rpc',
    'https://herpc.dtools.dev',
    'https://heapi.inleo.io',
    'https://api.primersion.com',
    'https://api.ausbit.dev',
    'https://api.tribaldex.com/rpc'
];

// Helper: Try all APIs in order until one succeeds
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) {}
    }
    return null;
}

async function fetchSwapHiveRate(token) {
    // Try buyBook (best ask)
    try {
        const data = await fetchWithBackups({
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                jsonrpc: '2.0',
                id: 1,
                method: 'find',
                params: {
                    contract: 'market',
                    table: 'buyBook',
                    query: { symbol: token, baseSymbol: 'SWAP.HIVE' },
                    limit: 1,
                    indexes: [{ index: 'price', descending: true }]
                }
            })
        });
        if (data && data.result && data.result.length > 0 && data.result[0].price && !isNaN(data.result[0].price)) {
            return parseFloat(data.result[0].price);
        }
    } catch (e) {}
    // Try metrics as fallback
    try {
        const data = await fetchWithBackups({
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                jsonrpc: '2.0',
                id: 1,
                method: 'findOne',
                params: {
                    contract: 'market',
                    table: 'metrics',
                    query: { symbol: token }
                }
            })
        });
        if (data && data.result && data.result.lastPrice && !isNaN(data.result.lastPrice)) {
            return parseFloat(data.result.lastPrice);
        }
    } catch (e) {}
    return null;
}

// --- UI Rate Display Logic (modified) ---
async function updateRateDisplay() {
    const token = document.getElementById('hiveToken').value;
    const tokenAmount = parseFloat(document.getElementById('hiveAmount').value);
    const rateDisplay = document.getElementById('rateDisplay');
    if (tokenAmount > 0) {
        const swapHiveRate = await fetchSwapHiveRate(token);
        if (swapHiveRate) {
            const pekAmount = tokenAmount * swapHiveRate;
            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.';
        }
    } else {
        rateDisplay.textContent = '';
    }
}
document.getElementById('hiveAmount').addEventListener('input', updateRateDisplay);
document.getElementById('hiveToken').addEventListener('change', updateRateDisplay);
window.addEventListener('DOMContentLoaded', updateRateDisplay);

// --- Helper: Build Hive Engine custom_json for marketSell/marketBuy ---
function buildMarketSellJson(account, symbol, quantity) {
    return {
        contractName: "market",
        contractAction: "marketSell",
        contractPayload: {
            symbol: symbol,
            quantity: String(quantity)
        }
    };
}
function buildMarketBuyJson(account, symbol, quantity) {
    return {
        contractName: "market",
        contractAction: "marketBuy",
        contractPayload: {
            symbol: symbol,
            quantity: String(quantity)
        }
    };
}

// --- Helper: Hivesigner custom_json link ---
function buildHivesignerCustomJsonLink(account, json, authority = 'Active') {
    const op = [
        'custom_json',
        {
            required_auths: [account],
            required_posting_auths: [],
            id: 'ssc-mainnet-hive',
            json: JSON.stringify(json)
        }
    ];
    const ops = encodeURIComponent(JSON.stringify([op]));
    return `https://hivesigner.com/sign/tx?operations=${ops}&authority=${authority}`;
}

// --- Combined Swap: Sell Token for SWAP.HIVE, then Buy PEK ---
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');
    swapResult.innerHTML = '';
    if (!account || !symbol || !quantity || quantity <= 0) {
        swapResult.innerHTML = "Please fill in all fields.";
        return;
    }
    // Step 1: Sell selected token for SWAP.HIVE
    const sellJson = buildMarketSellJson(account, symbol, quantity);
    if (useKeychain) {
        if (!window.hive_keychain) {
            swapResult.innerHTML = "Hive Keychain extension not detected.";
            return;
        }
        // Use a non-async callback for Keychain (Keychain expects a regular function, not async)
        window.hive_keychain.requestCustomJson(
            account,
            'ssc-mainnet-hive',
            'Active',
            JSON.stringify(sellJson),
            `Sell ${quantity} ${symbol} for SWAP.HIVE`,
            function(response) {
                if (response.success) {
                    swapResult.innerHTML = "Sell order broadcasted! Waiting for your SWAP.HIVE payout...";
                    // Poll for payout up to 30s, every 2s
                    let payout = 0;
                    let pollCount = 0;
                    const pollPayout = async function() {
                        payout = await getLastSwapHivePayoutFromLogs(account, symbol);
                        if (payout > 0.000001) {
                            performBuyPEK(account, payout, true);
                        } else if (++pollCount < 15) {
                            setTimeout(pollPayout, 2000);
                        } else {
                            swapResult.innerHTML = "No SWAP.HIVE payout detected from your sale after 30 seconds. Please check your wallet and try again.";
                        }
                    };
                    setTimeout(pollPayout, 2000);
                } else {
                    swapResult.innerHTML = "Keychain error: " + (response.message || "Unknown error");
                }
            }
        );
    } else {
        const url = buildHivesignerCustomJsonLink(account, sellJson, 'Active');
        window.open(url, '_blank');
        swapResult.innerHTML = "Sell order link opened in Hivesigner. Waiting for your SWAP.HIVE payout...";
        // Poll for payout up to 30s, every 2s
        let pollCount = 0;
        let payout = 0;
        const pollPayout = async function() {
            payout = await getLastSwapHivePayoutFromLogs(account, symbol);
            if (payout > 0.000001) {
                performBuyPEK(account, payout, false);
            } else if (++pollCount < 15) {
                setTimeout(pollPayout, 2000);
            } else {
                swapResult.innerHTML = "No SWAP.HIVE payout detected from your sale after 30 seconds. Please check your wallet and try again.";
            }
        };
        setTimeout(pollPayout, 2000);
    }
}

// Fetch the user's SWAP.HIVE balance from Hive Engine
async function getSwapHiveBalance(account) {
    try {
        const res = await fetch('https://api.hive-engine.com/rpc', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                jsonrpc: '2.0',
                id: 1,
                method: 'findOne',
                params: {
                    contract: 'tokens',
                    table: 'balances',
                    query: { account: account, symbol: 'SWAP.HIVE' }
                }
            })
        });
        const data = await res.json();
        if (data && data.result && data.result.balance) {
            return parseFloat(data.result.balance);
        }
    } catch (e) {}
    return 0;
}

// Fetch the most recent SWAP.HIVE payout from a marketSell for the selected token
async function getLastSwapHivePayout(account, symbol) {
    try {
        const res = await fetch('https://api.hive-engine.com/rpc', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                jsonrpc: '2.0',
                id: 1,
                method: 'find',
                params: {
                    contract: 'market',
                    table: 'trades',
                    query: { account: account, symbol: symbol, market: 'SWAP.HIVE' },
                    limit: 10,
                    indexes: [{ index: 'timestamp', descending: true }]
                }
            })
        });
        const data = await res.json();
        if (data && data.result && data.result.length > 0) {
            // Find the most recent trade where the user was the seller and payoutSymbol is SWAP.HIVE
            for (const trade of data.result) {
                if (trade.account === account && trade.symbol === symbol && trade.payoutSymbol === 'SWAP.HIVE') {
                    return parseFloat(trade.payoutQuantity);
                }
            }
        }
    } catch (e) {}
    return 0;
}

// Fetch the most recent SWAP.HIVE payout from a marketSell for the selected token using logs/events
async function getLastSwapHivePayoutFromLogs(account, symbol) {
    try {
        // Query the last 10 blocks for marketSell actions by the user
        const res = await fetch('https://api.hive-engine.com/rpc', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                jsonrpc: '2.0',
                id: 1,
                method: 'find',
                params: {
                    contract: 'blockLog',
                    table: 'blocks',
                    query: {},
                    limit: 10,
                    indexes: [{ index: 'blockNumber', descending: true }]
                }
            })
        });
        const data = await res.json();
        if (data && data.result && data.result.length > 0) {
            for (const block of data.result) {
                if (block.transactions) {
                    for (const tx of block.transactions) {
                        if (tx.sender === account && tx.contract === 'market' && tx.action === 'marketSell' && tx.payload && tx.payload.symbol === symbol) {
                            // Look for a transferFromContract event for SWAP.HIVE to the user
                            if (tx.logs && tx.logs.events) {
                                for (let i = 0; i < tx.logs.events.length; i++) {
                                    const event = tx.logs.events[i];
                                    if (event.contract === 'tokens' && event.event === 'transferFromContract' && event.data && event.data.to === account && event.data.symbol === 'SWAP.HIVE') {
                                        return parseFloat(event.data.quantity);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    } catch (e) {}
    return 0;
}

// --- Combined Swap: Sell Token for SWAP.HIVE, then Buy PEK ---
async function performBuyPEK(account, swapHiveAmount, useKeychain) {
    const swapResult = document.getElementById('swapResult');
    // Subtract Hive Engine multiTransaction fee (0.001 SWAP.HIVE)
    const MULTI_TX_FEE = 0.001;
    let buyAmount = swapHiveAmount - MULTI_TX_FEE;
    if (buyAmount <= 0) {
        swapResult.innerHTML = "Insufficient SWAP.HIVE amount after fee deduction.";
        return;
    }
    const buyJson = buildMarketBuyJson(account, 'PEK', buyAmount);
    if (useKeychain) {
        if (!window.hive_keychain) {
            swapResult.innerHTML = "Hive Keychain extension not detected.";
            return;
        }
        window.hive_keychain.requestCustomJson(
            account,
            'ssc-mainnet-hive',
            'Active',
            JSON.stringify(buyJson),
            `Buy PEK with ${buyAmount} SWAP.HIVE`,
            function(response) {
                if (response.success) {
                    swapResult.innerHTML = "Buy order broadcasted!";
                } else {
                    swapResult.innerHTML = "Keychain error: " + (response.message || "Unknown error");
                }
            }
        );
    } else {
        const url = buildHivesignerCustomJsonLink(account, buyJson, 'Active');
        window.open(url, '_blank');
        swapResult.innerHTML = "Buy order link opened in Hivesigner.";
    }
}
Sort:  

try running it locally in a simple http server like this:
go into the folder with the code and run python3 -m http.server 9000 -d . and see if it works, you may not be able to make the cross api calls from your host, it might not be the code itself.

It was working but it wasn't swapping the correct amounts I played around with it and now it's giving me this.

Ill be adding logging and debugging in the window of the browser tomorrow.