@kamino-finance/klend-sdk
Version:
Typescript SDK for interacting with the Kamino Lending (klend) protocol
168 lines • 7.91 kB
JavaScript
"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