@kamino-finance/farms-sdk
Version:
335 lines • 12.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseTokenSymbol = exports.getCustomProgramErrorCode = exports.WAD = void 0;
exports.collToLamportsDecimal = collToLamportsDecimal;
exports.lamportsToCollDecimal = lamportsToCollDecimal;
exports.decimalToBN = decimalToBN;
exports.checkIfAccountExists = checkIfAccountExists;
exports.mapAnchorError = mapAnchorError;
exports.getTokenAccountBalance = getTokenAccountBalance;
exports.getTokenAccountBalanceLamports = getTokenAccountBalanceLamports;
exports.getSolBalanceInLamports = getSolBalanceInLamports;
exports.getSolBalance = getSolBalance;
exports.createAddExtraComputeUnitsTransaction = createAddExtraComputeUnitsTransaction;
exports.u16ToBytes = u16ToBytes;
exports.accountExist = accountExist;
exports.fetchFarmStateWithRetry = fetchFarmStateWithRetry;
exports.fetchGlobalConfigWithRetry = fetchGlobalConfigWithRetry;
exports.fetchUserStateWithRetry = fetchUserStateWithRetry;
exports.getTreasuryVaultPDA = getTreasuryVaultPDA;
exports.getTreasuryAuthorityPDA = getTreasuryAuthorityPDA;
exports.getFarmAuthorityPDA = getFarmAuthorityPDA;
exports.getFarmVaultPDA = getFarmVaultPDA;
exports.getRewardVaultPDA = getRewardVaultPDA;
exports.getUserStatePDA = getUserStatePDA;
exports.getGlobalConfigValue = getGlobalConfigValue;
exports.createKeypairRentExemptIx = createKeypairRentExemptIx;
exports.sleep = sleep;
exports.scaleDownWads = scaleDownWads;
exports.convertAmountToStake = convertAmountToStake;
exports.retryAsync = retryAsync;
exports.noopProfiledFunctionExecution = noopProfiledFunctionExecution;
exports.isValidPubkey = isValidPubkey;
const farms_1 = require("../@codegen/farms/errors/farms");
const kit_1 = require("@solana/kit");
const decimal_js_1 = require("decimal.js");
const accounts_1 = require("../@codegen/farms/accounts");
const system_1 = require("@solana-program/system");
const programs_1 = require("../@codegen/farms/programs");
const compute_budget_1 = require("@solana-program/compute-budget");
const pubkey_1 = require("./pubkey");
exports.WAD = new decimal_js_1.Decimal("1".concat(Array(18 + 1).join("0")));
const addressEncoder = (0, kit_1.getAddressEncoder)();
function collToLamportsDecimal(amount, decimals) {
const factor = Math.pow(10, decimals);
return new decimal_js_1.Decimal(amount).mul(factor);
}
function lamportsToCollDecimal(amount, decimals) {
const factor = Math.pow(10, decimals);
return new decimal_js_1.Decimal(amount).div(factor);
}
function decimalToBN(value) {
// Note: the `Decimal.toString()` can return exponential notation (e.g. "1e9") for large numbers. This notation is
// not accepted by `BigInt` constructor (i.e. invalid character "e"). Hence, we use `Decimal.toFixed()` (which is
// different than `number.toFixed()` - it will not do any rounding, just render a normal notation).
// see https://mikemcl.github.io/decimal.js/#toFixed
return BigInt(value.toFixed());
}
async function checkIfAccountExists(connection, account) {
return ((await connection.getAccountInfo(account, { encoding: "base64" }).send())
.value != null);
}
/**
* Get the custom program error code if there's any in the error message and return parsed error code hex to number string
* @param errMessage string - error message that would contain the word "custom program error:" if it's a customer program error
* @returns [boolean, string] - probably not a custom program error if false otherwise the second element will be the code number in string
*/
const getCustomProgramErrorCode = (errMessage) => {
const index = errMessage.indexOf("Custom program error:");
if (index === -1) {
return [false, "May not be a custom program error"];
}
else {
return [
true,
`${parseInt(errMessage.substring(index + 22, index + 28).replace(" ", ""), 16)}`,
];
}
};
exports.getCustomProgramErrorCode = getCustomProgramErrorCode;
/**
*
* Maps the private Anchor type ProgramError to a normal Error.
* Pass ProgramErr.msg as the Error message so that it can be used with chai matchers
*
* @param fn - function which may throw an anchor ProgramError
*/
async function mapAnchorError(fn) {
try {
return await fn;
}
catch (e) {
let [isCustomProgramError, errorCode] = (0, exports.getCustomProgramErrorCode)(JSON.stringify(e));
if (isCustomProgramError) {
if (!isNaN(Number(errorCode))) {
const errorMessage = (0, farms_1.getFarmsErrorMessage)(Number(errorCode));
if (errorMessage) {
throw new Error(errorMessage);
}
}
throw new Error(e);
}
throw e;
}
}
async function getTokenAccountBalance(rpc, tokenAccount) {
const tokenAccountBalance = await rpc
.getTokenAccountBalance(tokenAccount)
.send();
return new decimal_js_1.Decimal(tokenAccountBalance.value.amount).div(decimal_js_1.Decimal.pow(10, tokenAccountBalance.value.decimals));
}
async function getTokenAccountBalanceLamports(rpc, tokenAccount) {
const tokenAccountBalance = await rpc
.getTokenAccountBalance(tokenAccount)
.send();
return new decimal_js_1.Decimal(tokenAccountBalance.value.amount).toNumber();
}
async function getSolBalanceInLamports(rpc, account) {
let balance = undefined;
while (balance === undefined) {
balance = (await rpc.getBalance(account).send()).value;
}
return balance;
}
async function getSolBalance(rpc, account) {
const balance = new decimal_js_1.Decimal((await getSolBalanceInLamports(rpc, account)).toString());
return lamportsToCollDecimal(balance, 9);
}
function createAddExtraComputeUnitsTransaction(units) {
return (0, compute_budget_1.getSetComputeUnitLimitInstruction)({ units });
}
function u16ToBytes(num) {
const arr = new ArrayBuffer(2);
const view = new DataView(arr);
view.setUint16(0, num, false);
return new Uint8Array(arr);
}
async function accountExist(rpc, account) {
const info = await rpc.getAccountInfo(account, { encoding: "base64" }).send();
if (info.value === null || info.value.data[0].length === 0) {
return false;
}
return true;
}
async function fetchFarmStateWithRetry(rpc, addr) {
return fetchWithRetry(async () => {
const account = await (0, accounts_1.fetchMaybeFarmState)(rpc, addr);
return account.exists ? account.data : null;
}, addr);
}
async function fetchGlobalConfigWithRetry(rpc, addr) {
const result = await fetchWithRetry(async () => {
const account = await (0, accounts_1.fetchMaybeGlobalConfig)(rpc, addr);
return account.exists ? account.data : null;
}, addr);
if (result === null) {
throw new Error(`GlobalConfig account ${addr} not found after retries`);
}
return result;
}
async function fetchUserStateWithRetry(rpc, addr) {
const result = await fetchWithRetry(async () => {
const account = await (0, accounts_1.fetchMaybeUserState)(rpc, addr);
return account.exists ? account.data : null;
}, addr);
if (result === null) {
throw new Error(`UserState account ${addr} not found after retries`);
}
return result;
}
async function getTreasuryVaultPDA(programId, globalConfig, rewardMint) {
const [treasuryVault] = await (0, kit_1.getProgramDerivedAddress)({
seeds: [
new TextEncoder().encode("tvault"),
addressEncoder.encode(globalConfig),
addressEncoder.encode(rewardMint),
],
programAddress: programId,
});
return treasuryVault;
}
async function getTreasuryAuthorityPDA(farmsProgramId, globalConfig) {
const [treasuryAuthority] = await (0, kit_1.getProgramDerivedAddress)({
seeds: [
new TextEncoder().encode("authority"),
addressEncoder.encode(globalConfig),
],
programAddress: farmsProgramId,
});
return treasuryAuthority;
}
async function getFarmAuthorityPDA(farmsProgramId, farmState) {
const [farmAuthority] = await (0, kit_1.getProgramDerivedAddress)({
seeds: [
new TextEncoder().encode("authority"),
addressEncoder.encode(farmState),
],
programAddress: farmsProgramId,
});
return farmAuthority;
}
async function getFarmVaultPDA(farmsProgramId, farmState, tokenMint) {
const [farmVault] = await (0, kit_1.getProgramDerivedAddress)({
seeds: [
new TextEncoder().encode("fvault"),
addressEncoder.encode(farmState),
addressEncoder.encode(tokenMint),
],
programAddress: farmsProgramId,
});
return farmVault;
}
async function getRewardVaultPDA(programId, farmState, rewardMint) {
const [rewardVault] = await (0, kit_1.getProgramDerivedAddress)({
seeds: [
new TextEncoder().encode("rvault"),
addressEncoder.encode(farmState),
addressEncoder.encode(rewardMint),
],
programAddress: programId,
});
return rewardVault;
}
async function getUserStatePDA(programId, farmState, owner) {
const [userState] = await (0, kit_1.getProgramDerivedAddress)({
seeds: [
new TextEncoder().encode("user"),
addressEncoder.encode(farmState),
addressEncoder.encode(owner),
],
programAddress: programId,
});
return userState;
}
async function fetchWithRetry(fetch, address, retries = 3) {
for (let i = 0; i < retries; i++) {
let resp = await fetch();
if (resp !== null) {
return resp;
}
console.log(`[${i + 1}/${retries}] Fetched account ${address} is null. Refetching...`);
}
return null;
}
function getGlobalConfigValue(flagValueType, flagValue) {
let value;
if (flagValueType === "number") {
value = BigInt(flagValue);
}
else if (flagValueType === "bool") {
if (flagValue === "false") {
value = false;
}
else if (flagValue === "true") {
value = true;
}
else {
throw new Error("the provided flag value is not valid bool");
}
}
else if (flagValueType === "publicKey") {
value = (0, kit_1.address)(flagValue);
}
else {
throw new Error("flagValueType must be 'number', 'bool', or 'publicKey'");
}
let buffer;
if (typeof value === "string" && (0, kit_1.isAddress)(value)) {
buffer = new Uint8Array(addressEncoder.encode(value));
}
else if (typeof value === "boolean") {
buffer = new Uint8Array(32);
buffer[0] = value ? 1 : 0;
}
else if (typeof value === "bigint") {
buffer = new Uint8Array(32);
const view = new DataView(buffer.buffer);
view.setBigUint64(0, value, true); // Because we send 32 bytes and a u64 has 8 bytes, we write it in LE
}
else {
throw Error("wrong type for value");
}
return [...buffer];
}
async function createKeypairRentExemptIx(rpc, payer, account, size, programId = programs_1.FARMS_PROGRAM_ADDRESS) {
return (0, system_1.getCreateAccountInstruction)({
payer: payer,
space: size,
lamports: await rpc.getMinimumBalanceForRentExemption(size).send(),
programAddress: programId,
newAccount: account,
});
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function scaleDownWads(value) {
return new decimal_js_1.Decimal(value.toString()).div(exports.WAD).toNumber();
}
function convertAmountToStake(amount, totalStaked, totalAmount) {
if (amount === new decimal_js_1.Decimal(0)) {
return new decimal_js_1.Decimal(0);
}
if (totalAmount !== new decimal_js_1.Decimal(0)) {
return totalStaked.mul(amount).div(totalAmount);
}
else {
return amount;
}
}
const parseTokenSymbol = (tokenSymbol) => {
return String.fromCharCode(...tokenSymbol.filter((x) => x > 0));
};
exports.parseTokenSymbol = parseTokenSymbol;
async function retryAsync(fn, retriesLeft = 5, interval = 2000) {
try {
return await fn();
}
catch (error) {
if (retriesLeft) {
await new Promise((resolve) => setTimeout(resolve, interval));
return await retryAsync(fn, retriesLeft - 1, interval);
}
throw error;
}
}
function noopProfiledFunctionExecution(promise) {
return promise;
}
function isValidPubkey(address) {
if (!address) {
return false;
}
return address !== pubkey_1.DEFAULT_PUBLIC_KEY;
}
//# sourceMappingURL=utils.js.map