UNPKG

@symmetry-hq/baskets-sdk

Version:

Software Development Kit for interacting with Symmetry Baskets Program

1,160 lines (1,109 loc) 46.4 kB
import { AnchorProvider, BN, Instruction, Program, Wallet } from "@coral-xyz/anchor"; import { AccountInfo, AddressLookupTableAccount, ComputeBudgetProgram, Connection, Keypair, MessageV0, PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, Transaction, TransactionInstruction, TransactionMessage, TransactionSignature, VersionedTransaction } from "@solana/web3.js"; import { BasketsIDL, IDL } from "./basketsIDL"; import { ADDITIONAL_FEE, ADDITIONAL_UNITS, CreateBasketParams, CREATE_FEE_ACCOUNT, BasketStateChainData, BASKETS_PROGRAM_PDA, CURVE_DATA_ADDRESS, RebalanceInfo, REBALANCE_FEE_ACCOUNT, Side, SWAP_FEE_ACCOUNT, TokenSettings, TOKEN_LIST_ADDRESS, BASKETS_PROGRAM_ID, JupSwapData, JUP_AGGREGATOR, SimpleEditParams, FilterType, FilterTime, SortBy, WeightType, WeightTime, SimpleCreateParams, TransactionToSend, } from "./config"; import { calculateRebalanceAmounts, getOraclePrices, rawOraclePrices, sendSignedTransactions, signTransactionsWithWallet, signVersionedTransactions, validateCreateBasketParams } from "./utils"; import { TOKEN_PROGRAM_ID, createAssociatedTokenAccountInstruction, getAssociatedTokenAddressSync } from "./splTokenHelpers"; import axios from "axios"; import { MD5 } from "crypto-js"; import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; import { Metaplex } from "@metaplex-foundation/js"; import { buildClaimTokensFromSellStateIxs, buildCloseBasketIx, buildCreateBasketIx, buildEditBasketIx, buildEditManagetIx, buildSellBasketIx, buildSellBasketToSingleTokenIx, buildSetMetadataIx, buildUpdateCurrentWeightsIx, getSwbFeedsUpdateIxs } from "./instructionsBuilder"; export class Basket { public ownAddress: PublicKey; public data: BasketStateChainData; constructor( ownAddress: PublicKey, basketData: BasketStateChainData, ) { this.ownAddress = ownAddress; this.data = basketData; } static loadFromRawData( program: Program<BasketsIDL>, rawData: { pubkey: PublicKey; account: AccountInfo<Buffer>; } ): Basket { return new Basket( rawData.pubkey, program.coder.accounts.decode("fundState", rawData.account.data) ) } static async loadFromPubkey( program: Program<BasketsIDL>, basketState: PublicKey ): Promise<Basket> { let basketData = await program.account.fundState.fetch(basketState, "confirmed"); return new Basket( basketState, //@ts-ignore basketData, ); } static async getCompositionAndPrice( connection: Connection, pubkey: PublicKey, getPrice: boolean = false, ): Promise<any> { let provider = new AnchorProvider( connection, new NodeWallet(Keypair.generate()), { skipPreflight: true, preflightCommitment: "recent", commitment: "processed", } ); let program = new Program<BasketsIDL>(IDL, provider); let accounts = [pubkey, TOKEN_LIST_ADDRESS]; let accountsInfo = await connection.getMultipleAccountsInfo(accounts, "confirmed"); //@ts-ignore let basketData = await program.coder.accounts.decode("fundState", accountsInfo[0]?.data); //@ts-ignore let tokenList = await program.coder.accounts.decode("tokenList", accountsInfo[1]?.data); let supply = parseInt(basketData.supplyOutstanding.toString()) / 10 ** 6; let mint = basketData.fundToken.toBase58(); let tokenIds = basketData.currentCompToken .slice(0, parseInt(basketData.numOfTokens.toString())).map((x: any) => parseInt(x.toString())); let tokenAmounts = basketData.currentCompAmount .slice(0, parseInt(basketData.numOfTokens.toString())) .map((x: any, id: any) => parseInt(x.toString()) / 10 ** tokenList.list[tokenIds[id]].decimals ); let tokenMints = tokenIds.map((x: any) => tokenList.list[x].tokenMint.toBase58()); let price = undefined; let tvl = undefined; if (getPrice) { let oraclePrices = await rawOraclePrices(connection, tokenList.list.slice(0, parseInt(tokenList.numTokens.toString()))); tvl = 0; for (let i=0; i<tokenAmounts.length; i++) tvl += oraclePrices[tokenIds[i]] * tokenAmounts[i]; price = tvl / supply; } let result = { price: price, supply: supply, tvl: tvl, mint: mint, composition: tokenAmounts.map((_: any, id: any) => { return { mint: tokenMints[id], amount: tokenAmounts[id], } }) } return result; } static async create( program: Program<BasketsIDL>, connection: Connection, wallet: Wallet, tokenList: TokenSettings[], lookups: AddressLookupTableAccount[], basketParams: SimpleCreateParams, lamports: number = ADDITIONAL_FEE, ): Promise<Basket> { let createData = await buildCreateBasketIx(program, tokenList, basketParams); let blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash; let V0Create = new VersionedTransaction( new TransactionMessage({ payerKey: wallet.publicKey, recentBlockhash: blockhash, instructions: [ createData, ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ] }).compileToV0Message(lookups) ); let signedTransactions = await signVersionedTransactions( wallet, [V0Create] ); let resultCreate = await sendSignedTransactions( connection, signedTransactions, 1 ); console.log("Create Tx:", resultCreate[0]); return await Basket .loadFromPubkey( program, createData.keys[2].pubkey, ); } async update(program: Program<BasketsIDL>) { //@ts-ignore this.data = await program.account.fundState.fetch(this.ownAddress, "confirmed"); } async editManager( program: Program<BasketsIDL>, connection: Connection, wallet: Wallet, newManager: PublicKey, lamports: number = ADDITIONAL_FEE, ): Promise<TransactionSignature> { let editManager = new Transaction(); editManager.instructions = [ await buildEditManagetIx(program, this, newManager), ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ]; let signedTransactions = await signTransactionsWithWallet( connection, wallet, [{ transaction: editManager, signers: [] }] ); let resultEdit = await sendSignedTransactions( connection, [signedTransactions[0]], 1 ); return resultEdit[0]; } async edit( program: Program<BasketsIDL>, connection: Connection, wallet: Wallet, tokenList: TokenSettings[], lookups: AddressLookupTableAccount[], basketParams: SimpleEditParams, lamports: number = ADDITIONAL_FEE, ): Promise<TransactionSignature> { let blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash; let V0Create = new VersionedTransaction( new TransactionMessage({ payerKey: wallet.publicKey, recentBlockhash: blockhash, instructions: [ await buildEditBasketIx(program, tokenList, this, basketParams), ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ] }).compileToV0Message(lookups) ); let signedTransactions = await signVersionedTransactions( wallet, [V0Create] ); let resultEdit = await sendSignedTransactions( connection, signedTransactions, 1 ); return resultEdit[0]; } async setMetaData( program: Program<BasketsIDL>, wallet: Wallet, symbol: string, name: string, uri: string, lamports: number = ADDITIONAL_FEE, ): Promise<TransactionSignature> { let transaction = new Transaction(); transaction.instructions = [ await buildSetMetadataIx(program, this, {symbol: symbol, name: name, uri: uri}), ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ] let signedTransactions = await signTransactionsWithWallet( program.provider.connection, wallet, [{transaction: transaction, signers: []}] ); return (await sendSignedTransactions( program.provider.connection, signedTransactions, 1 ))[0]; } async close( program: Program<BasketsIDL>, wallet: Wallet, lamports: number = ADDITIONAL_FEE, ): Promise<TransactionSignature> { let transaction = new Transaction(); transaction.instructions = [ await buildCloseBasketIx(program, this), ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ]; let signedTransactions = await signTransactionsWithWallet( program.provider.connection, wallet, [{transaction: transaction, signers: []}] ); return (await sendSignedTransactions( program.provider.connection, signedTransactions, 1 ))[0]; } getSwbFeeds( tokenList: TokenSettings[], ): PublicKey[] { let oracles: PublicKey[] = []; for (let i = 0; i < this.data.numOfTokens.toNumber(); i++) if (tokenList[this.data.currentCompToken[i].toNumber()].oracleType == "SwbOnDemand") oracles.push(new PublicKey(tokenList[this.data.currentCompToken[i].toNumber()].oracleAccount)) return oracles; } async rebalanceFromUsdcTransactionData( program: Program<BasketsIDL>, wallet: Wallet, pda: PublicKey, basketState: PublicKey, tokenList: TokenSettings[], rebalanceFeeAccount: PublicKey, jupSwapData: JupSwapData, lamports: number = ADDITIONAL_FEE, ): Promise<{ payerKey: PublicKey, instructions: TransactionInstruction[], lookupTables: AddressLookupTableAccount[] }> { let tokenId = jupSwapData.toTokenId; let ix = (jupSwapData.type == "Simple") ? await program.methods .rebalanceBuy( jupSwapData.toTokenId, new BN(jupSwapData.fromAmount), jupSwapData.dataLength, Array.from(jupSwapData.data), ) .accounts({ signer: wallet.publicKey, fundState: basketState, tokenList: TOKEN_LIST_ADDRESS, oracleSol: new PublicKey(tokenList[1].oracleAccount), oracleToken: new PublicKey(tokenList[tokenId].oracleAccount), oracleUsdc: new PublicKey(tokenList[0].oracleAccount), pdaAccount: pda, pdaTokenAccount: new PublicKey(tokenList[tokenId].pdaTokenAccount), pdaUsdcAccount: new PublicKey(tokenList[0].pdaTokenAccount), rebalanceFeeAccount: rebalanceFeeAccount, tokenProgram: TOKEN_PROGRAM_ID, }) .remainingAccounts(jupSwapData.accounts) .instruction() : await program.methods .rebalanceBuyTransitive( jupSwapData.toTokenId, new BN(jupSwapData.fromAmount), jupSwapData.firstIxEnd, jupSwapData.dataLength, jupSwapData.firstIxAccounts, Array.from(jupSwapData.data), ) .accounts({ signer: wallet.publicKey, fundState: basketState, tokenList: TOKEN_LIST_ADDRESS, oracleSol: new PublicKey(tokenList[1].oracleAccount), oracleToken: new PublicKey(tokenList[tokenId].oracleAccount), oracleUsdc: new PublicKey(tokenList[0].oracleAccount), pdaAccount: pda, pdaTokenAccount: new PublicKey(tokenList[tokenId].pdaTokenAccount), pdaMidAccount: new PublicKey(jupSwapData.midTokenPda), pdaUsdcAccount: new PublicKey(tokenList[0].pdaTokenAccount), rebalanceFeeAccount: rebalanceFeeAccount, tokenProgram: TOKEN_PROGRAM_ID, }) .remainingAccounts(jupSwapData.accounts) .instruction(); let rawData = { payerKey: wallet.publicKey, instructions: [ ix, ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ], lookupTables: jupSwapData.lookupTableAccounts }; return rawData; } async rebalanceToUsdcTransactionData( program: Program<BasketsIDL>, wallet: Wallet, pda: PublicKey, basketState: PublicKey, tokenList: TokenSettings[], rebalanceFeeAccount: PublicKey, jupSwapData: JupSwapData, lamports: number = ADDITIONAL_FEE, ): Promise<TransactionToSend> { let tokenId = jupSwapData.fromTokenId; let ix = (jupSwapData.type == "Simple") ? await program.methods .rebalanceSell( tokenId, new BN(jupSwapData.fromAmount), jupSwapData.dataLength, Array.from(jupSwapData.data), ) .accounts({ signer: wallet.publicKey, fundState: basketState, tokenList: TOKEN_LIST_ADDRESS, oracleSol: new PublicKey(tokenList[1].oracleAccount), oracleToken: new PublicKey(tokenList[tokenId].oracleAccount), oracleUsdc: new PublicKey(tokenList[0].oracleAccount), pdaAccount: pda, pdaTokenAccount: new PublicKey(tokenList[tokenId].pdaTokenAccount), pdaUsdcAccount: new PublicKey(tokenList[0].pdaTokenAccount), rebalanceFeeAccount: rebalanceFeeAccount, tokenProgram: TOKEN_PROGRAM_ID, }) .remainingAccounts(jupSwapData.accounts) .instruction() : await program.methods .rebalanceSellTransitive( tokenId, new BN(jupSwapData.fromAmount), jupSwapData.firstIxEnd, jupSwapData.dataLength, jupSwapData.firstIxAccounts, Array.from(jupSwapData.data), ) .accounts({ signer: wallet.publicKey, fundState: basketState, tokenList: TOKEN_LIST_ADDRESS, oracleSol: new PublicKey(tokenList[1].oracleAccount), oracleToken: new PublicKey(tokenList[tokenId].oracleAccount), oracleUsdc: new PublicKey(tokenList[0].oracleAccount), pdaAccount: pda, pdaTokenAccount: new PublicKey(tokenList[tokenId].pdaTokenAccount), pdaMidAccount: new PublicKey(jupSwapData.midTokenPda), pdaUsdcAccount: new PublicKey(tokenList[0].pdaTokenAccount), rebalanceFeeAccount: rebalanceFeeAccount, tokenProgram: TOKEN_PROGRAM_ID, }) .remainingAccounts(jupSwapData.accounts) .instruction(); let rawData = { payerKey: wallet.publicKey, instructions: [ ix, ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ], lookupTables: jupSwapData.lookupTableAccounts }; return rawData; } getRebalanceInfo( program: Program<BasketsIDL>, tokenList: TokenSettings[], oraclePriceData: number[], timestamp: number, forceRebalance: boolean = false, ): RebalanceInfo[] { let basketStateData = this.data; let currentCompToken: BN[] = basketStateData.currentCompToken; let currentCompAmount: BN[] = basketStateData.currentCompAmount; let targetWeight: BN[] = basketStateData.targetWeight; let numTokens = parseInt(basketStateData.numOfTokens.toString()); let rebalanceThreshold = basketStateData.rebalanceThreshold; let weightSum = basketStateData.weightSum; let rebalanceInterval = parseInt(basketStateData.rebalanceInterval.toString()); let lastRebalanceTime = basketStateData.lastRebalanceTime; return calculateRebalanceAmounts( program, numTokens, timestamp, Array.from(lastRebalanceTime, x => parseInt(x.toString())), rebalanceInterval, Array.from(currentCompToken, x => parseInt(x.toString())), Array.from(currentCompAmount, x => parseInt(x.toString())), Array.from(targetWeight, x => parseInt(x.toString())), parseInt(weightSum.toString()), tokenList, parseInt(rebalanceThreshold.toString()), oraclePriceData, forceRebalance, ); } async rebalanceFrom( program: Program<BasketsIDL>, wallet: Wallet, tokenList: TokenSettings[], jupSwapDatas: JupSwapData[], rebalanceInfos: RebalanceInfo[], lookups: AddressLookupTableAccount[], lamports: number = ADDITIONAL_FEE, ): Promise<TransactionToSend[]> { let ix = await buildUpdateCurrentWeightsIx(program, this, tokenList); let transactionsData: { payerKey: PublicKey, instructions: TransactionInstruction[], lookupTables: AddressLookupTableAccount[] }[] = []; for (let i = rebalanceInfos.length - 1; i >= 0; i--) { if (rebalanceInfos[i].side == Side.To) continue; let rebalanceData = jupSwapDatas[i]; if (!rebalanceData) continue; let txData = await this.rebalanceFromUsdcTransactionData( program, wallet, BASKETS_PROGRAM_PDA, this.ownAddress, tokenList, REBALANCE_FEE_ACCOUNT, rebalanceData, lamports, ).catch((e) => { console.log(e.message); return null;}); if (!txData) continue; transactionsData.push(txData); } for (let i = 0; i < transactionsData.length; i++) { transactionsData[i].instructions = [ix, ...transactionsData[i].instructions]; transactionsData[i].lookupTables = [...lookups, ...transactionsData[i].lookupTables]; } return transactionsData; } async rebalanceTo( program: Program<BasketsIDL>, wallet: Wallet, tokenList: TokenSettings[], jupSwapDatas: JupSwapData[], rebalanceInfos: RebalanceInfo[], lookups: AddressLookupTableAccount[], lamports: number = ADDITIONAL_FEE, ): Promise<{ payerKey: PublicKey, instructions: TransactionInstruction[], lookupTables: AddressLookupTableAccount[] }[]> { let ix = await buildUpdateCurrentWeightsIx(program, this, tokenList); let transactionsData: { payerKey: PublicKey, instructions: TransactionInstruction[], lookupTables: AddressLookupTableAccount[] }[] = []; for (let i = rebalanceInfos.length - 1; i >= 0; i--) { if (rebalanceInfos[i].side == Side.From) continue; let rebalanceData = jupSwapDatas[i]; if (!rebalanceData) continue; let txData = await this.rebalanceToUsdcTransactionData( program, wallet, BASKETS_PROGRAM_PDA, this.ownAddress, tokenList, REBALANCE_FEE_ACCOUNT, rebalanceData, lamports, ).catch((e) => { console.log(e.message); return null;}); if (!txData) continue; transactionsData.push(txData); } for (let i = 0; i < transactionsData.length; i++) { transactionsData[i].instructions = [ix, ...transactionsData[i].instructions]; transactionsData[i].lookupTables = [...lookups, ...transactionsData[i].lookupTables]; } return transactionsData; } // async rebalanceSingle( // program: Program<BasketsIDL>, // wallet: Wallet, // tokenList: TokenSettings[], // jupSwapData: JupSwapData, // rebalanceInfo: RebalanceInfo, // lamports: number, // updateOracles: boolean, /// NEED to implement // ): Promise<TransactionSignature> { // let prepareIx = await buildUpdateCurrentWeightsIx(program, this, tokenList); // let txData = (rebalanceInfo.side == Side.To) ? // await this.rebalanceToUsdcTransactionData( // program, // wallet, // BASKETS_PROGRAM_PDA, // this.ownAddress, // tokenList, // REBALANCE_FEE_ACCOUNT, // jupSwapData, // lamports, // ) : // await this.rebalanceFromUsdcTransactionData( // program, // wallet, // BASKETS_PROGRAM_PDA, // this.ownAddress, // tokenList, // REBALANCE_FEE_ACCOUNT, // jupSwapData, // lamports, // ); // txData.instructions = [prepareIx, ...txData.instructions]; // const lookupTableAccount1 = ( // await program.provider.connection.getAddressLookupTable(BASKETS_LOOKUP_TABLE_1) // ).value; // const lookupTableAccount2 = ( // await program.provider.connection.getAddressLookupTable(BASKETS_LOOKUP_TABLE_2) // ).value; // //@ts-ignore // txData.lookupTables.push(lookupTableAccount1); // //@ts-ignore // txData.lookupTables.push(lookupTableAccount2); // let blockhash = (await program.provider.connection.getLatestBlockhash()).blockhash; // let signedTransactions = await signVersionedTransactions( // wallet, // [new VersionedTransaction( // new TransactionMessage({ // payerKey: txData.payerKey, // recentBlockhash: blockhash, // instructions: txData.instructions, // }).compileToV0Message(txData.lookupTables) // )] // ).catch(e => { console.log(e); }); // let txs = await sendSignedTransactions( // program.provider.connection, //@ts-ignore // signedTransactions, // 1 // ).catch((e) => {console.log(e); return [""]}); // return txs[0]; // } // async sellBasketToSingleToken( // program: Program<BasketsIDL>, // wallet: Wallet, // tokenList: TokenSettings[], // withdrawToken: PublicKey, // amount: number, // lamports: number, // updateOracles: boolean, /// NEED to implement // ): Promise<TransactionSignature> { // let transaction = new Transaction(); // transaction.instructions = [ // await buildSellBasketToSingleTokenIx(program, tokenList, wallet.publicKey, this, withdrawToken, amount), // ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), // ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) // ]; // let signedTransactions = await signTransactionsWithWallet( // program.provider.connection, // wallet, // [{transaction: transaction, signers:[]}] // ); // let txs = await sendSignedTransactions( // program.provider.connection, // signedTransactions, // 1, // ); // return txs[0]; // } async computeOutputAmountWithSingleToken( oraclePrices: number[], tokenList: TokenSettings[], withdrawToken: TokenSettings, burnAmount: number, ): Promise<number> { let tokenIndex = -1; for (let i = 0; i < this.data.currentCompToken.length; i++) if (parseInt(this.data.currentCompToken[i].toString()) == withdrawToken.id) { tokenIndex = i; break; } if (tokenIndex == -1) throw new Error("Token not in composition"); let withdrawTokenPrice = oraclePrices[withdrawToken.id]; let withdrawWorth = 0; let basketWorth = 0; for (let i = 0; i < parseInt(this.data.numOfTokens.toString()); i++) { let tokenPrice = oraclePrices[parseInt(this.data.currentCompToken[i].toString())]; basketWorth += tokenPrice * parseInt(this.data.currentCompAmount[i].toString()) / 10 ** tokenList[parseInt(this.data.currentCompToken[i].toString())].decimals; let allocation = ( parseInt(this.data.currentCompAmount[i].toString()) * burnAmount * 10 ** 6 / parseInt(this.data.supplyOutstanding.toString()) ); withdrawWorth += tokenPrice * allocation / 10 ** tokenList[parseInt(this.data.currentCompToken[i].toString())].decimals; } let targetValueBefore = basketWorth * parseInt(this.data.targetWeight[tokenIndex].toString()) / parseInt(this.data.weightSum.toString()); let valueBefore = withdrawTokenPrice * parseInt(this.data.currentCompAmount[tokenIndex].toString()) / 10 ** withdrawToken.decimals let valueToRebalance = 0; if (valueBefore <= targetValueBefore) valueToRebalance = withdrawWorth; else { let valueToTgtWeight = (valueBefore - targetValueBefore) * parseInt(this.data.weightSum.toString()) / (parseInt(this.data.weightSum.toString()) - parseInt(this.data.targetWeight[tokenIndex].toString())) if (valueToTgtWeight < withdrawWorth) { valueToRebalance = withdrawWorth - valueToTgtWeight; } } let tokenAmount = 0; tokenAmount += (withdrawWorth - valueToRebalance) / withdrawTokenPrice; let penalty = valueToRebalance * 300 / 10000; tokenAmount += (valueToRebalance - penalty) / withdrawTokenPrice; if (tokenAmount > parseInt(this.data.currentCompAmount[tokenIndex].toString()) / 10 ** withdrawToken.decimals) tokenAmount = parseInt(this.data.currentCompAmount[tokenIndex].toString()) / 10 ** withdrawToken.decimals; return tokenAmount; } async sell( program: Program<BasketsIDL>, wallet: Wallet, amount: number, rebalance: number, lamports: number = ADDITIONAL_FEE, ): Promise<Basket> { let sellBasketData = await buildSellBasketIx(program, wallet.publicKey, this, amount, rebalance); let transaction = new Transaction(); transaction.instructions = [ sellBasketData, ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ]; let signedTransactions = await signTransactionsWithWallet( program.provider.connection, wallet, [{transaction: transaction, signers: []}] ); await sendSignedTransactions( program.provider.connection, signedTransactions, 1, ); return await Basket.loadFromPubkey( program, sellBasketData.keys[3].pubkey, ); } async claimTokens( program: Program<BasketsIDL>, wallet: Wallet, tokenList: TokenSettings[], lamports: number = ADDITIONAL_FEE, ): Promise<TransactionSignature[]> { await this.update(program); let basketOwner = this.data.manager; let connection = program.provider.connection; let transactions: Transaction[] = []; let claimTokensIxs = await buildClaimTokensFromSellStateIxs(program, tokenList, wallet.publicKey, this); let ixId = 0; for (let i = 0; i < parseInt(this.data.numOfTokens.toString()); i++) { if (parseInt(this.data.currentCompAmount[i].toString()) == 0 && i != 0) continue; let transaction = new Transaction(); let tokenId = parseInt(this.data.currentCompToken[i].toString()); let userTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[tokenId].tokenMint), basketOwner, true, ); let infoAta = await connection.getAccountInfo(userTokenAccount, "confirmed"); if (!infoAta) transaction.add( await createAssociatedTokenAccountInstruction( wallet.publicKey, userTokenAccount, basketOwner, new PublicKey(tokenList[tokenId].tokenMint), ) ); transaction.instructions = [ ...transaction.instructions, claimTokensIxs[ixId], ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ] transactions.push(transaction); ixId = ixId + 1; } let signedTransactions = await signTransactionsWithWallet( connection, wallet, transactions.map(transaction => { return { transaction: transaction, signers: []} }) ); return await sendSignedTransactions( connection, signedTransactions, ); } async removeDust( program: Program<BasketsIDL>, wallet: Wallet, tokenList: TokenSettings[], oraclePriceData: number[], lamports: number, updateOracles: boolean, /// NEED to implement ): Promise<TransactionSignature[]> { let preTransactions: Transaction[] = []; let swapTransactions: Transaction[] = []; for (let j = parseInt(this.data.numOfTokens.toString()) - 1; j > 0; j--) { let token = parseInt(this.data.currentCompToken[j].toString()); let decimals = tokenList[token].decimals; let amount = parseInt(this.data.currentCompAmount[j].toString()); let weight = parseInt(this.data.targetWeight[j].toString()); let price = oraclePriceData[token]; let value = (amount / 10 ** decimals) * price; if (value > 0.005 || weight != 0) continue; let transaction = new Transaction(); let instructions = []; let fromTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[0].tokenMint), wallet.publicKey, true, ); let toTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[token].tokenMint), wallet.publicKey, true, ); const info = await program.provider.connection.getAccountInfo(toTokenAccount, "confirmed"); if (!info) { instructions.push( createAssociatedTokenAccountInstruction( wallet.publicKey, toTokenAccount, wallet.publicKey, new PublicKey(tokenList[token].tokenMint), ) ); } let swapFeeAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[token].tokenMint), SWAP_FEE_ACCOUNT, ); const info2 = await program.provider.connection.getAccountInfo(swapFeeAccount, "confirmed"); if (!info2) { instructions.push( createAssociatedTokenAccountInstruction( wallet.publicKey, swapFeeAccount, SWAP_FEE_ACCOUNT, new PublicKey(tokenList[token].tokenMint), ) ); } let hostFeeAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[token].tokenMint), this.data.hostPubkey, true, ); const info3 = await program.provider.connection.getAccountInfo(hostFeeAccount, "confirmed"); if (!info3) { instructions.push( createAssociatedTokenAccountInstruction( wallet.publicKey, hostFeeAccount, this.data.hostPubkey, new PublicKey(tokenList[token].tokenMint), ) ); } let managerFeeAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[token].tokenMint), this.data.manager, true, ); const info4 = await program.provider.connection.getAccountInfo(managerFeeAccount, "confirmed"); if (!info4 && managerFeeAccount.toBase58() != hostFeeAccount.toBase58()) { instructions.push( createAssociatedTokenAccountInstruction( wallet.publicKey, managerFeeAccount, this.data.manager, new PublicKey(tokenList[token].tokenMint), ) ); } if (instructions.length > 0) { transaction.add(...instructions); transaction = transaction.add( ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}) ).add( ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ); preTransactions.push(transaction); } let usdcAmount = Math.max(Math.floor(1.1 * value * 10 ** 6), 50) swapTransactions.push( new Transaction() .add(await program.methods.liquidityProvision( new BN(0), new BN(token), new BN(usdcAmount), new BN(amount), ) .accounts({ buyer: program.provider.publicKey, fundState: this.ownAddress, pdaAccount: BASKETS_PROGRAM_PDA, pdaFromTokenAccount: new PublicKey(tokenList[0].pdaTokenAccount), buyerFromTokenAccount: fromTokenAccount, pdaToTokenAccount: new PublicKey(tokenList[token].pdaTokenAccount), buyerToTokenAccount: toTokenAccount, oracleSol: new PublicKey(tokenList[1].oracleAccount), swapFeeAccount: getAssociatedTokenAddressSync( new PublicKey(tokenList[token].tokenMint), SWAP_FEE_ACCOUNT, ), hostFeeAccount: getAssociatedTokenAddressSync( new PublicKey(tokenList[token].tokenMint), this.data.hostPubkey, true, ), managerFeeAccount: getAssociatedTokenAddressSync( new PublicKey(tokenList[token].tokenMint), this.data.manager, true, ), tokenList: TOKEN_LIST_ADDRESS, curveData: CURVE_DATA_ADDRESS, tokenProgram: TOKEN_PROGRAM_ID }) .remainingAccounts(this.data.currentCompToken.map(x => { return { pubkey: new PublicKey(tokenList[parseInt(x.toString())].oracleAccount), isSigner: false, isWritable: false, } })) .instruction() ) .add( ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}) ).add( ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ) ); } let transactions = [...preTransactions, ...swapTransactions]; let signedTransactions = await signTransactionsWithWallet( program.provider.connection, wallet, transactions.map(transaction => { return { transaction: transaction, signers: []} }) ); let _ = await sendSignedTransactions( program.provider.connection, signedTransactions.slice(0, preTransactions.length), ); return await sendSignedTransactions( program.provider.connection, signedTransactions.slice(preTransactions.length, signedTransactions.length), swapTransactions.length, ); } async liquidityProvision( program: Program<BasketsIDL>, wallet: Wallet, tokenList: TokenSettings[], fromToken: number, toToken: number, fromAmount: number, lamports: number = ADDITIONAL_FEE, ): Promise<TransactionSignature[]> { let preTransactions: Transaction[] = []; let swapTransactions: Transaction[] = []; let transaction = new Transaction(); let instructions = []; let fromTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[fromToken].tokenMint), wallet.publicKey, true, ); let toTokenAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[toToken].tokenMint), wallet.publicKey, true, ); const info = await program.provider.connection.getAccountInfo(toTokenAccount, "confirmed"); if (!info) { instructions.push( createAssociatedTokenAccountInstruction( wallet.publicKey, toTokenAccount, wallet.publicKey, new PublicKey(tokenList[toToken].tokenMint), ) ); } let swapFeeAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[toToken].tokenMint), SWAP_FEE_ACCOUNT, ); const info2 = await program.provider.connection.getAccountInfo(swapFeeAccount, "confirmed"); if (!info2) { instructions.push( createAssociatedTokenAccountInstruction( wallet.publicKey, swapFeeAccount, SWAP_FEE_ACCOUNT, new PublicKey(tokenList[toToken].tokenMint), ) ); } let hostFeeAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[toToken].tokenMint), this.data.hostPubkey, true, ); const info3 = await program.provider.connection.getAccountInfo(hostFeeAccount, "confirmed"); if (!info3) { instructions.push( createAssociatedTokenAccountInstruction( wallet.publicKey, hostFeeAccount, this.data.hostPubkey, new PublicKey(tokenList[toToken].tokenMint), ) ); } let managerFeeAccount = getAssociatedTokenAddressSync( new PublicKey(tokenList[toToken].tokenMint), this.data.manager, true, ); const info4 = await program.provider.connection.getAccountInfo(managerFeeAccount, "confirmed"); if (!info4 && managerFeeAccount.toBase58() != hostFeeAccount.toBase58()) { instructions.push( createAssociatedTokenAccountInstruction( wallet.publicKey, managerFeeAccount, this.data.manager, new PublicKey(tokenList[toToken].tokenMint), ) ); } if (instructions.length > 0) { transaction.add(...instructions); transaction = transaction.add( ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}) ).add( ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ); preTransactions.push(transaction); } swapTransactions.push( new Transaction() .add(await program.methods.liquidityProvision( new BN(fromToken), new BN(toToken), new BN(Math.floor(fromAmount * 10 ** tokenList[fromToken].decimals)), new BN(0), ) .accounts({ buyer: program.provider.publicKey, fundState: this.ownAddress, pdaAccount: BASKETS_PROGRAM_PDA, pdaFromTokenAccount: new PublicKey(tokenList[fromToken].pdaTokenAccount), buyerFromTokenAccount: fromTokenAccount, pdaToTokenAccount: new PublicKey(tokenList[toToken].pdaTokenAccount), buyerToTokenAccount: toTokenAccount, oracleSol: new PublicKey(tokenList[1].oracleAccount), swapFeeAccount: getAssociatedTokenAddressSync( new PublicKey(tokenList[toToken].tokenMint), SWAP_FEE_ACCOUNT, ), hostFeeAccount: getAssociatedTokenAddressSync( new PublicKey(tokenList[toToken].tokenMint), this.data.hostPubkey, true, ), managerFeeAccount: getAssociatedTokenAddressSync( new PublicKey(tokenList[toToken].tokenMint), this.data.manager, true, ), tokenList: TOKEN_LIST_ADDRESS, curveData: CURVE_DATA_ADDRESS, tokenProgram: TOKEN_PROGRAM_ID }) .remainingAccounts(this.data.currentCompToken .slice(0, parseInt(this.data.numOfTokens.toString())).map(x => { return { pubkey: new PublicKey(tokenList[parseInt(x.toString())].oracleAccount), isSigner: false, isWritable: false, } })) .instruction() ) .add( ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}) ).add( ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}) ) ); let transactions = [...preTransactions, ...swapTransactions]; let signedTransactions = await signTransactionsWithWallet( program.provider.connection, wallet, transactions.map(transaction => { return { transaction: transaction, signers: []} }) ); let _ = await sendSignedTransactions( program.provider.connection, signedTransactions.slice(0, preTransactions.length), ); return await sendSignedTransactions( program.provider.connection, signedTransactions.slice(preTransactions.length, signedTransactions.length), swapTransactions.length, ); } }