UNPKG

@kamino-finance/klend-sdk

Version:

Typescript SDK for interacting with the Kamino Lending (klend) protocol

214 lines 9.28 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createWsolAtaIfMissing = exports.getAtasWithCreateIxsIfMissing = void 0; exports.createAssociatedTokenAccountIdempotentInstruction = createAssociatedTokenAccountIdempotentInstruction; exports.getAssociatedTokenAddress = getAssociatedTokenAddress; exports.createAtasIdempotent = createAtasIdempotent; exports.getTransferWsolIxs = getTransferWsolIxs; exports.getTokenAccountBalance = getTokenAccountBalance; exports.getTokenAccountBalanceDecimal = getTokenAccountBalanceDecimal; exports.getAllStandardTokenProgramTokenAccounts = getAllStandardTokenProgramTokenAccounts; exports.getTokenAccountMint = getTokenAccountMint; exports.getTokenAccountAmount = getTokenAccountAmount; const kit_1 = require("@solana/kit"); const decimal_js_1 = __importDefault(require("decimal.js")); const dist_1 = require("@kamino-finance/kliquidity-sdk/dist"); const token_1 = require("@solana-program/token"); const token_2022_1 = require("@solana-program/token-2022"); const pubkey_1 = require("./pubkey"); const system_1 = require("@solana-program/system"); /** * Create an idempotent create ATA instruction * Overrides the create ATA ix to use the idempotent version as the spl-token library does not provide this ix yet * @param owner - owner of the ATA * @param mint - mint of the ATA * @param payer - payer of the transaction * @param tokenProgram - optional token program address - spl-token if not provided * @param ata - optional ata address - derived if not provided * @returns The ATA address public key and the transaction instruction */ async function createAssociatedTokenAccountIdempotentInstruction(payer, mint, owner = payer.address, tokenProgram = token_1.TOKEN_PROGRAM_ADDRESS, ata) { let ataAddress = ata; if (!ataAddress) { ataAddress = await getAssociatedTokenAddress(mint, owner, tokenProgram, token_2022_1.ASSOCIATED_TOKEN_PROGRAM_ADDRESS); } const createUserTokenAccountIx = (0, token_2022_1.getCreateAssociatedTokenIdempotentInstruction)({ owner, mint, tokenProgram, ata: ataAddress, payer, }, { programAddress: token_2022_1.ASSOCIATED_TOKEN_PROGRAM_ADDRESS, }); return [ataAddress, createUserTokenAccountIx]; } async function getAssociatedTokenAddress(mint, owner, tokenProgram = token_1.TOKEN_PROGRAM_ADDRESS, associatedTokenProgramId = token_2022_1.ASSOCIATED_TOKEN_PROGRAM_ADDRESS) { const [ata] = await (0, token_2022_1.findAssociatedTokenPda)({ mint, owner, tokenProgram, }, { programAddress: associatedTokenProgramId }); return ata; } const getAtasWithCreateIxsIfMissing = async (rpc, user, mints) => { const atas = await Promise.all(mints.map(async (x) => getAssociatedTokenAddress(x.mint, user.address, x.tokenProgram))); const accountInfos = await rpc.getMultipleAccounts(atas).send(); const createAtaIxs = []; for (let i = 0; i < atas.length; i++) { if (accountInfos.value[i] === null) { const { mint, tokenProgram } = mints[i]; const [ata, createIxn] = await createAssociatedTokenAccountIdempotentInstruction(user, mint, user.address, tokenProgram); atas[i] = ata; createAtaIxs.push(createIxn); } } return { atas, createAtaIxs, }; }; exports.getAtasWithCreateIxsIfMissing = getAtasWithCreateIxsIfMissing; async function createAtasIdempotent(user, mints) { const res = []; for (const mint of mints) { const [ata, createAtaIx] = await createAssociatedTokenAccountIdempotentInstruction(user, mint.mint, user.address, mint.tokenProgram); res.push({ ata, createAtaIx, }); } return res; } function getTransferWsolIxs(owner, ata, amountLamports) { const ixs = []; ixs.push((0, system_1.getTransferSolInstruction)({ source: owner, amount: amountLamports, destination: ata, })); ixs.push((0, token_2022_1.getSyncNativeInstruction)({ account: ata, }, { programAddress: token_1.TOKEN_PROGRAM_ADDRESS })); return ixs; } async function getTokenAccountBalance(connection, tokenAccount) { const tokenAccountBalance = await connection.getTokenAccountBalance(tokenAccount).send(); return Number(tokenAccountBalance.value.amount).valueOf(); } /// Get the balance of a token account in decimal format (tokens, not lamports) async function getTokenAccountBalanceDecimal(rpc, mint, owner, tokenProgram = token_1.TOKEN_PROGRAM_ADDRESS) { const ata = await getAssociatedTokenAddress(mint, owner, tokenProgram); const accInfo = await (0, kit_1.fetchEncodedAccount)(rpc, ata); if (!accInfo.exists) { return new decimal_js_1.default('0'); } const { value } = await rpc.getTokenAccountBalance(ata).send(); return new decimal_js_1.default(value.uiAmountString); } /** * Creates a wSOL ata if missing and syncs the balance. If the ata exists and it has more or equal no wrapping happens * @param rpc - Solana RPC rpc (read) * @param amount min amount to have in the wSOL ata. If the ata exists and it has more or equal no wrapping happens * @param owner - owner of the ata * @returns wsolAta: the keypair of the ata, used to sign the initialization transaction; createAtaIxs: a list with ixs to initialize the ata and wrap SOL if needed; closeAtaIxs: a list with ixs to close the ata */ const createWsolAtaIfMissing = async (rpc, amount, owner) => { const createIxs = []; const closeIxs = []; const wsolAta = await getAssociatedTokenAddress(pubkey_1.WRAPPED_SOL_MINT, owner.address, token_1.TOKEN_PROGRAM_ADDRESS); const solDeposit = amount; const wsolAtaAccountInfo = await (0, token_2022_1.fetchMaybeToken)(rpc, wsolAta); // This checks if we need to create it if (wsolAtaAccountInfo.exists) { createIxs.push((0, token_2022_1.getCreateAssociatedTokenIdempotentInstruction)({ owner: owner.address, payer: owner, ata: wsolAta, mint: pubkey_1.WRAPPED_SOL_MINT, tokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, })); } let wsolExistingBalanceLamports = new decimal_js_1.default(0); try { if (wsolAtaAccountInfo.exists) { const uiAmount = (await getTokenAccountBalanceDecimal(rpc, pubkey_1.WRAPPED_SOL_MINT, owner.address)).toNumber(); wsolExistingBalanceLamports = (0, dist_1.collToLamportsDecimal)(new decimal_js_1.default(uiAmount), dist_1.DECIMALS_SOL); } } catch (err) { console.log('Err Token Balance', err); } if (solDeposit !== null && solDeposit.gt(wsolExistingBalanceLamports)) { createIxs.push((0, system_1.getTransferSolInstruction)({ source: owner, destination: wsolAta, amount: BigInt(solDeposit.sub(wsolExistingBalanceLamports).floor().toString()), })); } if (createIxs.length > 0) { // Primitive way of wrapping SOL createIxs.push((0, token_2022_1.getSyncNativeInstruction)({ account: wsolAta, }, { programAddress: token_1.TOKEN_PROGRAM_ADDRESS })); } closeIxs.push((0, token_2022_1.getCloseAccountInstruction)({ owner, account: wsolAta, destination: owner.address, }, { programAddress: token_1.TOKEN_PROGRAM_ADDRESS })); return { wsolAta, createAtaIxs: createIxs, closeAtaIxs: closeIxs, }; }; exports.createWsolAtaIfMissing = createWsolAtaIfMissing; /** * Get all standard token accounts for tokens using old Token Program, not Token 2022 for a given wallet * @param rpc - Solana RPC rpc (read) * @param wallet - wallet to get the token accounts for * @returns an array of all token accounts for the given wallet */ async function getAllStandardTokenProgramTokenAccounts(rpc, wallet) { return rpc .getProgramAccounts(token_1.TOKEN_PROGRAM_ADDRESS, { filters: [ { dataSize: 165n }, { memcmp: { offset: 32n, bytes: wallet.toString(), encoding: 'base58' } }, ], encoding: 'jsonParsed', }) .send(); } // Type guard to check if account data is parsed function isParsedTokenAccountData(data) { return (data && typeof data === 'object' && 'parsed' in data && data.parsed && typeof data.parsed === 'object' && 'info' in data.parsed && data.parsed.info && typeof data.parsed.info === 'object' && 'mint' in data.parsed.info && 'tokenAmount' in data.parsed.info); } // Helper function to safely get mint from parsed token account function getTokenAccountMint(accountData) { if (isParsedTokenAccountData(accountData)) { return accountData.parsed.info.mint; } return null; } // Helper function to safely get token amount from parsed token account function getTokenAccountAmount(accountData) { if (isParsedTokenAccountData(accountData)) { return accountData.parsed.info.tokenAmount.uiAmount; } return null; } //# sourceMappingURL=ata.js.map