UNPKG

@symmetry-hq/baskets-v2-sdk

Version:

Symmetry Baskets V2 SDK

190 lines (189 loc) 10.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BASKETS_STATE_SIZE = void 0; exports.fetchBasketState = fetchBasketState; exports.parseBasketState = parseBasketState; exports.getBasketTokenPrices = getBasketTokenPrices; exports.computeRebalanceInfos = computeRebalanceInfos; exports.getBasketTvl = getBasketTvl; const web3_js_1 = require("@solana/web3.js"); const oracle_1 = require("../utils/oracle"); const constants_1 = require("../utils/constants"); const constants_2 = require("../utils/constants"); const constants_3 = require("../utils/constants"); const programAccounts_1 = require("../utils/programAccounts"); const constants_4 = require("../utils/constants"); exports.BASKETS_STATE_SIZE = 28189; function fetchBasketState(program, basket) { return __awaiter(this, void 0, void 0, function* () { return yield program.account.basketV200.fetch(basket); }); } function parseBasketState(basketState) { const managers = []; for (let i = 0; i < constants_1.MANAGERS_PER_BASKET; i++) { if (basketState.managers[i].equals(web3_js_1.PublicKey.default)) { break; } managers.push(basketState.managers[i]); } return { version: basketState.version, ownAddress: basketState.ownAddress.toBase58(), basketType: basketState.basketType, basketPda: basketState.basketPda.toBase58(), mint: basketState.mint.toBase58(), supplyOutstanding: parseInt(basketState.supplyOutstanding.toString()), lastPrice: parseInt(basketState.lastPrice.toString()), startingPrice: parseInt(basketState.startingPrice.toString()), highestPrice: parseInt(basketState.highestPrice.toString()), creator: basketState.creator.toBase58(), creatorDepositFeeBps: basketState.creatorDepositFeeBps, creatorManagementFeeBps: basketState.creatorManagementFeeBps, creatorPerformanceFeeBps: basketState.creatorPerformanceFeeBps, host: basketState.host.toBase58(), hostDepositFeeBps: basketState.hostDepositFeeBps, hostManagementFeeBps: basketState.hostManagementFeeBps, hostPerformanceFeeBps: basketState.hostPerformanceFeeBps, managers: basketState.managers.slice(0, managers.length).map((manager) => manager.toBase58()), managersWeightBps: basketState.managersWeightBps.slice(0, managers.length), managersAuthority: basketState.managersAuthority.slice(0, managers.length), managersDepositFeeBps: basketState.managersDepositFeeBps, managersPerformanceFeeBps: basketState.managersPerformanceFeeBps, managersManagementFeeBps: basketState.managersManagementFeeBps, basketDepositFeeBps: basketState.basketDepositFeeBps, basketWithdrawFeeBps: basketState.basketWithdrawFeeBps, rebalanceIntervalSeconds: parseInt(basketState.rebalanceIntervalSeconds.toString()), rebalanceThresholdBps: basketState.rebalanceThresholdBps, rebalanceSlippageBps: basketState.rebalanceSlippageBps, lpThresholdBps: basketState.lpThresholdBps, allowAutomation: basketState.allowAutomation, allowLp: basketState.allowLp, lamportsForAutomation: parseInt(basketState.lamportsForAutomation.toString()), // symbolLength: basketState.symbolLength, // symbol: basketState.symbol, // nameLength: basketState.nameLength, // name: basketState.name, // uriLength: basketState.uriLength, // uri: basketState.uri, metadataAccount: basketState.metadataAccount.toBase58(), lookupTable1: basketState.lookupTable1.toBase58(), lookupTable2: basketState.lookupTable2.toBase58(), otherLookupTable1: basketState.otherLookupTable1.toBase58(), otherLookupTable2: basketState.otherLookupTable2.toBase58(), writeVersion: parseInt(basketState.writeVersion.toString()), numTokens: basketState.numTokens, compositionMints: basketState.compositionMints.slice(0, basketState.numTokens).map((mint) => mint.toBase58()), compositionDecimals: basketState.compositionDecimals.slice(0, basketState.numTokens), compositionOracleType: basketState.compositionOracleType.slice(0, basketState.numTokens), compositionOracle1: basketState.compositionOracle1.slice(0, basketState.numTokens).map((oracle) => oracle.toBase58()), compositionOracle2: basketState.compositionOracle2.slice(0, basketState.numTokens).map((oracle) => oracle.toBase58()), compositionTargetWeights: basketState.compositionTargetWeights.slice(0, basketState.numTokens), compositionAmounts: basketState.compositionAmounts.slice(0, basketState.numTokens).map(x => parseInt(x.toString())), tokenPrices: basketState.tokenPrices.slice(0, basketState.numTokens).map(x => parseInt(x.toString())), tokenPriceUpdateTimestamps: basketState.tokenPriceUpdateTimestamps.slice(0, basketState.numTokens).map(x => parseInt(x.toString())), lastRebalanceTimestamp: basketState.lastRebalanceTimestamp.slice(0, basketState.numTokens).map(x => parseInt(x.toString())), metadata: null, tvl: null, tokenValues: null, }; } function getBasketTokenPrices(program, basketState) { return __awaiter(this, void 0, void 0, function* () { // Collect oracle accounts to fetch const oracleAccounts = [ constants_1.PYTHNET_CUSTODY_PRICE_USDC_ACCOUNT, constants_2.PYTHNET_CUSTODY_PRICE_SOL_ACCOUNT, ]; for (let i = 0; i < basketState.numTokens; i++) { oracleAccounts.push(basketState.compositionOracle1[i]); const oracle2 = basketState.compositionOracle2[i]; if (!oracle2.equals(web3_js_1.PublicKey.default)) { oracleAccounts.push(oracle2); } } const oracleAccountInfos = yield (0, programAccounts_1.getAccountInfos)(program.provider.connection, oracleAccounts); const usdcPrice = (0, oracle_1.loadOraclePrice)(constants_3.USDC_DECIMALS, oracle_1.OracleType.Pyth, oracleAccountInfos[0], null, 0, 0, 0).avgPrice; const solPrice = (0, oracle_1.loadOraclePrice)(constants_4.WSOL_DECIMALS, oracle_1.OracleType.Pyth, oracleAccountInfos[1], null, 0, 0, 0).avgPrice; // Calculate prices for each token const oraclePrices = []; let currentIndex = 2; for (let i = 0; i < basketState.numTokens; i++) { const oracleAccount1 = oracleAccountInfos[currentIndex]; let oracleAccount2 = oracleAccountInfos[currentIndex]; currentIndex++; if (!basketState.compositionOracle2[i].equals(web3_js_1.PublicKey.default)) { oracleAccount2 = oracleAccountInfos[currentIndex]; currentIndex++; } const oraclePrice = (0, oracle_1.loadOraclePrice)(basketState.compositionDecimals[i], basketState.compositionOracleType[i], oracleAccount1, oracleAccount2, solPrice, usdcPrice, 0); oraclePrices.push(oraclePrice); } return oraclePrices; }); } function computeRebalanceInfos(params) { let basketValue = 0; const values = []; for (let i = 0; i < params.basketState.numTokens; i++) { const tokenPrice = params.oraclePrices[i].avgPrice; const currentAmount = parseInt(params.basketState.compositionAmounts[i].toString()); const decimals = params.basketState.compositionDecimals[i]; const currentValue = tokenPrice * currentAmount / (10 ** decimals); basketValue += currentValue; values.push(currentValue); } ; const tokenValues = []; const rebalanceInfos = []; for (let i = 0; i < params.basketState.numTokens; i++) { const tokenPrice = params.oraclePrices[i].avgPrice; const currentAmount = parseInt(params.basketState.compositionAmounts[i].toString()); const decimals = params.basketState.compositionDecimals[i]; const currentValue = tokenPrice * currentAmount / (10 ** decimals); const currentWeight = Math.floor(currentValue * constants_1.TOTAL_WEIGHT / basketValue); const targetWeight = params.basketState.compositionTargetWeights[i]; const targetValue = basketValue * targetWeight / constants_1.TOTAL_WEIGHT; let maxSpendAmount = 0; if (currentWeight > targetWeight) { maxSpendAmount = Math.floor(currentAmount * (currentWeight - targetWeight) / currentWeight); } rebalanceInfos.push({ token: params.basketState.compositionMints[i], tokenDecimals: decimals, tokenPrice: tokenPrice, index: i, currentAmount: currentAmount, currentWeight: currentWeight, currentValue: currentValue, targetWeight: targetWeight, targetValue: targetValue, valueDiff: targetValue - currentValue, maxSpendAmount: maxSpendAmount, }); tokenValues.push([Number(values[i].toFixed(6)), (currentWeight / 100).toFixed(2) + "% -> " + (targetWeight / 100).toFixed(2) + "%"]); } return { tvl: basketValue, tokenValues: tokenValues, rebalanceInfos: rebalanceInfos, }; } function getBasketTvl(program, basketState) { return __awaiter(this, void 0, void 0, function* () { const oraclePrices = yield getBasketTokenPrices(program, basketState); return computeRebalanceInfos({ basketState: basketState, oraclePrices: oraclePrices, }); }); }