UNPKG

@mihalex/farms-sdk-tests

Version:
1,299 lines (1,129 loc) 33 kB
import { AnchorProvider, BN, Idl, Program, Provider } from "@coral-xyz/anchor"; import FARMS_IDL from "./rpc_client/farms.json"; import { Connection, GetProgramAccountsFilter, PublicKey, sendAndConfirmTransaction, Signer, Transaction, TransactionInstruction, TransactionSignature, } from "@solana/web3.js"; import { calculateCurrentRewardPerToken, calculatePendingRewards, getReadOnlyWallet, SIZE_FARM_STATE, SIZE_GLOBAL_CONFIG, } from "./utils"; import { getAssociatedTokenAddress, getTreasuryVaultPDA, getUserStatePDA, collToLamportsDecimal, getFarmVaultPDA, getFarmAuthorityPDA, getRewardVaultPDA, lamportsToCollDecimal, getTreasuryAuthorityPDA, createKeypairRentExemptIx, scaleDownWads, createAddExtraComputeUnitsTransaction, } from "./utils"; import { UserState, UserStateFields } from "./rpc_client/accounts"; import { PendingReward, UserFarm } from "./models"; import { FarmState, FarmStateFields, GlobalConfig, } from "./rpc_client/accounts"; import * as farmOperations from "./utils/operations"; import Decimal from "decimal.js"; import { Keypair } from "@solana/web3.js"; import { GlobalConfigOptionKind, FarmConfigOptionKind, TimeUnit, } from "./rpc_client/types/index"; import { FarmAndKey, UserAndKey } from "./models"; export const farmsId = new PublicKey( "FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr", ); export class Farms { private readonly _connection: Connection; private readonly _provider: Provider; private readonly _farmsProgram: Program; private readonly _farmsProgramId: PublicKey; constructor(connection: Connection) { this._connection = connection; this._provider = new AnchorProvider(connection, getReadOnlyWallet(), { commitment: connection.commitment, }); this._farmsProgramId = farmsId; this._farmsProgram = new Program( FARMS_IDL as Idl, this._farmsProgramId, this._provider, ); } getConnection() { return this._connection; } getProgramID() { return this._farmsProgramId; } getProgram() { return this._farmsProgram; } async getAllUserStatesForUser(user: PublicKey): Promise<Array<UserAndKey>> { let filters: GetProgramAccountsFilter[] = []; filters.push({ memcmp: { bytes: user.toBase58(), offset: 48, }, }); filters.push({ dataSize: UserState.layout.span + 8, }); const userStates = ( await this._farmsProgram.account.userState.all(filters) ).map((x) => { let res: UserAndKey = { userState: new UserState(x.account as unknown as UserStateFields), key: x.publicKey, }; return res; }); return userStates; } async getAllUserStates(): Promise<UserAndKey[]> { return ( await this._farmsProgram.account.userState.all([ { dataSize: UserState.layout.span + 8, }, ]) ).map((x) => { const userAndKey: UserAndKey = { userState: new UserState(x.account as unknown as UserStateFields), key: x.publicKey, }; return userAndKey; }); } async getFarmsForMint(mint: PublicKey): Promise<Array<FarmAndKey>> { let filters: GetProgramAccountsFilter[] = []; filters.push({ memcmp: { bytes: mint.toBase58(), offset: 72, }, }); filters.push({ dataSize: FarmState.layout.span + 8, }); const farms = (await this._farmsProgram.account.farmState.all(filters)).map( (x) => { let res: FarmAndKey = { farmState: new FarmState(x.account as unknown as FarmStateFields), key: x.publicKey, }; return res; }, ); return farms; } async getAllFarmStates(): Promise<FarmAndKey[]> { return ( await this._farmsProgram.account.farmState.all([ { dataSize: FarmState.layout.span + 8, }, ]) ).map((x): FarmAndKey => { const farmAndKey: FarmAndKey = { farmState: new FarmState(x.account as unknown as FarmStateFields), key: x.publicKey, }; return farmAndKey; }); } async getAllFarmStatesByPubkeys(keys: string[]): Promise<FarmAndKey[]> { return ( await this._farmsProgram.account.farmState.all([ { dataSize: FarmState.layout.span + 8, }, ]) ) .filter((x) => { return keys.includes(x.publicKey.toString()); }) .map((x) => { const farmState: FarmState = new FarmState( x.account as unknown as FarmStateFields, ); return { farmState, key: x.publicKey, }; }); } async getStakedAmountForMintForFarm( mint: PublicKey, farm: PublicKey, ): Promise<Decimal> { const farms = await this.getFarmsForMint(mint); for (let index = 0; index < farms.length; index++) { if (farms[index].key.toString() === farm.toString()) { return lamportsToCollDecimal( new Decimal( scaleDownWads(farms[index].farmState.totalActiveStakeScaled), ), farms[index].farmState.token.decimals.toNumber(), ); } } throw Error("No Farm found"); } async getStakedAmountForMint(mint: PublicKey): Promise<Decimal> { const farms = await this.getFarmsForMint(mint); let totalStaked = new Decimal(0); for (let index = 0; index < farms.length; index++) { totalStaked = totalStaked.add( lamportsToCollDecimal( new Decimal(farms[index].farmState.totalStakedAmount.toString()), farms[index].farmState.token.decimals.toNumber(), ), ); } return totalStaked; } async getUserStateKeyForUserForFarm( user: PublicKey, farm: PublicKey, ): Promise<Array<PublicKey>> { const userStates = await this.getAllUserStatesForUser(user); const userStateKeysForFarm: PublicKey[] = []; for (let index = 0; index < userStates.length; index++) { if ( userStates[index].userState.farmState.toString() === farm.toString() ) { userStateKeysForFarm.push(userStates[index].key); } } if (userStateKeysForFarm.length === 0) { throw Error("No user state found for user " + user + " for farm " + farm); } else { return userStateKeysForFarm; } } async getAllFarmsForUser(user: PublicKey): Promise<Map<string, UserFarm>> { const userStates = await this.getAllUserStatesForUser(user); const farmPks = new Array<string>(); for (let i = 0; i < userStates.length; i++) { farmPks[i] = userStates[i].userState.farmState.toString(); } const farmStates = await this.getAllFarmStatesByPubkeys(farmPks); if (!farmStates) { throw new Error("Error fetching farms"); } if (farmStates.length === 0) { // Return empty if no serializable farm states found return new Map<string, UserFarm>(); } const timestamp = new Decimal( (await this._connection.getBlockTime(await this._connection.getSlot()))!, ); const userFarms = new Map<string, UserFarm>(); for (let userState of userStates) { const userPendingRewardAmounts: Decimal[] = []; let farmState = farmStates.find( (farmState) => farmState.key.toString() === userState.userState.farmState.toString(), ); if (!farmState) { // Skip farms that are not serializable anymore continue; } let hasReward = false; // calculate userState pending rewards for ( let indexReward = 0; indexReward < farmState.farmState.rewardInfos.length; indexReward++ ) { userPendingRewardAmounts[indexReward] = calculatePendingRewards( farmState.farmState, userState.userState, indexReward, timestamp, ); if (userPendingRewardAmounts[indexReward].gt(0)) { hasReward = true; } } // add new userFarm state if non empty (has rewards or stake) and not already present if (!userFarms.has(userState.userState.farmState.toString())) { const userFarm: UserFarm = { farm: userState.userState.farmState, stakedToken: farmState.farmState.token.mint, activeStakeByDelegatee: new Map<string, Decimal>(), pendingDepositStakeByDelegatee: new Map<string, Decimal>(), pendingWithdrawalUnstakeByDelegatee: new Map<string, Decimal>(), pendingRewards: new Array(farmState.farmState.rewardInfos.length) .fill(undefined) .map(function () { return { rewardTokenMint: new PublicKey(0), cumulatedPendingRewards: new Decimal(0), pendingRewardsByDelegatee: new Map<string, Decimal>(), }; }), }; if ( new Decimal(scaleDownWads(userState.userState.activeStakeScaled)).gt( 0, ) || hasReward ) { userFarms.set(userState.userState.farmState.toString(), userFarm); } else { // skip as we are not accounting for empty userFarms continue; } } // add new userFarm state if non empty (has rewards or stake) and not already present const refUserFarm = Object.fromEntries(userFarms)[userState.userState.farmState.toString()]; if (!refUserFarm) { throw new Error("User farm state not loaded properly "); } const updatedUserFarm = { ...refUserFarm }; if ( userState.userState.delegatee.toString() in updatedUserFarm.activeStakeByDelegatee ) { console.error( "Delegatee for user for farm already present. There should be only one delegatee for this user for this farm", ); continue; } // active stake by delegatee updatedUserFarm.activeStakeByDelegatee.set( userState.userState.delegatee.toString(), lamportsToCollDecimal( new Decimal(scaleDownWads(userState.userState.activeStakeScaled)), farmState.farmState.token.decimals.toNumber(), ), ); // pendingDepositStake by delegatee updatedUserFarm.pendingDepositStakeByDelegatee.set( userState.userState.delegatee.toString(), new Decimal( scaleDownWads(userState.userState.pendingDepositStakeScaled), ), ); // pendingWithdrawalUnstake by delegatee updatedUserFarm.pendingWithdrawalUnstakeByDelegatee.set( userState.userState.delegatee.toString(), new Decimal( scaleDownWads(userState.userState.pendingWithdrawalUnstakeScaled), ), ); // cummulating rewards for ( let indexReward = 0; indexReward < farmState.farmState.rewardInfos.length; indexReward++ ) { updatedUserFarm.pendingRewards[indexReward].rewardTokenMint = farmState.farmState.rewardInfos[indexReward].token.mint; updatedUserFarm.pendingRewards[indexReward].cumulatedPendingRewards = updatedUserFarm.pendingRewards[ indexReward ].cumulatedPendingRewards.add(userPendingRewardAmounts[indexReward]); updatedUserFarm.pendingRewards[ indexReward ].pendingRewardsByDelegatee.set( userState.userState.delegatee.toString(), userPendingRewardAmounts[indexReward], ); } // set updated userFarm userFarms.set(userState.userState.farmState.toString(), updatedUserFarm); } return userFarms; } async executeTransaction( ix: TransactionInstruction[], signer: Keypair, extraSigners: Signer[] = [], ): Promise<TransactionSignature> { const tx = new Transaction(); let { blockhash } = await this._connection.getLatestBlockhash(); tx.recentBlockhash = blockhash; tx.feePayer = signer.publicKey; tx.add(...ix); let sig: TransactionSignature = await sendAndConfirmTransaction( this._connection, tx, [signer, ...extraSigners], { skipPreflight: true, commitment: "confirmed" }, ); return sig; } async createNewUserIx( user: PublicKey, farm: PublicKey, ): Promise<TransactionInstruction> { const userState = getUserStatePDA(this._farmsProgramId, farm, user); const ix = farmOperations.initializeUser(farm, user, userState); return ix; } async createNewUser( user: Keypair, farm: PublicKey, ): Promise<TransactionSignature> { const ix = await this.createNewUserIx(user.publicKey, farm); let sig = await this.executeTransaction([ix], user); const userState = getUserStatePDA( this._farmsProgramId, farm, user.publicKey, ); if (process.env.DEBUG === "true") { console.log("Initialize User: " + userState); console.log("Refresh Farm txn: " + sig.toString()); } return sig; } async stakeIx( user: PublicKey, farm: PublicKey, amountLamports: Decimal, stakeTokenMint: PublicKey, ): Promise<TransactionInstruction> { const farmVault = getFarmVaultPDA( this._farmsProgramId, farm, stakeTokenMint, ); const userStatePk = await getUserStatePDA(this._farmsProgramId, farm, user); const userTokenAta = await getAssociatedTokenAddress(user, stakeTokenMint); const ix = farmOperations.stake( user, userStatePk, userTokenAta, farm, farmVault, stakeTokenMint, new BN(amountLamports.toString()), ); return ix; } async stake( user: Keypair, farm: PublicKey, amountLamports: Decimal, stakeTokenMint: PublicKey, ): Promise<TransactionSignature> { const ix = await this.stakeIx( user.publicKey, farm, amountLamports, stakeTokenMint, ); let increaseComputeIx = createAddExtraComputeUnitsTransaction( user.publicKey, 400_000, ); let sig = await this.executeTransaction([increaseComputeIx, ix], user); if (process.env.DEBUG === "true") { console.log("User " + " stake " + amountLamports); console.log("Stake txn: " + sig.toString()); } return sig; } async unstakeIx( user: PublicKey, farm: PublicKey, amountLamports: string, ): Promise<TransactionInstruction> { const userStatePk = getUserStatePDA(this._farmsProgramId, farm, user); const ix = farmOperations.unstake( user, userStatePk, farm, new BN(amountLamports), ); return ix; } async unstake( user: Keypair, farm: PublicKey, sharesAmount: string, ): Promise<TransactionSignature> { const ix = await this.unstakeIx(user.publicKey, farm, sharesAmount); let sig = await this.executeTransaction([ix], user); if (process.env.DEBUG === "true") { console.log("Unstake " + sharesAmount); console.log("Unstake txn: " + sig.toString()); } return sig; } async withdrawUnstakedDepositIx( user: PublicKey, userState: PublicKey, farmState: PublicKey, stakeTokenMint: PublicKey, ): Promise<TransactionInstruction> { const userTokenAta = await getAssociatedTokenAddress(user, stakeTokenMint); const farmVault = getFarmVaultPDA( this._farmsProgramId, farmState, stakeTokenMint, ); const farmVaultsAuthority = getFarmAuthorityPDA( this._farmsProgramId, farmState, ); const ix = farmOperations.withdrawUnstakedDeposit( user, userState, farmState, userTokenAta, farmVault, farmVaultsAuthority, ); return ix; } async withdrawUnstakedDeposit( user: Keypair, farmState: PublicKey, tokenMint: PublicKey, userState: PublicKey, ): Promise<TransactionSignature> { const ix = await this.withdrawUnstakedDepositIx( user.publicKey, userState, farmState, tokenMint, ); let sig = await this.executeTransaction([ix], user); if (process.env.DEBUG === "true") { console.log("User " + userState + " withdraw unstaked deposit "); console.log("Withdraw Unstaked Deposit txn: " + sig.toString()); } return sig; } async claimForUserForFarmRewardIx( user: PublicKey, farm: PublicKey, rewardMint: PublicKey, rewardIndex = -1, ): Promise<TransactionInstruction[]> { const userStatePks = await this.getUserStateKeyForUserForFarm(user, farm); const farmState = await FarmState.fetch(this._connection, farm); if (!farmState) { throw new Error(`Farm not found ${farm.toString()}`); } const userRewardAta = await getAssociatedTokenAddress(user, rewardMint); const treasuryVault = getTreasuryVaultPDA( this._farmsProgramId, farmState.globalConfig, rewardMint, ); // find rewardIndex if not defined if (rewardIndex === -1) { for (let i = 0; farmState.rewardInfos.length; i++) { if ( farmState.rewardInfos[i].token.mint.toString() == rewardMint.toString() ) { rewardIndex = i; break; } } } const ixns: TransactionInstruction[] = []; for ( let userStateIndex = 0; userStateIndex < userStatePks.length; userStateIndex++ ) { const ix = farmOperations.harvestReward( user, userStatePks[userStateIndex], userRewardAta, farmState.globalConfig, treasuryVault, farm, farmState.rewardInfos[rewardIndex].rewardsVault, farmState.farmVaultsAuthority, rewardIndex, ); ixns.push(ix); } return ixns; } async claimForUserForFarmReward( user: Keypair, farm: PublicKey, rewardMint: PublicKey, rewardIndex = -1, ): Promise<TransactionSignature> { const ixns = await this.claimForUserForFarmRewardIx( user.publicKey, farm, rewardMint, rewardIndex, ); let sig = await this.executeTransaction(ixns, user); if (process.env.DEBUG === "true") { console.log("Harvest reward " + rewardIndex); console.log("HarvestReward txn: " + sig.toString()); } return sig; } async claimForUserForFarmAllRewardsIx( user: PublicKey, farm: PublicKey, ): Promise<Array<TransactionInstruction>> { const farmState = await FarmState.fetch(this._connection, farm); const userStatePks = await this.getUserStateKeyForUserForFarm(user, farm); const ixs = new Array<TransactionInstruction>(); if (!farmState) { throw new Error(`Farm not found ${farm.toString()}`); } for ( let userStateIndex = 0; userStateIndex < userStatePks.length; userStateIndex++ ) { for ( let rewardIndex = 0; rewardIndex < farmState.numRewardTokens.toNumber(); rewardIndex++ ) { const userRewardAta = await getAssociatedTokenAddress( user, farmState.rewardInfos[rewardIndex].token.mint, ); const treasuryVault = getTreasuryVaultPDA( this._farmsProgramId, farmState.globalConfig, farmState.rewardInfos[rewardIndex].token.mint, ); ixs.push( farmOperations.harvestReward( user, userStatePks[userStateIndex], userRewardAta, farmState.globalConfig, treasuryVault, farm, farmState.rewardInfos[rewardIndex].rewardsVault, farmState.farmVaultsAuthority, rewardIndex, ), ); } } return ixs; } async claimForUserForFarmAllRewards( user: Keypair, farm: PublicKey, ): Promise<Array<TransactionSignature>> { const ixs = await this.claimForUserForFarmAllRewardsIx( user.publicKey, farm, ); const sigs = new Array<TransactionSignature>(); for (let i = 0; i < ixs.length; i++) { sigs[i] = await this.executeTransaction([ixs[i]], user); } return sigs; } async transferOwnershipIx( user: PublicKey, userState: PublicKey, newUser: PublicKey, ): Promise<TransactionInstruction> { const ix = farmOperations.transferOwnership(user, userState, newUser); return ix; } async transferOwnership( user: Keypair, userState: PublicKey, newUser: PublicKey, ): Promise<TransactionSignature> { const ix = await this.transferOwnershipIx( user.publicKey, userState, newUser, ); let sig = await this.executeTransaction([ix], user); if (process.env.DEBUG === "true") { console.log( "Transfer User " + userState + " ownership from " + user.publicKey + " to " + newUser, ); console.log("Transfer User Ownership txn: " + sig.toString()); } return sig; } async transferOwnershipAllUserStatesIx( user: PublicKey, newUser: PublicKey, ): Promise<Array<TransactionInstruction>> { const userStates = await this.getAllUserStatesForUser(user); const ixs = new Array<TransactionInstruction>(); for (let index = 0; index < userStates.length; index++) { ixs[index] = farmOperations.transferOwnership( user, userStates[index].key, newUser, ); } return ixs; } async transferOwnershipAllUserStates( user: Keypair, newUser: PublicKey, ): Promise<Array<TransactionSignature>> { const ixs = await this.transferOwnershipAllUserStatesIx( user.publicKey, newUser, ); const sigs = new Array<TransactionSignature>(); for (let i = 0; i < ixs.length; i++) { sigs[i] = await this.executeTransaction([ixs[i]], user); } return sigs; } async createFarmIx( admin: PublicKey, farm: Keypair, globalConfig: PublicKey, stakeTokenMint: PublicKey, ): Promise<TransactionInstruction[]> { const farmVault = getFarmVaultPDA( this._farmsProgramId, farm.publicKey, stakeTokenMint, ); const farmVaultAuthority = getFarmAuthorityPDA( this._farmsProgramId, farm.publicKey, ); let ixs: TransactionInstruction[] = []; ixs.push( await createKeypairRentExemptIx( this._provider.connection, admin, farm, SIZE_FARM_STATE, this._farmsProgramId, ), ); ixs.push( farmOperations.initializeFarm( globalConfig, admin, farm.publicKey, farmVault, farmVaultAuthority, stakeTokenMint, ), ); return ixs; } async createFarm( admin: Keypair, globalConfig: PublicKey, farm: Keypair, mint: PublicKey, ): Promise<TransactionSignature> { const ix = await this.createFarmIx( admin.publicKey, farm, globalConfig, mint, ); let sig = await this.executeTransaction(ix, admin, [farm]); if (process.env.DEBUG === "true") { console.log("Initialize Farm: " + farm.toString()); console.log("Initialize Farm txn: " + sig.toString()); } return sig; } async addRewardToFarmIx( admin: PublicKey, globalConfig: PublicKey, farm: PublicKey, mint: PublicKey, ): Promise<TransactionInstruction> { const globalConfigState = await GlobalConfig.fetch( this._connection, globalConfig, ); if (!globalConfigState) { throw new Error("Could not fetch global config"); } const treasuryVault = getTreasuryVaultPDA( this._farmsProgramId, globalConfig, mint, ); const farmState = await FarmState.fetch(this._connection, farm); if (!farmState) { throw new Error(`Could not fetch farm state ${farm.toBase58()}`); } const rewardVault = getRewardVaultPDA(this._farmsProgramId, farm, mint); const ix = farmOperations.initializeReward( globalConfig, globalConfigState.treasuryVaultsAuthority, treasuryVault, admin, farm, rewardVault, farmState.farmVaultsAuthority, mint, ); return ix; } async addRewardToFarm( admin: Keypair, globalConfig: PublicKey, farm: PublicKey, mint: PublicKey, ): Promise<TransactionSignature> { const ix = await this.addRewardToFarmIx( admin.publicKey, globalConfig, farm, mint, ); let sig = await this.executeTransaction([ix], admin); if (process.env.DEBUG === "true") { console.log("Initialize Reward: " + mint); console.log("Initialize Reward txn: " + sig.toString()); } return sig; } async addRewardAmountToFarmIx( admin: PublicKey, farm: PublicKey, mint: PublicKey, amount: Decimal, ): Promise<TransactionInstruction> { const farmState = await FarmState.fetch(this._connection, farm); if (!farmState) { throw new Error(`Could not fetch farm state ${farm.toBase58()}`); } let amountLamports = new BN( collToLamportsDecimal( amount, farmState.token.decimals.toNumber(), ).toString(), ); const adminRewardAta = await getAssociatedTokenAddress(admin, mint); let rewardIndex = -1; for (let i = 0; farmState.rewardInfos.length; i++) { if (farmState.rewardInfos[i].token.mint.toString() === mint.toString()) { rewardIndex = i; break; } } const ix = farmOperations.addReward( admin, farm, farmState.rewardInfos[rewardIndex].rewardsVault, farmState.farmVaultsAuthority, adminRewardAta, mint, rewardIndex, amountLamports, ); return ix; } async addRewardAmountToFarm( admin: Keypair, farm: PublicKey, mint: PublicKey, amount: Decimal, ): Promise<TransactionSignature> { const ix = await this.addRewardAmountToFarmIx( admin.publicKey, farm, mint, amount, ); let sig = await this.executeTransaction([ix], admin); if (process.env.DEBUG === "true") { console.log("Add Reward: " + mint + " amount: " + amount); console.log("Add Reward txn: " + sig.toString()); } return sig; } async updateRewardToFarmIx( admin: PublicKey, farm: PublicKey, mint: PublicKey, mode: FarmConfigOptionKind, value: number, ): Promise<TransactionInstruction> { const farmState = await FarmState.fetch(this._connection, farm); let rewardIndex = -1; if (!farmState) { throw new Error(`Could not fetch farm state ${farm.toBase58()}`); } for (let i = 0; farmState.rewardInfos.length; i++) { if (farmState.rewardInfos[i].token.mint.toString() === mint.toString()) { rewardIndex = i; break; } } const ix = farmOperations.updateRewardConfig( admin, farm, rewardIndex, mode, value, ); return ix; } async updateRewardToFarm( admin: Keypair, farm: PublicKey, mint: PublicKey, mode: FarmConfigOptionKind, value: number, ): Promise<TransactionSignature> { const ix = await this.updateRewardToFarmIx( admin.publicKey, farm, mint, mode, value, ); let sig = await this.executeTransaction([ix], admin); if (process.env.DEBUG === "true") { console.log( "Update Reward: " + mint + " mode: " + mode.discriminator + " value: " + value, ); console.log("Update Reward txn: " + sig.toString()); } return sig; } async refreshFarmIx(farm: PublicKey): Promise<TransactionInstruction> { const ix = farmOperations.refreshFarm(farm); return ix; } async refreshFarm( payer: Keypair, farm: PublicKey, ): Promise<TransactionSignature> { const ix = await this.refreshFarmIx(farm); let sig = await this.executeTransaction([ix], payer); if (process.env.DEBUG === "true") { console.log("Refresh Farm: " + farm); console.log("Refresh Farm txn: " + sig.toString()); } return sig; } async refreshUserIx( userState: PublicKey, farmState: PublicKey, ): Promise<TransactionInstruction> { const ix = farmOperations.refreshUserState(userState, farmState); return ix; } async refreshUser( payer: Keypair, userState: PublicKey, farmState: PublicKey, ): Promise<TransactionSignature> { const ix = await this.refreshUserIx(userState, farmState); let sig = await this.executeTransaction([ix], payer); if (process.env.DEBUG === "true") { console.log("Refresh User: " + userState); console.log("Refresh User txn: " + sig.toString()); } return sig; } async createGlobalConfigIxs( admin: PublicKey, globalConfig: Keypair, ): Promise<TransactionInstruction[]> { let ixs: TransactionInstruction[] = []; ixs.push( await createKeypairRentExemptIx( this._provider.connection, admin, globalConfig, SIZE_GLOBAL_CONFIG, this._farmsProgramId, ), ); const treasuryVaultAuthority = getTreasuryAuthorityPDA( this._farmsProgramId, globalConfig.publicKey, ); ixs.push( farmOperations.initializeGlobalConfig( admin, globalConfig.publicKey, treasuryVaultAuthority, ), ); return ixs; } async createGlobalConfig( admin: Keypair, globalConfig: Keypair, ): Promise<TransactionSignature> { const ix = await this.createGlobalConfigIxs(admin.publicKey, globalConfig); const sig = await this.executeTransaction(ix, admin, [globalConfig]); if (process.env.DEBUG === "true") { console.log("Initialize Global Config: " + globalConfig.toString()); console.log("Initialize Global Config txn: " + sig.toString()); } return sig; } async updateGlobalConfigIx( admin: PublicKey, globalConfig: PublicKey, mode: GlobalConfigOptionKind, flagValue: string, flagValueType: string, ): Promise<TransactionInstruction> { const ix = farmOperations.updateGlobalConfig( admin, globalConfig, mode, flagValue, flagValueType, ); return ix; } async updateGlobalConfig( admin: Keypair, globalConfig: PublicKey, mode: GlobalConfigOptionKind, flagValue: string, flagValueType: string, ): Promise<TransactionSignature> { const ix = await this.updateGlobalConfigIx( admin.publicKey, globalConfig, mode, flagValue, flagValueType, ); const sig = await this.executeTransaction([ix], admin); if (process.env.DEBUG === "true") { console.log( "Update Global Config: " + globalConfig.toString() + " mode: " + mode.discriminator + " value: " + flagValue, ); console.log("Update Global Config txn: " + sig.toString()); } return sig; } async withdrawTreasuryIx( admin: PublicKey, globalConfig: PublicKey, rewardMint: PublicKey, amount: BN, withdrawAta?: PublicKey, ): Promise<TransactionInstruction> { const treasuryVault = getTreasuryVaultPDA( this._farmsProgramId, globalConfig, rewardMint, ); const treasuryVaultAuthority = getTreasuryAuthorityPDA( this._farmsProgramId, globalConfig, ); if (!withdrawAta) { withdrawAta = await getAssociatedTokenAddress(admin, rewardMint); } const ix = farmOperations.withdrawTreasury( admin, globalConfig, treasuryVault, treasuryVaultAuthority, withdrawAta, amount, rewardMint, ); return ix; } async withdrawTreasury( admin: Keypair, globalConfig: PublicKey, rewardMint: PublicKey, amount: BN, withdrawAta?: PublicKey, ): Promise<TransactionSignature> { const ix = await this.withdrawTreasuryIx( admin.publicKey, globalConfig, rewardMint, amount, withdrawAta, ); const sig = await this.executeTransaction([ix], admin); if (process.env.DEBUG === "true") { console.log( "Admin " + admin.publicKey + " withdraw treasury of " + rewardMint + " an amount of " + amount, ); console.log("Withdraw treasury txn: " + sig.toString()); } return sig; } } export async function getCurrentTimeUnit( conn: Connection, farm: FarmState, ): Promise<Decimal> { const slot = await conn.getSlot(); const timestamp = await conn.getBlockTime(slot); if (farm.timeUnit == TimeUnit.Seconds.discriminator) { return new Decimal(timestamp!); } else { return new Decimal(slot); } } export async function getCurrentRps( conn: Connection, farm: FarmState, rewardIndex: number, ): Promise<number> { const currentTimeUnit = new Decimal(await getCurrentTimeUnit(conn, farm)); return calculateCurrentRewardPerToken(farm, rewardIndex, currentTimeUnit); }