@symmetry-hq/baskets-v2-sdk
Version:
Symmetry Baskets V2 SDK
112 lines (98 loc) • 3.77 kB
text/typescript
// Core dependencies
import { BN, Program } from "@coral-xyz/anchor";
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
// Local imports
import { BasketsProgram } from "../../idl/types";
import { fetchBasketState } from "../../state/basket";
import { MAX_PRICE_UPDATES_PER_TX, PYTHNET_CUSTODY_PRICE_SOL_ACCOUNT, PYTHNET_CUSTODY_PRICE_USDC_ACCOUNT, UPDATE_PRICES_AUTHORITY } from "../../utils/constants";
async function getJupPrices(
mints: PublicKey[],
): Promise<number[]> {
let batchSize = 100;
let prices: number[] = [];
for (let i = 0; i < mints.length; i += batchSize) {
const batch = mints.slice(i, i + batchSize);
let str = batch.map(mint => mint.toBase58()).join(",");
let result = await fetch(`https://api.jup.ag/price/v2?ids=${str}`);
let data = (await result.json()).data;
for (let j = 0; j < batch.length; j++) {
if (data[batch[j].toBase58()])
prices.push(data[batch[j].toBase58()].price);
else {
throw new Error("Price not found for " + batch[j].toBase58());
}
}
}
return prices;
}
// Helper function to create update prices instruction
async function createUpdatePricesIx(
program: Program<BasketsProgram>,
basket: PublicKey,
accounts: {
pubkey: PublicKey;
isSigner: boolean;
isWritable: boolean;
}[],
tokenPrices: BN[],
): Promise<TransactionInstruction> {
while (tokenPrices.length < 25)
tokenPrices.push(new BN(0));
return program.methods
.updateTokenPrices(tokenPrices)
.accountsStrict({
authority: UPDATE_PRICES_AUTHORITY,
basket,
usdcPriceOracle: PYTHNET_CUSTODY_PRICE_USDC_ACCOUNT,
solPriceOracle: PYTHNET_CUSTODY_PRICE_SOL_ACCOUNT,
})
.remainingAccounts(accounts)
.instruction();
};
// Update token prices instructions
export async function updateTokenPricesIxs(params: {
program: Program<BasketsProgram>;
basket: PublicKey;
}): Promise<{
ixs: TransactionInstruction[],
luts: PublicKey[],
}> {
// Destructure params and get basket state
const { program, basket } = params;
const basketState = await fetchBasketState(program, basket);
let jupPrices = await getJupPrices(basketState.compositionMints.slice(0, basketState.numTokens));
// Initialize arrays to store instructions and remaining accounts
const ixs: TransactionInstruction[] = [];
let remainingAccounts = [];
let tokenPrices = [];
// Process each token's oracle accounts
for (let i = 0; i < basketState.numTokens; i++) {
let num = Math.floor(jupPrices[i] * 10 ** 10);
tokenPrices.push(new BN(num).mul(new BN(100)));
// Add primary oracle
remainingAccounts.push({
pubkey: basketState.compositionOracle1[i],
isSigner: false,
isWritable: false,
});
remainingAccounts.push({
pubkey: basketState.compositionOracle2[i],
isSigner: false,
isWritable: false,
});
// Create instruction if we've hit the max accounts limit
if (remainingAccounts.length + 1 >= MAX_PRICE_UPDATES_PER_TX) {
ixs.push(await createUpdatePricesIx(program, basket, remainingAccounts, tokenPrices));
remainingAccounts = [];
tokenPrices = [];
}
}
// Create final instruction if there are remaining accounts
if (remainingAccounts.length > 0) {
ixs.push(await createUpdatePricesIx(program, basket, remainingAccounts, tokenPrices));
}
return {
ixs,
luts: [basketState.lookupTable1, basketState.lookupTable2],
};
}