@symmetry-hq/baskets-v2-sdk
Version:
Symmetry Baskets V2 SDK
190 lines (189 loc) • 10.3 kB
JavaScript
;
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,
});
});
}