UNPKG

@mihalex/farms-sdk-tests

Version:
468 lines 21.2 kB
"use strict"; 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