@mihalex/farms-sdk-tests
Version:
1,299 lines (1,129 loc) • 33 kB
text/typescript
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);
}