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.";
}
}