UNPKG

liquidops

Version:

LiquidOps is an over-collateralised lending and borrowing protocol built on Arweave's L2 AO.

173 lines (147 loc) 5.22 kB
import { getData } from "../../ao/messaging/getData"; import { tokens, tokenData, SupportedTokensTickers, redstoneOracleAddress, } from "../../ao/utils/tokenAddressData"; import { collateralEnabledTickers } from "../../ao/utils/tokenAddressData"; import { getPosition } from "./getPosition"; // Base token position with the core metrics interface TokenPosition { borrowBalance: bigint; capacity: bigint; collateralization: bigint; liquidationLimit: bigint; ticker: string; } // Global position across all tokens interface GlobalPosition { borrowBalanceUSD: bigint; capacityUSD: bigint; collateralizationUSD: bigint; liquidationLimitUSD: bigint; usdDenomination: bigint; tokenPositions: { [token: string]: TokenPosition; }; } type RedstonePrices = Record<string, { t: number; a: string; v: number }>; export interface GetGlobalPositionRes { globalPosition: GlobalPosition; prices: RedstonePrices; } export interface GetGlobalPosition { walletAddress: string; } export async function getGlobalPosition({ walletAddress, }: GetGlobalPosition): Promise<GetGlobalPositionRes> { try { if (!walletAddress) { throw new Error("Please specify a wallet address."); } // Get list of tokens to process const tokensList = Object.keys(tokens); // Make a request to RedStone oracle for prices const redstonePriceFeedRes = await getData({ Target: redstoneOracleAddress, Action: "v2.Request-Latest-Data", Tickers: JSON.stringify( collateralEnabledTickers.map((ticker) => { if (ticker === "QAR") return "AR"; if (ticker === "WUSDC") return "USDC"; return ticker; }), ), }); // Parse prices const prices: RedstonePrices = JSON.parse( redstonePriceFeedRes.Messages[0].Data, ); // Fetch positions for each token const positionsPromises = tokensList.map(async (token) => { try { const position = await getPosition({ token, recipient: walletAddress, }); return { token, position, }; } catch (error) { // If position doesn't exist for this token, return null return { token, position: null, }; } }); const positionsResults = await Promise.all(positionsPromises); // Initialize global position const globalPosition: GlobalPosition = { borrowBalanceUSD: BigInt(0), capacityUSD: BigInt(0), collateralizationUSD: BigInt(0), liquidationLimitUSD: BigInt(0), usdDenomination: BigInt(0), tokenPositions: {}, }; // Highest denomination used let highestDenomination = BigInt(0); // Calculate global position for the wallet across all tokens for (const { token, position } of positionsResults) { // Skip if position doesn't exist for this token if (!position) continue; // Parse values to BigInt const tokenPosition: TokenPosition = { borrowBalance: BigInt(position.borrowBalance || 0), capacity: BigInt(position.capacity || 0), collateralization: BigInt(position.collateralization || 0), liquidationLimit: BigInt(position.liquidationLimit || 0), ticker: token, }; // Store the token position globalPosition.tokenPositions[token] = tokenPosition; // Get token price and denomination for USD conversion const tokenPrice = prices[token === "QAR" ? "AR" : token === "WUSDC" ? "USDC" : token].v; const tokenDenomination = tokenData[token as SupportedTokensTickers].baseDenomination; // Set the highest denomination if (highestDenomination < tokenDenomination) highestDenomination = tokenDenomination; // Use the token's specific denomination for scaling const scale = BigInt(10) ** highestDenomination; const priceScaled = BigInt(Math.round(tokenPrice * Number(scale))); // The scale difference caused by the different token denominations const scaleDifference = BigInt(10) ** (highestDenomination - tokenDenomination); // Convert token values to USD const borrowBalanceUSD = (tokenPosition.borrowBalance * scaleDifference * priceScaled) / scale; const capacityUSD = (tokenPosition.capacity * scaleDifference * priceScaled) / scale; const collateralizationUSD = (tokenPosition.collateralization * scaleDifference * priceScaled) / scale; const liquidationLimitUSD = (tokenPosition.liquidationLimit * scaleDifference * priceScaled) / scale; // Add to global position totals globalPosition.borrowBalanceUSD += borrowBalanceUSD; globalPosition.capacityUSD += capacityUSD; globalPosition.collateralizationUSD += collateralizationUSD; globalPosition.liquidationLimitUSD += liquidationLimitUSD; } // Set USD denomination (should be the highest used denomination) globalPosition.usdDenomination = highestDenomination; return { globalPosition, prices, }; } catch (error) { throw new Error(`Error in getGlobalPosition function: ${error}`); } }