UNPKG

@kamino-finance/klend-sdk

Version:

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

168 lines 7.91 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isWsolInfoInvalid = exports.createWsolAtaIfMissing = exports.getAtasWithCreateIxsIfMissing = void 0; exports.createAssociatedTokenAccountIdempotentInstruction = createAssociatedTokenAccountIdempotentInstruction; exports.getAssociatedTokenAddress = getAssociatedTokenAddress; exports.createAtasIdempotent = createAtasIdempotent; exports.getTransferWsolIxs = getTransferWsolIxs; exports.getTokenAccountBalance = getTokenAccountBalance; exports.getTokenAccountBalanceDecimal = getTokenAccountBalanceDecimal; const spl_token_1 = require("@solana/spl-token"); const web3_js_1 = require("@solana/web3.js"); const decimal_js_1 = __importDefault(require("decimal.js")); const dist_1 = require("@kamino-finance/kliquidity-sdk/dist"); /** * 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 */ function createAssociatedTokenAccountIdempotentInstruction(owner, mint, payer = owner, tokenProgram = spl_token_1.TOKEN_PROGRAM_ID, ata) { let ataAddress = ata; if (!ataAddress) { ataAddress = getAssociatedTokenAddress(mint, owner, true, tokenProgram, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID); } const createUserTokenAccountIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(payer, ataAddress, owner, mint, tokenProgram, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID); return [ataAddress, createUserTokenAccountIx]; } function getAssociatedTokenAddress(mint, owner, allowOwnerOffCurve = true, programId = spl_token_1.TOKEN_PROGRAM_ID, associatedTokenProgramId = spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID) { if (!allowOwnerOffCurve && !web3_js_1.PublicKey.isOnCurve(owner.toBuffer())) throw new Error('Token owner off curve'); const [address] = web3_js_1.PublicKey.findProgramAddressSync([owner.toBuffer(), programId.toBuffer(), mint.toBuffer()], associatedTokenProgramId); return address; } const getAtasWithCreateIxsIfMissing = async (connection, user, mints) => { const atas = mints.map((x) => getAssociatedTokenAddress(x.mint, user, true, x.tokenProgram)); const accountInfos = await connection.getMultipleAccountsInfo(atas); const createAtaIxs = []; for (let i = 0; i < atas.length; i++) { if (!accountInfos[i]) { const { mint, tokenProgram } = mints[i]; const [ata, createIxn] = createAssociatedTokenAccountIdempotentInstruction(user, mint, user, tokenProgram); atas[i] = ata; createAtaIxs.push(createIxn); } } return { atas, createAtaIxs, }; }; exports.getAtasWithCreateIxsIfMissing = getAtasWithCreateIxsIfMissing; function createAtasIdempotent(user, mints) { const res = []; for (const mint of mints) { const [ata, createAtaIx] = createAssociatedTokenAccountIdempotentInstruction(user, mint.mint, user, mint.tokenProgram); res.push({ ata, createAtaIx, }); } return res; } function getTransferWsolIxs(owner, ata, amountLamports) { const ixs = []; ixs.push(web3_js_1.SystemProgram.transfer({ fromPubkey: owner, toPubkey: ata, lamports: amountLamports.toNumber(), })); ixs.push(new web3_js_1.TransactionInstruction({ keys: [ { pubkey: ata, isSigner: false, isWritable: true, }, ], data: Buffer.from(new Uint8Array([17])), programId: spl_token_1.TOKEN_PROGRAM_ID, })); return ixs; } async function getTokenAccountBalance(connection, tokenAccount) { const tokenAccountBalance = await connection.getTokenAccountBalance(tokenAccount); return Number(tokenAccountBalance.value.amount).valueOf(); } /// Get the balance of a token account in decimal format (tokens, not lamports) async function getTokenAccountBalanceDecimal(connection, mint, owner, tokenProgram = spl_token_1.TOKEN_PROGRAM_ID) { const ata = getAssociatedTokenAddress(mint, owner, true, tokenProgram); const accInfo = await connection.getAccountInfo(ata); if (accInfo === null) { return new decimal_js_1.default('0'); } const { value } = await connection.getTokenAccountBalance(ata); 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 connection - Solana RPC connection (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 (connection, amount, owner) => { const createIxs = []; const closeIxs = []; const wsolAta = (0, spl_token_1.getAssociatedTokenAddressSync)(spl_token_1.NATIVE_MINT, owner, true, spl_token_1.TOKEN_PROGRAM_ID); const solDeposit = amount; const wsolAtaAccountInfo = await connection.getAccountInfo(wsolAta); // This checks if we need to create it if ((0, exports.isWsolInfoInvalid)(wsolAtaAccountInfo)) { createIxs.push((0, spl_token_1.createAssociatedTokenAccountInstruction)(owner, wsolAta, owner, spl_token_1.NATIVE_MINT, spl_token_1.TOKEN_PROGRAM_ID)); } let wsolExistingBalanceLamports = new decimal_js_1.default(0); try { if (wsolAtaAccountInfo != null) { const uiAmount = (await getTokenAccountBalanceDecimal(connection, spl_token_1.NATIVE_MINT, owner)).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(web3_js_1.SystemProgram.transfer({ fromPubkey: owner, toPubkey: wsolAta, lamports: BigInt(solDeposit.sub(wsolExistingBalanceLamports).floor().toString()), })); } if (createIxs.length > 0) { // Primitive way of wrapping SOL createIxs.push(new web3_js_1.TransactionInstruction({ keys: [ { pubkey: wsolAta, isSigner: false, isWritable: true, }, ], data: Buffer.from(new Uint8Array([17])), programId: spl_token_1.TOKEN_PROGRAM_ID, })); } closeIxs.push((0, spl_token_1.createCloseAccountInstruction)(wsolAta, owner, owner, [], spl_token_1.TOKEN_PROGRAM_ID)); return { wsolAta, createAtaIxs: createIxs, closeAtaIxs: closeIxs, }; }; exports.createWsolAtaIfMissing = createWsolAtaIfMissing; const isWsolInfoInvalid = (wsolAtaAccountInfo) => { const res = wsolAtaAccountInfo === null || (wsolAtaAccountInfo !== null && wsolAtaAccountInfo.data.length === 0 && wsolAtaAccountInfo.owner.eq(web3_js_1.PublicKey.default)); return res; }; exports.isWsolInfoInvalid = isWsolInfoInvalid; //# sourceMappingURL=ata.js.map