blockchain-api
Version:
API utilities for interacting with the Exatechl2 blockchain
147 lines (146 loc) • 6.09 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTokenPrice = getTokenPrice;
exports.getFormattedTokenPrice = getFormattedTokenPrice;
const ethers_1 = require("ethers");
const abis_1 = require("./abis");
const logger_1 = require("../../utils/logger");
let lastPrice = {
usd: 0,
bnb: 0,
lastUpdated: 0,
changePercent: 0
};
let priceHistory = [];
const EXATECH_DECIMALS = 9;
/**
*
* @returns
*/
async function getTokenPrice() {
try {
const now = Date.now();
if (lastPrice.lastUpdated > 0 && (now - lastPrice.lastUpdated < 1 * 60 * 1000)) {
return {
usd: lastPrice.usd,
bnb: lastPrice.bnb,
changePercent: lastPrice.changePercent
};
}
const provider = new ethers_1.ethers.providers.JsonRpcProvider("https://bsc-dataseed1.binance.org");
const bnbPriceInUsd = await getBnbPriceInUsd(provider);
logger_1.logger.debug(`BNB price in USD: ${bnbPriceInUsd}`);
const bnbPerExatech = await getExatechPriceInBnbViaRouter(provider);
logger_1.logger.debug(`EXATECH price in BNB via Router: ${bnbPerExatech}`);
const usdPrice = bnbPerExatech * bnbPriceInUsd;
logger_1.logger.debug(`EXATECH final price: ${usdPrice} USD`);
priceHistory.push({
timestamp: now,
usd: usdPrice,
bnb: bnbPerExatech
});
if (priceHistory.length > 24) {
priceHistory.shift();
}
let changePercent = 0;
if (priceHistory.length > 1) {
const oldestPrice = priceHistory[0].usd;
changePercent = ((usdPrice - oldestPrice) / oldestPrice) * 100;
}
lastPrice = {
usd: usdPrice,
bnb: bnbPerExatech,
lastUpdated: now,
changePercent
};
return {
usd: usdPrice,
bnb: bnbPerExatech,
changePercent
};
}
catch (error) {
logger_1.logger.error("Error fetching token price:", error);
throw error;
}
}
async function getBnbPriceInUsd(provider) {
const router = new ethers_1.ethers.Contract(abis_1.CONTRACT_ADDRESSES.PANCAKESWAP_ROUTER, abis_1.PANCAKESWAP_ROUTER_ABI, provider);
const oneBNB = ethers_1.ethers.utils.parseEther("1");
const bnbToUsdt = await router.getAmountsOut(oneBNB, [abis_1.CONTRACT_ADDRESSES.WBNB, abis_1.CONTRACT_ADDRESSES.USDT]);
return parseFloat(ethers_1.ethers.utils.formatUnits(bnbToUsdt[1], 18));
}
async function getExatechPriceInBnbViaRouter(provider) {
try {
const router = new ethers_1.ethers.Contract(abis_1.CONTRACT_ADDRESSES.PANCAKESWAP_ROUTER, abis_1.PANCAKESWAP_ROUTER_ABI, provider);
const exatechAmount = ethers_1.ethers.utils.parseUnits("1", EXATECH_DECIMALS);
logger_1.logger.debug(`Getting price for: ${exatechAmount.toString()} EXATECH baseunits (1 EXATECH)`);
const amountsOut = await router.getAmountsOut(exatechAmount, [abis_1.CONTRACT_ADDRESSES.EXATECH, abis_1.CONTRACT_ADDRESSES.WBNB]);
const bnbAmount = parseFloat(ethers_1.ethers.utils.formatEther(amountsOut[1]));
logger_1.logger.debug(`Router estimates 1 EXATECH = ${bnbAmount} BNB`);
return bnbAmount;
}
catch (error) {
logger_1.logger.error("Error getting price via router:", error);
return getExatechPriceInBnbViaReserves(provider);
}
}
async function getExatechPriceInBnbViaReserves(provider) {
try {
logger_1.logger.debug("Trying reserve method as fallback...");
const pairContract = new ethers_1.ethers.Contract(abis_1.CONTRACT_ADDRESSES.EXATECH_BNB_PAIR, abis_1.PAIR_ABI, provider);
const token0 = await pairContract.token0();
const token1 = await pairContract.token1();
logger_1.logger.debug(`Pair tokens: token0=${token0}, token1=${token1}`);
logger_1.logger.debug(`EXATECH address: ${abis_1.CONTRACT_ADDRESSES.EXATECH}`);
const isExatechToken0 = token0.toLowerCase() === abis_1.CONTRACT_ADDRESSES.EXATECH.toLowerCase();
logger_1.logger.debug(`Is EXATECH token0? ${isExatechToken0}`);
const reserves = await pairContract.getReserves();
let exatechReserve, bnbReserve;
if (isExatechToken0) {
exatechReserve = parseFloat(ethers_1.ethers.utils.formatUnits(reserves.reserve0, EXATECH_DECIMALS));
bnbReserve = parseFloat(ethers_1.ethers.utils.formatEther(reserves.reserve1));
logger_1.logger.debug(`Reserves: EXATECH=${exatechReserve}, BNB=${bnbReserve}`);
}
else {
exatechReserve = parseFloat(ethers_1.ethers.utils.formatUnits(reserves.reserve1, EXATECH_DECIMALS));
bnbReserve = parseFloat(ethers_1.ethers.utils.formatEther(reserves.reserve0));
logger_1.logger.debug(`Reserves: BNB=${bnbReserve}, EXATECH=${exatechReserve}`);
}
logger_1.logger.debug(`Exatech reserve: ${exatechReserve}, BNB reserve: ${bnbReserve}`);
if (exatechReserve < 0.0001 || bnbReserve < 0.0001) {
logger_1.logger.error("Reserves too small for accurate calculation");
throw new Error("Reserves too small");
}
return bnbReserve / exatechReserve;
}
catch (error) {
logger_1.logger.error("Error in reserve method:", error);
throw error;
}
}
/**
* @returns
*/
async function getFormattedTokenPrice() {
try {
const price = await getTokenPrice();
const priceStr = price.usd.toFixed(8);
const changeStr = price.changePercent >= 0
? `+${price.changePercent.toFixed(1)}%`
: `${price.changePercent.toFixed(1)}%`;
return {
price: `$${priceStr}`,
change: changeStr,
isPositive: price.changePercent >= 0
};
}
catch (error) {
logger_1.logger.error("Error formatting token price:", error);
return {
price: "N/A",
change: "-",
isPositive: false
};
}
}