UNPKG

@symmetry-hq/baskets-sdk

Version:

Software Development Kit for interacting with Symmetry Baskets Program

843 lines (794 loc) 32 kB
import { AddressLookupTableAccount, ComputeBudgetProgram, Keypair, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY, SYSVAR_RENT_PUBKEY, SystemProgram, TransactionInstruction, VersionedTransaction } from "@solana/web3.js"; import { BUY_FEE_ACCOUNT, BUY_FEE_WALLET, CREATE_FEE_ACCOUNT, CreateBasketParams, BASKETS_PROGRAM_ID, BASKETS_PROGRAM_PDA, FilterTime, FilterType, SortBy, TOKEN_LIST_ADDRESS, TOKEN_STATS_ADDRESS, TokenSettings, WeightTime, WeightType, ADDITIONAL_UNITS, SimpleCreateParams, SimpleEditParams, REBALANCE_FEE_ACCOUNT, REBALANCE_FEE_WALLET, TransactionToSend } from "./config"; import { MD5 } from "crypto-js"; import { BN, Program, Wallet } from "@coral-xyz/anchor"; import { BasketsIDL, IDL } from "./basketsIDL"; import { validateCreateBasketParams } from "./utils"; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "./splTokenHelpers"; import { Basket } from "./basketState"; import { BuyState } from "./buyState"; import { Metaplex } from "@metaplex-foundation/js"; import { FetchSignaturesMultiResponse, PullFeed } from "@switchboard-xyz/on-demand"; export async function buildCreateBasketIx( program: Program<BasketsIDL>, tokenList: TokenSettings[], basketParams: SimpleCreateParams, ): Promise<TransactionInstruction> { if (basketParams.composition.length == 0) throw new Error("Empty composition"); if (!basketParams.feeDelegate) basketParams.feeDelegate = basketParams.manager; let assetPool = [0]; let rules = []; let weightSum = 0; basketParams.composition.map(info => weightSum += info.weight); for (let i = 0; i < basketParams.composition.length; i++) { let tokenSettings = tokenList.find( token => token.tokenMint == basketParams.composition[i].token.toBase58() ); if (!tokenSettings) throw new Error("Token not supported."); if (tokenSettings.id != 0) assetPool.push(tokenSettings.id); rules.push({ filterBy: FilterType.Fixed, filterDays: FilterTime.Day, sortBy: SortBy.DescendingOrder, totalWeight: Math.floor((basketParams.composition[i].weight * 1000) / weightSum), fixedAsset: tokenSettings.id, numAssets: 1, weightBy: WeightType.Fixed, weightDays: WeightTime.Day, weightExpo: 0, excludeAssets: [], ruleAssets: [], }) } let validateBasketParams: CreateBasketParams = { ...basketParams, name: basketParams.name, symbol: basketParams.symbol, uri: basketParams.uri, manager: PublicKey.default, hostPlatform: PublicKey.default, hostPlatformFee: 0, activelyManaged: 1, assetPool: assetPool, rules: rules, refilterInterval: 7 * 24 * 3600, reweightInterval: 24 * 3600 } await validateCreateBasketParams(validateBasketParams, tokenList, undefined, true); let tokens = rules.map(info => info.fixedAsset); let weights = rules.map(info => new BN(info.totalWeight)); while (tokens.length < 15) tokens.push(0); while (weights.length < 15) weights.push(new BN(0)); let message = basketParams.name + basketParams.symbol + basketParams.uri; let md5 = Array.from(Buffer.from(MD5(message).toString(), "hex")); let seedPubkey = Keypair.generate().publicKey; let [basketToken] = PublicKey.findProgramAddressSync( [Buffer.from("mint"), seedPubkey.toBuffer()], BASKETS_PROGRAM_ID ); let [basketState] = PublicKey.findProgramAddressSync( [Buffer.from("basket"), seedPubkey.toBuffer()], BASKETS_PROGRAM_ID ); if (basketParams.symbol.length < 3) throw new Error("Wrong symbol format"); if (basketParams.name.length < 3) throw new Error("Wrong name format"); const metaplex = Metaplex.make(program.provider.connection); const metadataAccount = metaplex.nfts().pdas().metadata({ mint: basketToken }); let createInstruction = await program.methods .createBasket( basketParams.managerFee, basketParams.hostPlatformFee, basketParams.activelyManaged, new BN(basketParams.rebalanceInterval), basketParams.rebalanceThreshold, basketParams.rebalanceSlippage, basketParams.lpOffsetThreshold, [basketParams.disableRebalance? 1 : 0, basketParams.disableLp? 1 : 0], basketParams.composition.length, tokens, weights.map(x => x.toNumber()), { name: basketParams.name, symbol: basketParams.symbol, uri: basketParams.uri, } ) .accounts({ manager: basketParams.manager, tokenList: TOKEN_LIST_ADDRESS, fundState: basketState, pdaAccount: BASKETS_PROGRAM_PDA, fundToken: basketToken, createFeeSweeper: CREATE_FEE_ACCOUNT, metadataAccount: metadataAccount, metadataProgram: new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"), systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: SYSVAR_RENT_PUBKEY, host: basketParams.hostPlatform, feeDelegate: basketParams.feeDelegate, seedPubkey: seedPubkey, }) .instruction(); return createInstruction; } export async function buildEditBasketIx( program: Program<BasketsIDL>, tokenList: TokenSettings[], basketObj: Basket, basketParams: SimpleEditParams, ): Promise<TransactionInstruction> { if (basketParams.composition.length == 0) throw new Error("Empty composition"); if (!basketParams.feeDelegate) basketParams.feeDelegate = basketObj.data.feeDelegate; if (!basketParams.composition) { basketParams.composition = []; for (let i = 0; i < basketObj.data.numRuleTokens.toNumber(); i++) if (basketObj.data.ruleTokenWeights[i].toNumber() != 0) basketParams.composition.push({ token: new PublicKey( tokenList[basketObj.data.ruleTokens[i].toNumber()].tokenMint ), weight: basketObj.data.ruleTokenWeights[i].toNumber() }) } let assetPool = [0]; let rules = []; let weightSum = 0; basketParams.composition.map(info => weightSum += info.weight); for (let i = 0; i < basketParams.composition.length; i++) { let tokenSettings = tokenList.find( //@ts-ignore token => token.tokenMint == basketParams.composition[i].token.toBase58() ); if (!tokenSettings) throw new Error("Token not supported."); if (tokenSettings.id != 0) assetPool.push(tokenSettings.id); rules.push({ filterBy: FilterType.Fixed, filterDays: FilterTime.Day, sortBy: SortBy.DescendingOrder, totalWeight: Math.floor((basketParams.composition[i].weight * 1000) / weightSum), fixedAsset: tokenSettings.id, numAssets: 1, weightBy: WeightType.Fixed, weightDays: WeightTime.Day, weightExpo: 0, excludeAssets: [], ruleAssets: [], }) } //@ts-ignore let validateBasketParams: CreateBasketParams = { ...basketParams, name: "", symbol: "", uri: "", manager: PublicKey.default, hostPlatform: PublicKey.default, hostPlatformFee: 0, activelyManaged: 1, assetPool: assetPool, rules: rules, refilterInterval: 7 * 24 * 3600, reweightInterval: 24 * 3600 } await validateCreateBasketParams(validateBasketParams, tokenList, basketObj.ownAddress, true); let tokens = rules.map(info => info.fixedAsset); let weights = rules.map(info => new BN(info.totalWeight)); while (tokens.length < 15) tokens.push(0); while (weights.length < 15) weights.push(new BN(0)); return await program.methods .editBasket( basketParams.managerFee, new BN(basketParams.rebalanceInterval), basketParams.rebalanceThreshold, basketParams.rebalanceSlippage, (basketParams.lpOffsetThreshold), [basketParams.disableRebalance? 1 : 0, basketParams.disableLp? 1 : 0], rules.length, tokens, weights.map(x => x.toNumber()), ) .accounts({ fundState: basketObj.ownAddress, manager: basketObj.data.manager, tokenList: TOKEN_LIST_ADDRESS, feeDelegate: basketParams.feeDelegate, }) .instruction(); } export async function buildEditManagetIx( program: Program<BasketsIDL>, basketState: Basket, newManager: PublicKey, ): Promise<TransactionInstruction> { return await program.methods .editManager( newManager ) .accounts({ manager: basketState.data.manager, fundState: basketState.ownAddress, }) .instruction(); } export async function buildCloseBasketIx( program: Program<BasketsIDL>, basketState: Basket, ): Promise<TransactionInstruction> { return await program.methods.closeBasket() .accounts({ manager: basketState.data.manager, fundState: basketState.ownAddress, fundToken: basketState.data.fundToken, pdaAccount: BASKETS_PROGRAM_PDA, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID }) .instruction(); } export async function buildCreateMetadataIx( program: Program<BasketsIDL>, creator: PublicKey, basket: PublicKey, tokenMint: PublicKey, metadata: { symbol: string, name: string, uri: string } ): Promise<TransactionInstruction> { let {symbol, name, uri} = metadata; if (symbol.length < 3 || symbol.length > 10) throw new Error("Wrong symbol format"); if (name.length < 3 || name.length > 60) throw new Error("Wrong name format"); const metaplex = Metaplex.make(program.provider.connection); const metadataAccount = metaplex.nfts().pdas().metadata({ mint: tokenMint }); return await program.methods.createFundTokenMintMetadata({ name: name, symbol: symbol, uri: uri, }).accounts({ manager: creator, fundToken: tokenMint, fundState: basket, updateAuthority: BASKETS_PROGRAM_PDA, metadataAccount: metadataAccount, metadataProgram: new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"), systemProgram: SystemProgram.programId, rent: SYSVAR_RENT_PUBKEY, }).instruction(); } export async function buildUpdateMetadataIx( program: Program<BasketsIDL>, creator: PublicKey, basket: PublicKey, tokenMint: PublicKey, metadata: { symbol: string, name: string, uri: string } ): Promise<TransactionInstruction> { let {symbol, name, uri} = metadata; if (symbol.length < 3 || symbol.length > 10) throw new Error("Wrong symbol format"); if (name.length < 3 || name.length > 60) throw new Error("Wrong name format"); const metaplex = Metaplex.make(program.provider.connection); const metadataAccount = metaplex.nfts().pdas().metadata({ mint: tokenMint }); return await program.methods.updateFundTokenMintMetadata({ name: name, symbol: symbol, uri: uri, }).accounts({ manager: creator, fundToken: tokenMint, fundState: basket, updateAuthority: BASKETS_PROGRAM_PDA, metadataAccount: metadataAccount, metadataProgram: new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"), systemProgram: SystemProgram.programId, rent: SYSVAR_RENT_PUBKEY, }).instruction(); } export async function buildSetMetadataIx( program: Program<BasketsIDL>, basket: Basket, metadata: { symbol: string, name: string, uri: string } ): Promise<TransactionInstruction> { if (basket.data.symbolLength == 0) return await buildCreateMetadataIx( program, basket.data.manager, basket.ownAddress, basket.data.fundToken, metadata ); else return await buildUpdateMetadataIx( program, basket.data.manager, basket.ownAddress, basket.data.fundToken, metadata ); } export async function buildBuyBasketIx( program: Program<BasketsIDL>, tokenList: TokenSettings[], user: PublicKey, basketObj: Basket, amount: number, ): Promise<TransactionInstruction> { let buyerUsdcTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[0].tokenMint), user, true, ); let managerUsdcTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[0].tokenMint), basketObj.data.feeDelegate.toBase58() == PublicKey.default.toBase58() ? basketObj.data.manager : basketObj.data.feeDelegate, true, ); let hostUsdcTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[0].tokenMint), basketObj.data.hostPubkey, true, ); let buyerBasketTokenAccount = getAssociatedTokenAddressSync( basketObj.data.fundToken, user, true, ); let seedPubkey = Keypair.generate().publicKey; let [buyState] = PublicKey.findProgramAddressSync( [Buffer.from("buy"), seedPubkey.toBuffer()], BASKETS_PROGRAM_ID ); return await program.methods .buyFund(new BN(amount * 10 ** tokenList[0].decimals)) .accounts({ buyer: user, fundState: basketObj.ownAddress, fundToken: basketObj.data.fundToken, tokenList: TOKEN_LIST_ADDRESS, pdaAccount: BASKETS_PROGRAM_PDA, pdaUsdcAccount: new PublicKey(tokenList[0].pdaTokenAccount), buyerUsdcAccount: buyerUsdcTokenAccount, managerUsdcAccount: managerUsdcTokenAccount, smfFeeAccount: BUY_FEE_ACCOUNT, hostUsdcAccount: hostUsdcTokenAccount, buyerFundTokenAccount: buyerBasketTokenAccount, buyState: buyState, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: SYSVAR_RENT_PUBKEY, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, seedPubkey: seedPubkey, }) .instruction() } export async function buildClaimTokensFromBuyStateIxs( program: Program<BasketsIDL>, tokenList: TokenSettings[], feePayer: PublicKey, basketObj: Basket, buyStateObj: BuyState, ): Promise<TransactionInstruction[]> { let ixs: TransactionInstruction[] = []; for (let i = 0; i < buyStateObj.data.token.length; i++) { if (parseInt(buyStateObj.data.amountBought[i].toString()) == 0 && i != 0) continue; let tokenId = buyStateObj.data.token[i].toNumber(); let userTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[tokenId].tokenMint), buyStateObj.data.buyer, true, ); ixs.push( await program.methods .claimTokenFromBuyState(new BN(tokenId)) .accounts({ signer: feePayer, buyer: buyStateObj.data.buyer, fundState: basketObj.ownAddress, buyState: buyStateObj.ownAddress, tokenList: TOKEN_LIST_ADDRESS, buyerTokenAccount: userTokenAccount, pdaTokenAccount: new PublicKey(tokenList[tokenId].pdaTokenAccount), pdaAccount: BASKETS_PROGRAM_PDA, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, }) .instruction() ); } return ixs; } export async function buildMintFromBuyStateIx( program: Program<BasketsIDL>, tokenList: TokenSettings[], feePayer: PublicKey, basketObj: Basket, buyStateObj: BuyState, ): Promise<TransactionInstruction> { let accounts = []; for (let i = 0; i < basketObj.data.numOfTokens.toNumber(); i++) accounts.push({ pubkey: new PublicKey( tokenList[basketObj.data.currentCompToken[i].toNumber()].oracleAccount ), isSigner: false, isWritable: false, }) return await program.methods .mintFund() .accounts({ signer: feePayer, buyer: buyStateObj.data.buyer, fundState: basketObj.ownAddress, tokenList: TOKEN_LIST_ADDRESS, buyState: buyStateObj.ownAddress, oracleSol: new PublicKey(tokenList[1].oracleAccount), pdaAccount: BASKETS_PROGRAM_PDA, buyerFundTokenAccount: buyStateObj.data.buyerFundTokenAccount, fundToken: basketObj.data.fundToken, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID }) .remainingAccounts(accounts) .instruction(); } export async function buildBuyBasketWithMultipleTokensIx( program: Program<BasketsIDL>, tokenList: TokenSettings[], user: PublicKey, basketObj: Basket, contribution: {token: PublicKey, amount: number}[], ): Promise<TransactionInstruction> { let tokenMints = basketObj.data.currentCompToken.slice(0, basketObj.data.numOfTokens.toNumber()) .map(token => new PublicKey(tokenList[token.toNumber()].tokenMint)); tokenMints.push(basketObj.data.fundToken); let buyerAtas = tokenMints.map(token => getAssociatedTokenAddressSync(token, user, true) ); for (let i = 0; i < contribution.length; i++) { let inBasket = basketObj.data.currentCompToken.find(info => tokenList[info.toNumber()].tokenMint == contribution[i].token.toBase58() ); if (!inBasket) throw new Error("Token not present in the current compostion."); } let amountsBN: BN[] = []; let remainingAccounts = []; for (let i = 0; i < basketObj.data.numOfTokens.toNumber(); i++) { let tokenSettings = tokenList[basketObj.data.currentCompToken[i].toNumber()]; let tokenContribution = contribution.find(info => info.token.toBase58() == tokenSettings.tokenMint); let amount = (tokenContribution) ? tokenContribution.amount : 0; amountsBN.push(new BN(Math.floor(amount * 10 ** tokenSettings.decimals))); remainingAccounts.push({ pubkey: new PublicKey(tokenSettings.oracleAccount), isSigner: false, isWritable: false, }); remainingAccounts.push({ pubkey: buyerAtas[i], isSigner: false, isWritable: true, }); remainingAccounts.push({ pubkey: new PublicKey(tokenSettings.pdaTokenAccount), isSigner: false, isWritable: true, }); } let buyerBasketTokenAccount = buyerAtas[buyerAtas.length - 1]; while (amountsBN.length < 20) amountsBN.push(new BN(0)); return await program.methods .instantMint(amountsBN) .accounts({ authority: user, buyerFundTokenAccount: buyerBasketTokenAccount, fundToken: basketObj.data.fundToken, fundState: basketObj.ownAddress, tokenList: TOKEN_LIST_ADDRESS, pdaAccount: BASKETS_PROGRAM_PDA, oracleSol: new PublicKey(tokenList[1].oracleAccount), tokenProgram: TOKEN_PROGRAM_ID, }) .remainingAccounts(remainingAccounts) .instruction(); } export async function buildBuyBasketWithSingleTokenIx( program: Program<BasketsIDL>, tokenList: TokenSettings[], user: PublicKey, basketObj: Basket, contributionToken: PublicKey, contributionAmount: number, ): Promise<TransactionInstruction> { let tokenSettings = tokenList.find(info => info.tokenMint == contributionToken.toBase58() ); if (!tokenSettings || !basketObj.data.currentCompToken.find(x => x.toNumber() == tokenSettings?.id)) throw new Error("Token not present in the current compostion."); let buyerTokenAccount = getAssociatedTokenAddressSync(contributionToken, user, true); let buyerBasketTokenAccount = getAssociatedTokenAddressSync(basketObj.data.fundToken, user, true); let symmetryFeeAccount = getAssociatedTokenAddressSync(contributionToken, BUY_FEE_WALLET); let hostFeeAccount = getAssociatedTokenAddressSync(contributionToken, basketObj.data.hostPubkey, true); let managerFeeAccount = getAssociatedTokenAddressSync(contributionToken, basketObj.data.manager, true); let remainingAccounts = []; for (let i = 0; i < basketObj.data.numOfTokens.toNumber(); i++) { let tokenSettings = tokenList[basketObj.data.currentCompToken[i].toNumber()]; remainingAccounts.push({ pubkey: new PublicKey(tokenSettings.oracleAccount), isSigner: false, isWritable: false, }); } return await program.methods .singleTokenDeposit(tokenSettings?.id, new BN(contributionAmount * 10 ** tokenSettings.decimals)) .accounts({ authority: user, fundState: basketObj.ownAddress, fundToken: basketObj.data.fundToken, buyerTokenAccount: buyerTokenAccount, buyerFundTokenAccount: buyerBasketTokenAccount, pdaAccount: BASKETS_PROGRAM_PDA, pdaTokenAccount: new PublicKey(tokenSettings.pdaTokenAccount), symmetryFeeAccount: symmetryFeeAccount, hostFeeAccount: hostFeeAccount, managerFeeAccount: managerFeeAccount, oracleAccount: new PublicKey(tokenSettings.oracleAccount), oracleSol: new PublicKey(tokenList[1].oracleAccount), tokenList: TOKEN_LIST_ADDRESS, tokenProgram: TOKEN_PROGRAM_ID, }) .remainingAccounts(remainingAccounts) .instruction(); } export async function buildSellBasketToSingleTokenIx( program: Program<BasketsIDL>, tokenList: TokenSettings[], user: PublicKey, basketObj: Basket, withdrawToken: PublicKey, amount: number, ): Promise<TransactionInstruction> { let tokenSettings = tokenList.find(info => info.tokenMint == withdrawToken.toBase58()); if (!tokenSettings || !basketObj.data.currentCompToken.find(x => x.toNumber() == tokenSettings?.id)) throw new Error("Token not present in the current compostion."); let userBasketTokenAccount = getAssociatedTokenAddressSync(basketObj.data.fundToken, user, true); let userTokenAccount = getAssociatedTokenAddressSync(withdrawToken, user, true); let remainingAccounts = []; for (let i = 0; i < basketObj.data.numOfTokens.toNumber(); i++) { let tokenSettings = tokenList[basketObj.data.currentCompToken[i].toNumber()]; remainingAccounts.push({ pubkey: new PublicKey(tokenSettings.oracleAccount), isSigner: false, isWritable: false, }); } return await program.methods .instantBurn( new BN(amount * 10 ** 6), tokenSettings.id, ) .accounts({ seller: user, pdaAccount: BASKETS_PROGRAM_PDA, fundState: basketObj.ownAddress, fundToken: basketObj.data.fundToken, sellerFundTokenAccount: userBasketTokenAccount, withdrawTokenMint: withdrawToken, sellerTokenAccount: userTokenAccount, pdaTokenAccount: new PublicKey(tokenSettings?.pdaTokenAccount), tokenList: TOKEN_LIST_ADDRESS, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, }) .remainingAccounts(remainingAccounts) .instruction() } export async function buildSellBasketIx( program: Program<BasketsIDL>, user: PublicKey, basketObj: Basket, amount: number, rebalance: number, ): Promise<TransactionInstruction> { let userBasketTokenAccount = getAssociatedTokenAddressSync( basketObj.data.fundToken, user, true, ); let seedPubkey = Keypair.generate().publicKey; let [sellState] = PublicKey.findProgramAddressSync( [Buffer.from("sell"), seedPubkey.toBuffer()], BASKETS_PROGRAM_ID ); return await program.methods .sellFund( new BN(amount * 10 ** 6), new BN(rebalance), ) .accounts({ seller: user, fundState: basketObj.ownAddress, pdaAccount: BASKETS_PROGRAM_PDA, newFundState: sellState, sellerFundTokenAccount: userBasketTokenAccount, fundToken: basketObj.data.fundToken, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: SYSVAR_RENT_PUBKEY, seedPubkey: seedPubkey, }) .instruction() } export async function buildClaimTokensFromSellStateIxs( program: Program<BasketsIDL>, tokenList: TokenSettings[], feePayer: PublicKey, basketObj: Basket, ): Promise<TransactionInstruction[]> { let basketOwner = basketObj.data.manager; let ixs: TransactionInstruction[] = []; for (let i = 0; i < basketObj.data.numOfTokens.toNumber(); i++) { if (parseInt(basketObj.data.currentCompAmount[i].toString()) == 0 && i != 0) continue; let tokenId = basketObj.data.currentCompToken[i].toNumber(); let userTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[tokenId].tokenMint), basketOwner, true, ); ixs.push( await program.methods .claimToken(new BN(tokenId)) .accounts({ signer: feePayer, manager: basketOwner, fundState: basketObj.ownAddress, tokenList: TOKEN_LIST_ADDRESS, sellerTokenAccount: userTokenAccount, pdaTokenAccount: new PublicKey(tokenList[tokenId].pdaTokenAccount), pdaAccount: BASKETS_PROGRAM_PDA, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, }) .instruction() ); } return ixs; } export async function buildUpdateCurrentWeightsIx( program: Program<BasketsIDL>, basket: Basket, tokenList: TokenSettings[], ): Promise<TransactionInstruction> { let accounts = []; for (let i = 0; i < basket.data.numOfTokens.toNumber(); i++) accounts.push({ pubkey: new PublicKey( tokenList[basket.data.currentCompToken[i].toNumber()].oracleAccount ), isSigner: false, isWritable: false, }) return await program.methods .updateCurrentWeights() .accounts({ fundState: basket.ownAddress, tokenList: TOKEN_LIST_ADDRESS, oracleSol: new PublicKey(tokenList[1].oracleAccount), }) .remainingAccounts(accounts) .instruction(); } export async function buildWithdrawBeforeRebalanceIx( program: Program<BasketsIDL>, feePayer: PublicKey, basket: Basket, tokenList: TokenSettings[], fromToken: number, toToken: number, fromAmount: number, ): Promise<TransactionInstruction> { return await program.methods.withdrawBeforeRebalance( fromToken, toToken, new BN(fromAmount) ).accounts({ signer: feePayer, basketState: basket.ownAddress, tokenList: TOKEN_LIST_ADDRESS, oracleSol: new PublicKey(tokenList[1].oracleAccount), oracleTokenFrom: new PublicKey(tokenList[fromToken].oracleAccount), oracleTokenTo: new PublicKey(tokenList[toToken].oracleAccount), pdaAccount: BASKETS_PROGRAM_PDA, pdaTokenFrom: new PublicKey(tokenList[fromToken].pdaTokenAccount), pdaTokenTo: new PublicKey(tokenList[toToken].pdaTokenAccount), signerTokenFrom: getAssociatedTokenAddressSync(new PublicKey(tokenList[fromToken].tokenMint), feePayer, true), signerTokenTo: getAssociatedTokenAddressSync(new PublicKey(tokenList[toToken].tokenMint), feePayer, true), fromTokenMint: new PublicKey(tokenList[fromToken].tokenMint), toTokenMint: new PublicKey(tokenList[toToken].tokenMint), rebalanceState: PublicKey.findProgramAddressSync([Buffer.from("flashrebalance")], BASKETS_PROGRAM_ID)[0], tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, rent: SYSVAR_RENT_PUBKEY, instructionsSysvar: SYSVAR_INSTRUCTIONS_PUBKEY, }).instruction(); } export async function buildDepositAfterRebalanceIx( program: Program<BasketsIDL>, feePayer: PublicKey, basket: Basket, tokenList: TokenSettings[], toToken: number, ) { return await program.methods.depositAfterRebalance().accounts({ signer: feePayer, basketState: basket.ownAddress, tokenList: TOKEN_LIST_ADDRESS, oracleSol: new PublicKey(tokenList[1].oracleAccount), oracleTokenTo: new PublicKey(tokenList[toToken].oracleAccount), pdaAccount: BASKETS_PROGRAM_PDA, pdaTokenTo: new PublicKey(tokenList[toToken].pdaTokenAccount), signerTokenTo: getAssociatedTokenAddressSync(new PublicKey(tokenList[toToken].tokenMint), feePayer, true), rebalanceState: PublicKey.findProgramAddressSync([Buffer.from("flashrebalance")], BASKETS_PROGRAM_ID)[0], rebalanceFeeAccount: getAssociatedTokenAddressSync(new PublicKey(tokenList[toToken].tokenMint), REBALANCE_FEE_WALLET, true), tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, }).instruction(); } export async function getSwbFeedsUpdateIxs( program: Program, payer: PublicKey, feeds : PublicKey[], numSignatures: number = 2, ): Promise<[ TransactionInstruction, AddressLookupTableAccount[], FetchSignaturesMultiResponse ][]> { let maxUpdates = Math.floor(18 / numSignatures); let ixPromises = []; for (let i = 0; i < feeds.length; i += maxUpdates) { let promise = PullFeed.fetchUpdateManyIx(program, { feeds: feeds.slice(i, i + maxUpdates), numSignatures: numSignatures, payer: payer, }); ixPromises.push(promise); } return await Promise.all(ixPromises); } export async function updateOraclesTxs( swbProgram: Program, payer: PublicKey, feeds: PublicKey[], lamports: number, ): Promise<TransactionToSend[]> { let res = await getSwbFeedsUpdateIxs( swbProgram, payer, feeds, ).catch((e) => { console.log("Couldn't fetch SWB oracle update txs", e.message); return []; }); return res.map(update => { return { payerKey: payer, instructions: [ update[0], ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ], lookupTables: update[1] } }) }