@mihalex/farms-sdk-tests
Version:
468 lines • 21.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertAmountToStake = exports.convertStakeToAmount = exports.scaleDownWads = exports.sleep = exports.buildAndSendTxnWithLogs = exports.createFarmPublicKeyRentExempt = exports.createGlobalConfigPublicKeyRentExempt = exports.createKeypairRentExemptIx = exports.createKeypairRentExempt = exports.getGlobalConfigValue = exports.sendAndConfirmInstructions = exports.getUserStatePDA = exports.getRewardVaultPDA = exports.getFarmVaultPDA = exports.getFarmAuthorityPDA = exports.getTreasuryAuthorityPDA = exports.getTreasuryVaultPDA = exports.fetchUserStateWithRetry = exports.fetchGlobalConfigWithRetry = exports.fetchFarmStateWithRetry = exports.accountExist = exports.u16ToBytes = exports.createAddExtraComputeUnitsTransaction = exports.pubkeyFromFile = exports.endpointFromCluster = exports.getFarmsProgramId = exports.getSolBalance = exports.getSolBalanceInLamports = exports.getTokenAccountBalance = exports.mapAnchorError = exports.getCustomProgramErrorCode = exports.transferToken = exports.mintTo = exports.setupAta = exports.createAtaInstruction = exports.getAssociatedTokenAddress = exports.checkIfAccountExists = exports.solAirdropMin = exports.solAirdrop = exports.createMintFromKeypair = exports.createMint = exports.lamportsToCollDecimal = exports.collToLamportsDecimal = exports.parseKeypairFile = exports.WAD = exports.FarmsIdl = void 0;
const anchor = __importStar(require("@coral-xyz/anchor"));
const fs = __importStar(require("fs"));
const FarmsErrors = __importStar(require("../rpc_client/errors"));
const serum_1 = require("@project-serum/serum");
const token_instructions_1 = require("@project-serum/serum/lib/token-instructions");
const web3_js_1 = require("@solana/web3.js");
const decimal_js_1 = require("decimal.js");
const spl_token_1 = require("@solana/spl-token");
const web3 = __importStar(require("@solana/web3.js"));
const setup_1 = require("./setup");
const accounts_1 = require("../rpc_client/accounts");
const farms_json_1 = __importDefault(require("../rpc_client/farms.json"));
const Farms_1 = require("../Farms");
exports.FarmsIdl = farms_json_1.default;
exports.WAD = new decimal_js_1.Decimal("1".concat(Array(18 + 1).join("0")));
function parseKeypairFile(file) {
return web3_js_1.Keypair.fromSecretKey(Buffer.from(JSON.parse(require("fs").readFileSync(file))));
}
exports.parseKeypairFile = parseKeypairFile;
function collToLamportsDecimal(amount, decimals) {
let factor = Math.pow(10, decimals);
return amount.mul(factor);
}
exports.collToLamportsDecimal = collToLamportsDecimal;
function lamportsToCollDecimal(amount, decimals) {
let factor = Math.pow(10, decimals);
return amount.div(factor);
}
exports.lamportsToCollDecimal = lamportsToCollDecimal;
async function createMint(provider, authority, decimals = 6) {
const mint = anchor.web3.Keypair.generate();
return await createMintFromKeypair(provider, authority, mint, decimals);
}
exports.createMint = createMint;
async function createMintFromKeypair(provider, authority, mint, decimals = 6) {
const instructions = await createMintInstructions(provider, authority, mint.publicKey, decimals);
const tx = new anchor.web3.Transaction();
tx.add(...instructions);
await provider.sendAndConfirm(tx, [mint]);
return mint.publicKey;
}
exports.createMintFromKeypair = createMintFromKeypair;
async function createMintInstructions(provider, authority, mint, decimals) {
return [
anchor.web3.SystemProgram.createAccount({
fromPubkey: provider.wallet.publicKey,
newAccountPubkey: mint,
space: 82,
lamports: await provider.connection.getMinimumBalanceForRentExemption(82),
programId: token_instructions_1.TOKEN_PROGRAM_ID,
}),
serum_1.TokenInstructions.initializeMint({
mint,
decimals,
mintAuthority: authority,
}),
];
}
async function solAirdrop(provider, account, solAirdrop) {
const airdropTxnId = await provider.connection.requestAirdrop(account, collToLamportsDecimal(solAirdrop, 9).toNumber());
await provider.connection.confirmTransaction(airdropTxnId);
return await getSolBalance(provider, account);
}
exports.solAirdrop = solAirdrop;
async function solAirdropMin(provider, account, minSolAirdrop) {
const airdropBatchAmount = decimal_js_1.Decimal.max(50, minSolAirdrop);
let currentBalance = await getSolBalance(provider, account);
while (currentBalance.lt(minSolAirdrop)) {
try {
await provider.connection.requestAirdrop(account, collToLamportsDecimal(airdropBatchAmount, 9).toNumber());
}
catch (e) {
await sleep(100);
console.log("Error", e);
}
await sleep(100);
currentBalance = await getSolBalance(provider, account);
}
return currentBalance;
}
exports.solAirdropMin = solAirdropMin;
async function checkIfAccountExists(provider, account) {
return (await provider.connection.getAccountInfo(account)) != null;
}
exports.checkIfAccountExists = checkIfAccountExists;
async function getAssociatedTokenAddress(owner, tokenMintAddress) {
return await spl_token_1.Token.getAssociatedTokenAddress(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID
token_instructions_1.TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID
tokenMintAddress, // mint
owner, // owner
true);
}
exports.getAssociatedTokenAddress = getAssociatedTokenAddress;
async function createAtaInstruction(owner, tokenMintAddress, ata) {
return await spl_token_1.Token.createAssociatedTokenAccountInstruction(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, // always ASSOCIATED_TOKEN_PROGRAM_ID
token_instructions_1.TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID
tokenMintAddress, // mint
ata, // ata
owner, // owner of token account
owner);
}
exports.createAtaInstruction = createAtaInstruction;
async function setupAta(provider, tokenMintAddress, user) {
const ata = await getAssociatedTokenAddress(user.publicKey, tokenMintAddress);
if (!(await checkIfAccountExists(provider, ata))) {
const ix = await createAtaInstruction(user.publicKey, tokenMintAddress, ata);
const tx = new web3_js_1.Transaction().add(ix);
await provider.connection.sendTransaction(tx, [user]);
}
return ata;
}
exports.setupAta = setupAta;
async function mintTo(provider, mintPubkey, tokenAccount, amount) {
const tx = new web3_js_1.Transaction().add(spl_token_1.Token.createMintToInstruction(token_instructions_1.TOKEN_PROGRAM_ID, // always TOKEN_PROGRAM_ID
mintPubkey, // mint
tokenAccount, // receiver (sholud be a token account)
provider.wallet.publicKey, // mint authority
[], // only multisig account will use. leave it empty now.
amount));
await provider.sendAndConfirm(tx);
}
exports.mintTo = mintTo;
async function transferToken(provider, fromAccount, fromTokenAccount, toTokenAccount, amount) {
let tx = new web3_js_1.Transaction().add(spl_token_1.Token.createTransferInstruction(token_instructions_1.TOKEN_PROGRAM_ID, fromTokenAccount, toTokenAccount, fromAccount.publicKey, [], amount));
await web3.sendAndConfirmTransaction(provider.connection, tx, [fromAccount]);
await sleep(500);
}
exports.transferToken = transferToken;
/**
* 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) {
let error;
if (!![Number(errorCode)]) {
error = FarmsErrors.fromCode(Number(errorCode));
throw new Error(error);
}
else if (Number(errorCode) >= 6000 && Number(errorCode) <= 7000) {
errorCode[errorCode.length - 2] === "0"
? (errorCode = errorCode.slice(-1))
: (errorCode = errorCode.slice(-2));
// @ts-ignore
error = exports.FarmsIdl.errors[errorCode].msg;
throw new Error(error);
}
else {
throw new Error(e);
}
}
throw e;
}
}
exports.mapAnchorError = mapAnchorError;
async function getTokenAccountBalance(provider, tokenAccount) {
const tokenAccountBalance = await provider.connection.getTokenAccountBalance(tokenAccount);
return new decimal_js_1.Decimal(tokenAccountBalance.value.amount).div(decimal_js_1.Decimal.pow(10, tokenAccountBalance.value.decimals));
}
exports.getTokenAccountBalance = getTokenAccountBalance;
async function getSolBalanceInLamports(provider, account) {
let balance = undefined;
while (balance === undefined) {
balance = (await provider.connection.getAccountInfo(account))?.lamports;
}
return balance;
}
exports.getSolBalanceInLamports = getSolBalanceInLamports;
async function getSolBalance(provider, account) {
const balance = new decimal_js_1.Decimal(await getSolBalanceInLamports(provider, account));
return lamportsToCollDecimal(balance, 9);
}
exports.getSolBalance = getSolBalance;
function getFarmsProgramId(cluster) {
return new web3_js_1.PublicKey("FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr");
}
exports.getFarmsProgramId = getFarmsProgramId;
function endpointFromCluster(cluster) {
switch (cluster) {
case "mainnet":
return "https://hubble-dedi.rpcpool.com/98e7842fbc63114f80adf2810a80";
case "devnet":
return "https://hubblep-develope-bbc5.devnet.rpcpool.com/5849b35e-9963-46d9-b1d9-3c09dd4f6bdd";
case "localnet":
return "http://127.0.0.1:8899";
}
return "err";
}
exports.endpointFromCluster = endpointFromCluster;
function pubkeyFromFile(filepath) {
const fileContents = fs.readFileSync(filepath, "utf8");
const privateArray = fileContents
.replace("[", "")
.replace("]", "")
.split(",")
.map(function (item) {
return parseInt(item, 10);
});
const array = Uint8Array.from(privateArray);
const keypair = web3_js_1.Keypair.fromSecretKey(array);
return keypair.publicKey;
}
exports.pubkeyFromFile = pubkeyFromFile;
function createAddExtraComputeUnitsTransaction(owner, units) {
return web3.ComputeBudgetProgram.setComputeUnitLimit({ units });
}
exports.createAddExtraComputeUnitsTransaction = createAddExtraComputeUnitsTransaction;
function u16ToBytes(num) {
const arr = new ArrayBuffer(2);
const view = new DataView(arr);
view.setUint16(0, num, false);
return new Uint8Array(arr);
}
exports.u16ToBytes = u16ToBytes;
async function accountExist(connection, account) {
const info = await connection.getAccountInfo(account);
if (info === null || info.data.length === 0) {
return false;
}
return true;
}
exports.accountExist = accountExist;
async function fetchFarmStateWithRetry(env, address) {
return fetchWithRetry(async () => await accounts_1.FarmState.fetch(env.provider.connection, address), address);
}
exports.fetchFarmStateWithRetry = fetchFarmStateWithRetry;
async function fetchGlobalConfigWithRetry(env, address) {
return fetchWithRetry(async () => await accounts_1.GlobalConfig.fetch(env.provider.connection, address), address);
}
exports.fetchGlobalConfigWithRetry = fetchGlobalConfigWithRetry;
async function fetchUserStateWithRetry(env, address) {
return fetchWithRetry(async () => await accounts_1.UserState.fetch(env.provider.connection, address), address);
}
exports.fetchUserStateWithRetry = fetchUserStateWithRetry;
function getTreasuryVaultPDA(programId, globalConfig, rewardMint) {
const [treasuryVault, _rewardTreasuryVaultBump] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from("tvault"), globalConfig.toBuffer(), rewardMint.toBuffer()], programId);
return treasuryVault;
}
exports.getTreasuryVaultPDA = getTreasuryVaultPDA;
function getTreasuryAuthorityPDA(programId, globalConfig) {
const [treasuryAuthority, _treasuryAuthorityBump] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from("authority"), globalConfig.toBuffer()], programId);
return treasuryAuthority;
}
exports.getTreasuryAuthorityPDA = getTreasuryAuthorityPDA;
function getFarmAuthorityPDA(programId, farmState) {
const [farmAuthority, _farmAuthorityBump] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from("authority"), farmState.toBuffer()], programId);
return farmAuthority;
}
exports.getFarmAuthorityPDA = getFarmAuthorityPDA;
function getFarmVaultPDA(programId, farmState, tokenMint) {
const [farmVault, _farmVaultBump] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from("fvault"), farmState.toBuffer(), tokenMint.toBuffer()], programId);
return farmVault;
}
exports.getFarmVaultPDA = getFarmVaultPDA;
function getRewardVaultPDA(programId, farmState, rewardMint) {
const [rewardVault, _rewardVaultBump] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from("rvault"), farmState.toBuffer(), rewardMint.toBuffer()], programId);
return rewardVault;
}
exports.getRewardVaultPDA = getRewardVaultPDA;
function getUserStatePDA(programId, farmState, owner) {
const [userState, _userStateBump] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from("user"), farmState.toBuffer(), owner.toBuffer()], programId);
return userState;
}
exports.getUserStatePDA = getUserStatePDA;
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;
}
async function sendAndConfirmInstructions(env, ixns) {
let tx = new web3_js_1.Transaction();
for (let i = 0; i < ixns.length; i++) {
tx.add(ixns[i]);
}
let { blockhash } = await env.provider.connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;
tx.feePayer = env.initialOwner.publicKey;
return await web3.sendAndConfirmTransaction(env.provider.connection, tx, [
env.initialOwner,
]);
}
exports.sendAndConfirmInstructions = sendAndConfirmInstructions;
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 = new web3_js_1.PublicKey(flagValue);
}
else {
throw new Error("flagValueType must be 'number', 'bool', or 'publicKey'");
}
let buffer;
if (value instanceof web3_js_1.PublicKey) {
buffer = value.toBuffer();
}
else if (typeof value === "boolean") {
buffer = Buffer.alloc(32);
value ? buffer.writeUInt8(1, 0) : buffer.writeUInt8(0, 0);
}
else if (typeof value === "bigint") {
buffer = Buffer.alloc(32);
buffer.writeBigUInt64LE(value); // 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];
}
exports.getGlobalConfigValue = getGlobalConfigValue;
async function createKeypairRentExempt(provider, programId, address, size) {
const tx = new web3_js_1.Transaction();
tx.add(await createKeypairRentExemptIx(provider.connection, provider.wallet.publicKey, address, size, programId));
await provider.sendAndConfirm(tx, [address]);
return address;
}
exports.createKeypairRentExempt = createKeypairRentExempt;
async function createKeypairRentExemptIx(connection, payer, account, size, programId = Farms_1.farmsId) {
return web3_js_1.SystemProgram.createAccount({
fromPubkey: payer,
newAccountPubkey: account.publicKey,
space: size,
lamports: await connection.getMinimumBalanceForRentExemption(size),
programId: programId,
});
}
exports.createKeypairRentExemptIx = createKeypairRentExemptIx;
async function createGlobalConfigPublicKeyRentExempt(provider, programId) {
const config = web3_js_1.Keypair.generate();
const key = await createKeypairRentExempt(provider, programId, config, setup_1.SIZE_GLOBAL_CONFIG);
return key;
}
exports.createGlobalConfigPublicKeyRentExempt = createGlobalConfigPublicKeyRentExempt;
async function createFarmPublicKeyRentExempt(provider, programId) {
const farm = web3_js_1.Keypair.generate();
const key = await createKeypairRentExempt(provider, programId, farm, setup_1.SIZE_FARM_STATE);
return key;
}
exports.createFarmPublicKeyRentExempt = createFarmPublicKeyRentExempt;
async function buildAndSendTxnWithLogs(c, tx, owner, signers) {
const { blockhash } = await c.getLatestBlockhash();
tx.recentBlockhash = blockhash;
tx.feePayer = owner.publicKey;
try {
const sig = await c.sendTransaction(tx, [owner, ...signers]);
console.log("Transaction Hash:", sig);
await sleep(5000);
const res = await c.getTransaction(sig, {
commitment: "confirmed",
});
console.log("Transaction Logs:\n", res.meta.logMessages);
}
catch (e) {
console.log(e);
await sleep(5000);
const sig = e.toString().split(" failed ")[0].split("Transaction ")[1];
const res = await c.getTransaction(sig, {
commitment: "confirmed",
});
console.log("Txn", res.meta.logMessages);
}
}
exports.buildAndSendTxnWithLogs = buildAndSendTxnWithLogs;
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
exports.sleep = sleep;
function scaleDownWads(value) {
return new decimal_js_1.Decimal(value.toString()).div(exports.WAD).toNumber();
}
exports.scaleDownWads = scaleDownWads;
function convertStakeToAmount(stake, totalStaked, totalAmount) {
if (stake === new decimal_js_1.Decimal(0)) {
return new decimal_js_1.Decimal(0);
}
if (totalStaked !== new decimal_js_1.Decimal(0)) {
return stake.mul(totalAmount).div(totalStaked);
}
else {
return stake.add(totalAmount);
}
}
exports.convertStakeToAmount = convertStakeToAmount;
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;
}
}
exports.convertAmountToStake = convertAmountToStake;
//# sourceMappingURL=utils.js.map