UNPKG

@symmetry-hq/baskets-sdk

Version:

Software Development Kit for interacting with Symmetry Baskets Program

974 lines (907 loc) 35.3 kB
import { AddressLookupTableAccount, ComputeBudgetProgram, Connection, GetProgramAccountsFilter, Keypair, PublicKey, SystemProgram, Transaction, TransactionInstruction, TransactionMessage, TransactionSignature, VersionedTransaction, } from "@solana/web3.js"; import { AnchorProvider, Program, Wallet, BN, Idl, } from "@coral-xyz/anchor"; import { IDL, BasketsIDL } from "./basketsIDL"; import { Basket } from "./basketState"; import { CreateBasketParams, DATABASE_ADDESS, FilterOption, BasketError, BASKETS_PROGRAM_ID, BASKETS_PROGRAM_PDA, TokenSettings, TOKEN_LIST_ADDRESS, ADDITIONAL_FEE, SimpleEditParams, SimpleCreateParams, Side, ADDITIONAL_UNITS, SWB_PID, BASKETS_LOOKUP_TABLE_1, BASKETS_LOOKUP_TABLE_2, TransactionToSend, } from "./config"; import { confirmTransaction, delay, fetchTokenList, generateJupSwapInstruction, generateJupTxData, getAddressLookupTableAccounts, getCurrentComposition, getFilteredProgramAccounts, getOraclePrices, sendSignedTransactions, signTransactionsWithWallet, signVersionedTransactions, tryMetadata } from "./utils"; import { BuyState } from "./buyState"; import { simulate } from "./simulation"; import { TOKEN_PROGRAM_ID } from "./splTokenHelpers"; import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; import { buildDepositAfterRebalanceIx, buildUpdateCurrentWeightsIx, buildWithdrawBeforeRebalanceIx, updateOraclesTxs } from "./instructionsBuilder"; import { buildRebalanceTransactions, calculateRebalanceAmounts, getConfirmedTimestamp, getFlashRebalanceInfo, getJupiterSwapData, getLookupTableAccount, getSortedRebalanceInfo, isForceRebalanceNeeded, shouldProcessRebalance, signAndSendTransactions } from "./flashRebalance"; export * as BasketInstructions from "./basketInstructions"; export * from "./config"; export * from "./basketState"; export * from "./buyState"; export * from "./utils"; export * from "./basketsIDL"; export class BasketsSDK { private connection: Connection; private program: Program<BasketsIDL>; private swbProgram: Program; private swbIDL: Idl; private tokenList: TokenSettings[]; private lookups: AddressLookupTableAccount[]; private wallet?: Wallet; private lamports: number; private jupAPIkey: string = "https://quote-api.jup.ag/v6/"; constructor( connection: Connection, program: Program<BasketsIDL>, swbProgram: Program, swbIDL: Idl, tokenList: TokenSettings[], lookups: AddressLookupTableAccount[], wallet?: Wallet ) { this.connection = connection; this.wallet = wallet; this.program = program; this.swbProgram = swbProgram; this.swbIDL = swbIDL; this.tokenList = tokenList; this.lamports = ADDITIONAL_FEE; this.lookups = lookups; } static async init( connection: Connection, wallet?: Wallet, ): Promise<BasketsSDK> { let provider = new AnchorProvider( connection, wallet ? wallet : new NodeWallet(Keypair.generate()), { skipPreflight: true, preflightCommitment: "recent", commitment: "processed", } ); // const symmetryEngineIDL: BasketsIDL = (await Program.fetchIdl(BASKETS_PROGRAM_ID, provider))!; let program = new Program<BasketsIDL>(IDL, provider); let swbIDL = (await Program.fetchIdl(SWB_PID, provider))!; let swbProgram = new Program(swbIDL, provider); let tokenList = await fetchTokenList(program); let lookups = await Promise.all([ getLookupTableAccount(program.provider.connection, BASKETS_LOOKUP_TABLE_1), getLookupTableAccount(program.provider.connection, BASKETS_LOOKUP_TABLE_2) ]); return new BasketsSDK( connection, program, swbProgram, swbIDL, tokenList, lookups, wallet, ) } setWallet(wallet: Wallet) { this.wallet = wallet; let provider = new AnchorProvider( this.connection, wallet, { skipPreflight: true, preflightCommitment: "recent", commitment: "recent", } ) this.program = new Program<BasketsIDL>(IDL, provider); this.swbProgram = new Program(this.swbIDL, provider); } setPriorityFee(lamports: number) { this.lamports = lamports; } setJupAPIKey(apiKey: string) { this.jupAPIkey = apiKey; } async loadFromPubkey(pubkey: PublicKey): Promise<Basket> { return await Basket.loadFromPubkey( this.program, pubkey ); } async getCurrentCompositions(baskets: Basket[]): Promise<any> { let oraclePriceData = await getOraclePrices(this.program, this.tokenList); let parsed = baskets.map(basket => getCurrentComposition(basket, this.tokenList, oraclePriceData)); await Promise.all(parsed.map(x => tryMetadata(x))).then(metadatas => { for (let i = 0; i < parsed.length; i++) parsed[i].metadata = metadatas[i]; }) return parsed; } async findBaskets(filters: FilterOption[]): Promise<Basket[]> { let accountFilters: GetProgramAccountsFilter[] = [{dataSize: 10208}]; accountFilters.push({memcmp: { offset: 112, bytes: "11111111" }}) accountFilters = [...accountFilters, ...filters.map(filter => { return { memcmp: { offset: (filter.filterType == "host") ? 128 : 16, bytes: filter.filterPubkey.toBase58(), } } })]; let accounts = await getFilteredProgramAccounts( this.connection, accountFilters ).catch((e) => {console.log(e); return [];}); return accounts .map(account => Basket.loadFromRawData(this.program, account)) } async findActiveSellStates(user: PublicKey): Promise<Basket[]> { let accounts = await getFilteredProgramAccounts( this.connection, [ { dataSize: 10208 }, { memcmp: { offset: 16, bytes: user.toBase58() }}, { memcmp: { offset: 112, bytes: "Ahg1opVcGX", }}, ] ); return accounts .map(account => Basket.loadFromRawData(this.program, account)) } async findActiveBuyStates(user: PublicKey): Promise<BuyState[]> { let accounts = await getFilteredProgramAccounts( this.connection, [ { dataSize: 680 }, { memcmp: { offset: 40, bytes: user.toBase58() }} ] ); return await BuyState.loadMultiple(this.program, accounts); } async fetchAllBuyStates(filters: FilterOption[]): Promise<BuyState[]> { let accountFilters: GetProgramAccountsFilter[] = [{ dataSize: 680 }]; accountFilters = [...accountFilters, ...filters.map(filter => { return { memcmp: { offset: (filter.filterType == "host") ? 104 : 72, bytes: filter.filterPubkey.toBase58(), } } })]; let accounts = await getFilteredProgramAccounts( this.connection, accountFilters ); return await BuyState.loadMultiple(this.program, accounts); } async fetchBuyStateFromPubkey(pubkey: PublicKey): Promise<BuyState> { return await BuyState.loadFromPubkey(this.program, pubkey); } async fetchAllSellStates(filters: FilterOption[]): Promise<Basket[]> { let accountFilters: GetProgramAccountsFilter[] = [{ dataSize: 10208 }]; accountFilters.push({memcmp: { offset: 112, bytes: "Ahg1opVcGX" }}) accountFilters = [...accountFilters, ...filters.map(filter => { return { memcmp: { offset: (filter.filterType == "host") ? 128 : 16, bytes: filter.filterPubkey.toBase58(), } } })]; let accounts = await getFilteredProgramAccounts( this.connection, accountFilters ); return accounts .map(account => Basket.loadFromRawData(this.program, account)) } async fetchAllHoldings(user: PublicKey): Promise<{basketAddress: string, mint: string, balance: number, basketData: any}[]> { let tokenAccountsRaw = await this.connection.getTokenAccountsByOwner( new PublicKey(user), { programId: new PublicKey(TOKEN_PROGRAM_ID) }, "processed" ); let tokenAccountsDecoded = tokenAccountsRaw.value.map(account => { return { mint: new PublicKey(account.account.data.slice(0,32)), balance: parseInt(new BN(account.account.data.slice(64, 72), "le").toString()), } }); let basketsRaw = await getFilteredProgramAccounts( this.connection, [ {dataSize: 10208}, { memcmp: { offset: 112, bytes: "11111111" } } ] ); let basketsDecoded = basketsRaw.map(account => { return { decoded: this.program.coder.accounts.decode("fundState", account.account.data), pubkey: account.pubkey } }); let basketHoldings = []; for (let i = 0; i < basketsDecoded.length; i++) { let ataIndex = tokenAccountsDecoded .findIndex(x => x.mint.toBase58() == basketsDecoded[i].decoded.fundToken.toBase58()); if (ataIndex == -1) continue; basketHoldings.push({ basketAddress: basketsDecoded[i].pubkey.toBase58(), mint: basketsDecoded[i].decoded.fundToken.toBase58(), balance: tokenAccountsDecoded[ataIndex].balance / 10 ** 6, basketData: basketsDecoded[i].decoded, }) } return basketHoldings; } getTokenListData(): TokenSettings[] { return this.tokenList.filter(x => x.isLive == true); } tokenIdFromMint(tokenMint: string): number { for (let i = 0; i < this.tokenList.length; i++) if (this.tokenList[i].tokenMint == tokenMint) return this.tokenList[i].id; throw new BasketError("Token not supported by Symmetry Engine") } async createBasket(createBasketParams: SimpleCreateParams): Promise<Basket> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await Basket.create( this.program, this.connection, this.wallet, this.tokenList, this.lookups, createBasketParams, this.lamports, ) } async simulateBasket(createBasketParams: CreateBasketParams, simulationDays: number) { return await simulate( this.program, DATABASE_ADDESS, TOKEN_LIST_ADDRESS, createBasketParams, simulationDays ) } async editBasket(basket: Basket, editBasketParams: SimpleEditParams): Promise<TransactionSignature> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await basket.edit( this.program, this.connection, this.wallet, this.tokenList, this.lookups, editBasketParams, this.lamports, ) } async setMetadata(basket: Basket, symbol: string, name: string, uri: string = ""): Promise<TransactionSignature> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await basket.setMetaData( this.program, this.wallet, symbol, name, uri, this.lamports, ) } async editManager( basket: Basket, newManager: PublicKey, ): Promise<TransactionSignature> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await basket.editManager( this.program, this.connection, this.wallet, newManager, this.lamports, ) } // async simpleEditBasket( // basket: Basket, // basketParams: SimpleEditParams, // ): Promise<TransactionSignature[]> { // if (!this.wallet) // throw new BasketError("Wallet not provided"); // return await basket.simpleEdit( // this.program, // this.connection, // this.wallet, // this.tokenList, // basketParams, // this.lamports, // ) // } async closeBasket(basket: Basket): Promise<TransactionSignature> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await basket.close(this.program, this.wallet, this.lamports); } async refilterBasketInstruction(basket: Basket): Promise<TransactionInstruction> { //@ts-ignore return await refilterInstruction(this.program, basket.ownAddress, this.wallet?.publicKey); } // async refilterBasket(basket: Basket): Promise<TransactionSignature> { // if (!this.wallet) // throw new BasketError("Wallet not provided"); // return await basket.refilter( // this.program, // this.wallet, // this.lamports, // ); // } async reweightBasketInstruction(basket: Basket): Promise<TransactionInstruction> { //@ts-ignore return await reweightInstruction(this.program, basket.ownAddress, this.wallet?.publicKey); } // async reweightBasket(basket: Basket): Promise<TransactionSignature> { // if (!this.wallet) // throw new BasketError("Wallet not provided"); // return await basket.reweight( // this.program, // this.wallet, // this.lamports, // ); // } // async rebalanceBasketToken( // basket: Basket, // token: PublicKey, // updateOracles: boolean = true, // ): Promise<TransactionSignature> { // let oraclePriceData = await getOraclePrices(this.program, this.tokenList); // //@ts-ignore // let tokenSettings: TokenSettings = this.tokenList.find(x => x.tokenMint == token.toBase58()); // if (!this.wallet) // throw new BasketError("Wallet not provided"); // if (!tokenSettings) // throw new BasketError("Token not supported"); // let timestamp = await this.connection.getBlockTime( // await this.connection.getSlot("finalized") // ).catch((e) => null); // let rebalanceInfos = basket.getRebalanceInfo( // this.program, // this.tokenList, // oraclePriceData, // timestamp ? timestamp : 2000000000, // (this.wallet.publicKey.equals(basket.data.manager) && basket.data.activelyManaged.toNumber() == 1) // ); // let rebalanceInfo = rebalanceInfos.find(info => info.tokenId == tokenSettings.id); // if (!rebalanceInfo) // throw new BasketError("Token not in composition"); // let jupSwapData = await generateJupSwapInstruction( // rebalanceInfo, // basket.data.rebalanceSlippage.toNumber(), // this.connection, // rebalanceInfo.side == Side.To ? // oraclePriceData[rebalanceInfo.tokenId] / 10 ** this.tokenList[rebalanceInfo.tokenId].decimals : // oraclePriceData[0] / 10 ** this.tokenList[0].decimals, // rebalanceInfo.side == Side.From ? // oraclePriceData[rebalanceInfo.tokenId] / 10 ** this.tokenList[rebalanceInfo.tokenId].decimals : // oraclePriceData[0] / 10 ** this.tokenList[0].decimals, // rebalanceInfo.tokenId == 48 ? this.tokenList[5].tokenMint : this.tokenList[1].tokenMint, // rebalanceInfo.tokenId == 48 ? this.tokenList[5].pdaTokenAccount : this.tokenList[1].pdaTokenAccount, // this.jupAPIkey, // ).catch((e) => {console.log("JUP SWAP DATA", e.message); return null;}) // if (!jupSwapData) // throw new BasketError("Couldn't load quote."); // return await basket.rebalanceSingle( // this.program, // this.wallet, // this.tokenList, // jupSwapData, // rebalanceInfo, // this.lamports, // updateOracles // ) // } async rebalanceBasket(basket: Basket, updateOracles: boolean = true): Promise<TransactionSignature[]> { if (!this.wallet) throw new BasketError("Wallet not provided"); let [oraclePriceData, timestamp, updateOraclesTxData] = await Promise.all([ getOraclePrices(this.program, this.tokenList), getConfirmedTimestamp(this.connection, basket), updateOracles ? updateOraclesTxs( this.swbProgram, this.wallet.publicKey, basket.getSwbFeeds(this.tokenList), this.lamports, ) : [] ]); let rebalanceInfos = basket.getRebalanceInfo( this.program, this.tokenList, oraclePriceData, timestamp ? timestamp : 2000000000, (this.wallet.publicKey.equals(basket.data.manager) && basket.data.activelyManaged.toNumber() == 1) ); let jupSwapDatas = await Promise.all( rebalanceInfos.map(rebalanceInfo => generateJupSwapInstruction( rebalanceInfo, basket.data.rebalanceSlippage.toNumber(), this.connection, rebalanceInfo.side == Side.To ? oraclePriceData[rebalanceInfo.tokenId] / 10 ** this.tokenList[rebalanceInfo.tokenId].decimals : oraclePriceData[0] / 10 ** this.tokenList[0].decimals, rebalanceInfo.side == Side.From ? oraclePriceData[rebalanceInfo.tokenId] / 10 ** this.tokenList[rebalanceInfo.tokenId].decimals : oraclePriceData[0] / 10 ** this.tokenList[0].decimals, rebalanceInfo.tokenId == 48 ? this.tokenList[5].tokenMint : this.tokenList[1].tokenMint, rebalanceInfo.tokenId == 48 ? this.tokenList[5].pdaTokenAccount : this.tokenList[1].pdaTokenAccount, this.jupAPIkey, ).catch((e) => {console.log("JUP SWAP DATA", e.message); return null;}) ) ); let txsToSend: TransactionToSend[] = updateOraclesTxData; let updates = txsToSend.length; await basket.rebalanceTo( this.program, this.wallet, this.tokenList, //@ts-ignore jupSwapDatas, rebalanceInfos, this.lookups, this.lamports, ).then(result => txsToSend.push(...result)); let toSwaps = txsToSend.length - updates; if (basket.data.sellState.toNumber() == 0) await basket.rebalanceFrom( this.program, this.wallet, this.tokenList, //@ts-ignore jupSwapDatas, rebalanceInfos, this.lookups, this.lamports, ).then(result => txsToSend.push(...result)); let blockhash = (await this.connection.getLatestBlockhash("confirmed")).blockhash; let signedTransactions = await signVersionedTransactions( this.wallet, txsToSend.map(tx => new VersionedTransaction( new TransactionMessage({ payerKey: tx.payerKey, recentBlockhash: blockhash, instructions: tx.instructions, }).compileToV0Message(tx.lookupTables) )) ); let updateTxs = await sendSignedTransactions( this.connection, signedTransactions.slice(0, updates), 1, ); let rebalanceTo = await sendSignedTransactions( this.connection, signedTransactions.slice(updates, updates + toSwaps), 0, ); let rebalanceFrom = await sendSignedTransactions( this.connection, signedTransactions.slice(updates + toSwaps, signedTransactions.length), 0, ); let resultTxs = [...updateTxs, ...rebalanceFrom, ...rebalanceTo]; if (resultTxs.length > 0) await confirmTransaction(this.connection, resultTxs[resultTxs.length - 1]).catch(() => {}); return resultTxs; } async updateAllSwbOracles() { if (!this.wallet) { throw new BasketError("Wallet not provided"); } let feeds: PublicKey[] = []; for (let i = 0; i < this.tokenList.length; i++) if (this.tokenList[i].oracleType == "SwbOnDemand") feeds.push(new PublicKey(this.tokenList[i].oracleAccount)); let txsToSend = await updateOraclesTxs( this.swbProgram, this.wallet.publicKey, feeds, this.lamports, ); return signAndSendTransactions(txsToSend, this.connection, this.wallet, 0); } async rebalanceCronJob( basket: Basket, updateOracles: boolean = true, maxAllowedAccounts: number = 45, softCap: number = 5, hardCap: number = 5000, underTokens: number = 3, overTokens: number = 3, ): Promise<TransactionSignature[]> { if (!this.wallet) { throw new BasketError("Wallet not provided"); } const [oraclePriceData, timestamp, updateOraclesTxData] = await Promise.all([ getOraclePrices(this.program, this.tokenList), getConfirmedTimestamp(this.connection, basket), updateOracles ? updateOraclesTxs( this.swbProgram, this.wallet.publicKey, basket.getSwbFeeds(this.tokenList), this.lamports, ) : [] ]); const forceRebalance = isForceRebalanceNeeded(basket, this.wallet.publicKey); const rebalanceInfos = getSortedRebalanceInfo(basket, oraclePriceData, timestamp, this.tokenList); const txsToSend = await buildRebalanceTransactions( basket, rebalanceInfos, oraclePriceData, forceRebalance, this.lookups, maxAllowedAccounts, this.wallet.publicKey, this.connection, this.program, this.tokenList, this.lamports, updateOraclesTxData, softCap, hardCap, underTokens, overTokens, this.jupAPIkey, ); return signAndSendTransactions(txsToSend, this.connection, this.wallet, updateOracles == true ? 1 : 0); } async filterCronJobBaskets(allBaskets: Basket[], softCap: number = 10): Promise<Basket[]> { const timestamp = Date.now() / 1000; const oraclePriceData = await getOraclePrices(this.program, this.tokenList); let basketsToRebalance = []; for (let i = 0; i < allBaskets.length; i++) { let basket = allBaskets[i]; let possibleRebalances = 0; if (basket.data.disableRebalance.toNumber() == 1) continue; let rebalanceInfos = getSortedRebalanceInfo( basket, oraclePriceData, timestamp, this.tokenList ); for (const over of rebalanceInfos.over) { for (const under of rebalanceInfos.under) { if (!shouldProcessRebalance(over, under, false)) continue; const { from, to, tokenAmount, value } = calculateRebalanceAmounts( over, under, oraclePriceData, this.tokenList, 10000, ); if (value <= softCap) continue; possibleRebalances += 1; } } if (possibleRebalances > 0) basketsToRebalance.push(basket) } return basketsToRebalance; } async buyBasket(basket: Basket, amountUsdc: number): Promise<BuyState> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await BuyState.createNew( this.program, this.wallet, this.tokenList, basket, amountUsdc, this.lamports, ) } async getOraclePrices(): Promise<number[]> { let oraclePrices = await getOraclePrices(this.program, this.tokenList); return oraclePrices; } async computeMintAmountWithMultipleTokens(basket: Basket, contribution: {token: PublicKey, amount: number}[]): Promise<number> { let oraclePrices = await getOraclePrices(this.program, this.tokenList); let amount = BuyState.computeMintAmountWithMultipleTokens( this.tokenList, basket, contribution, oraclePrices ); return amount; } // async buyBasketWithMultipleTokens( // basket: Basket, // contribution: {token: PublicKey, amount: number}[], // updateOracles: boolean = true, // ): Promise<TransactionSignature> { // if (!this.wallet) // throw new BasketError("Wallet not provided"); // return await BuyState.multipleTokensDeposit( // this.program, // this.wallet, // this.tokenList, // basket, // contribution, // this.lamports, // updateOracles, // ); // } async computeMintAmountWithSingleToken(basket: Basket, token: PublicKey, amount: number): Promise<number> { let oraclePrices = await getOraclePrices(this.program, this.tokenList); let tokenSettings = this.tokenList.find(x => x.tokenMint == token.toBase58()); if (!tokenSettings) throw new BasketError("Token not in composition"); let exp = BuyState.computeMintAmountWithSingleToken( this.tokenList, basket, tokenSettings, amount, oraclePrices ); return exp; } async buyWithSingleToken( basket: Basket, token: PublicKey, amount: number, updateOracles: boolean = true, ): Promise<TransactionSignature> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await BuyState.singleTokenDeposit( this.program, this.wallet, this.tokenList, basket, token, amount, this.lamports, updateOracles, ); } async rebalanceBuyState( buyState: BuyState, updateOracles: boolean = true, ): Promise<TransactionSignature[]> { if (!this.wallet) throw new BasketError("Wallet not provided"); let [oraclePriceData, updateOraclesTxData] = await Promise.all([ await getOraclePrices(this.program, this.tokenList), updateOracles ? updateOraclesTxs( this.swbProgram, this.wallet.publicKey, buyState.basket.getSwbFeeds(this.tokenList), this.lamports, ) : [] ]); let rebalanceInfos = buyState.getBuyStateRebalanceInfo(this.tokenList); let jupSwapDatas = await Promise.all( rebalanceInfos.map(rebalanceInfo => generateJupSwapInstruction( rebalanceInfo, buyState.basket.data.rebalanceSlippage.toNumber(), this.connection, rebalanceInfo.side == Side.To ? oraclePriceData[rebalanceInfo.tokenId] / 10 ** this.tokenList[rebalanceInfo.tokenId].decimals : oraclePriceData[0] / 10 ** this.tokenList[0].decimals, rebalanceInfo.side == Side.From ? oraclePriceData[rebalanceInfo.tokenId] / 10 ** this.tokenList[rebalanceInfo.tokenId].decimals : oraclePriceData[0] / 10 ** this.tokenList[0].decimals, rebalanceInfo.tokenId == 48 ? this.tokenList[5].tokenMint : this.tokenList[1].tokenMint, rebalanceInfo.tokenId == 48 ? this.tokenList[5].pdaTokenAccount : this.tokenList[1].pdaTokenAccount, this.jupAPIkey ).catch((e) => {console.log("JUP SWAP DATA", e.message); return null;}) ) ); return await buyState.rebalanceBuyState( this.program, this.wallet, this.tokenList, //@ts-ignore jupSwapDatas, this.lamports, updateOraclesTxData, this.lookups, ); } async mintBasket( buyState: BuyState, updateOracles: boolean = true, ): Promise<TransactionSignature[]> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await buyState.mint( this.program, this.swbProgram, this.wallet, this.tokenList, this.lookups, this.lamports, updateOracles, ); } // async sellBasketToSingleToken( // basket: Basket, // withdrawToken: PublicKey, // amount: number, // updateOracles: boolean = true, // ): Promise<TransactionSignature> { // if (!this.wallet) // throw new BasketError("Wallet not provided"); // return await basket.sellBasketToSingleToken( // this.program, // this.wallet, // this.tokenList, // withdrawToken, // amount, // this.lamports, // updateOracles, // ) // } async computeOutputAmountWithSingleToken(basket: Basket, withdrawToken: PublicKey, amount: number): Promise<number> { let oraclePrices = await getOraclePrices(this.program, this.tokenList); let tokenSettings = this.tokenList.find(x => x.tokenMint == withdrawToken.toBase58()); if (!tokenSettings) throw new BasketError("Token not in composition"); let exp = basket.computeOutputAmountWithSingleToken( oraclePrices, this.tokenList, tokenSettings, amount, ); return exp; } async sellBasket(basket: Basket, amount: number, rebalance: number): Promise<Basket> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await basket.sell( this.program, this.wallet, amount, rebalance, this.lamports, ) } async claimTokensFromBuyState(buyState: BuyState): Promise<TransactionSignature[]> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await buyState.claimTokens( this.program, this.wallet, this.tokenList, this.lamports, ) } async claimTokens(basket: Basket): Promise<TransactionSignature[]> { if (!this.wallet) throw new BasketError("Wallet not provided"); return await basket.claimTokens( this.program, this.wallet, this.tokenList, this.lamports, ) } async removeDust( basket: Basket, updateOracles: boolean = true, ): Promise<TransactionSignature[]> { if (!this.wallet) throw new BasketError("Wallet not provided"); let oraclePriceData = await getOraclePrices(this.program, this.tokenList); return await basket.removeDust( this.program, this.wallet, this.tokenList, oraclePriceData, this.lamports, updateOracles, ); } async freezeProgram(): Promise<TransactionSignature> { if (!this.wallet) throw new BasketError("Wallet not provided"); let transaction = new Transaction(); transaction = transaction.add( await this.program.methods.freezeProgram().accounts({ owner: this.wallet?.publicKey, tokenList: TOKEN_LIST_ADDRESS, systemProgram: SystemProgram.programId, }).instruction() ); transaction = transaction.add( ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}) ).add( ComputeBudgetProgram.setComputeUnitPrice({microLamports: this.lamports}) ); let signedTransactions = await signTransactionsWithWallet( this.program.provider.connection, this.wallet, [{transaction: transaction, signers: []}] ); return (await sendSignedTransactions( this.program.provider.connection, signedTransactions, 1 ))[0] } async unfreezeProgram(): Promise<TransactionSignature> { if (!this.wallet) throw new BasketError("Wallet not provided"); let transaction = new Transaction(); transaction = transaction.add( await this.program.methods.unfreezeProgram().accounts({ owner: this.wallet?.publicKey, tokenList: TOKEN_LIST_ADDRESS, systemProgram: SystemProgram.programId, }).instruction() ); transaction = transaction.add( ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}) ).add( ComputeBudgetProgram.setComputeUnitPrice({microLamports: this.lamports}) ); let signedTransactions = await signTransactionsWithWallet( this.program.provider.connection, this.wallet, [{transaction: transaction, signers: []}] ); return (await sendSignedTransactions( this.program.provider.connection, signedTransactions, 1 ))[0] } }