UNPKG

@kamino-finance/farms-sdk

Version:
721 lines 42.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.upsertAllFarmConfigsCommand = upsertAllFarmConfigsCommand; exports.estimateRewardDurationRewardCurve = estimateRewardDurationRewardCurve; exports.calculateDaysBalanceRemaining = calculateDaysBalanceRemaining; exports.estimateRewardDuration = estimateRewardDuration; const web3_js_1 = require("@solana/web3.js"); const decimal_js_1 = __importDefault(require("decimal.js")); const Farms_1 = require("../Farms"); const accounts_1 = require("../rpc_client/accounts"); const types_1 = require("../rpc_client/types"); const utils_1 = require("./utils"); const utils_2 = require("../utils"); const fs_1 = __importDefault(require("fs")); const cli_color_1 = __importDefault(require("cli-color")); const klend_sdk_1 = require("@kamino-finance/klend-sdk"); const anchor_1 = require("@coral-xyz/anchor"); const microLamport = 10 ** 6; const computeUnits = 200_000; const microLamportsPrioritizationFee = microLamport / computeUnits; async function upsertAllFarmConfigsCommand(targetPath, mode, priorityFeeMultiplier) { const admin = process.env.ADMIN; const rpc = process.env.RPC; const env = (0, utils_1.initializeClient)(rpc, admin, (0, utils_2.getFarmsProgramId)(rpc), mode === "multisig"); // Load files from given directory const farmConfigs = recursiveReadDirectoryOfConfigs(targetPath); const slot = await env.provider.connection.getSlot("finalized"); for (const [_index, farmConfig] of farmConfigs.entries()) { await upsertFarmConfig(env, farmConfig, mode, slot, priorityFeeMultiplier); } } async function upsertFarmConfig(env, farmConfig, mode, slot, priorityFeeMultiplier) { let ixs = []; let signers = [env.initialOwner]; const farmsClient = new Farms_1.Farms(env.provider.connection); const farmState = await accounts_1.FarmState.fetch(env.provider.connection, farmConfig.farmPubkey); if (farmState === null) { throw Error("Farm state does not exist"); } const topUpPayer = mode === "multisig" ? new web3_js_1.PublicKey(process.env.MULTISIG) : env.initialOwner.publicKey; const payer = farmState.farmAdmin; validateStakingTokenMint(farmConfig, farmState); // Set farm configurations based on differences between config and current farm state ixs.push(...(await setDepositCapIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setLockingModeIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setLockingStartIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setLockingDurationIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setLockingEarlyWithdrawalPenaltyIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setStrategyIdIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setWithdrawAuthorityIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setSlashedAmountSpillAddressIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setPendingFarmAdminIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setDelegateRpsAdminIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setScopePricesAccountIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setScopeOraclePriceIdIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setScopeOracleMaxAgeIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setDepositWarmupPeriodIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); ixs.push(...(await setWithdrawCooldownPeriodIfNecessary(payer, farmsClient, farmConfig, farmState, mode))); for (const [rewardIndex, _rewardInfoConfig] of farmConfig.rewards.entries()) { const rewardTokenProgram = farmState.rewardInfos[rewardIndex].token.tokenProgram; const rewardDecimals = farmState.rewardInfos[rewardIndex].token.decimals.toNumber(); // Initialize reward if required ixs.push(...(await initRewardIfNecessary(payer, farmsClient, farmConfig, farmState, rewardIndex, mode))); ixs.push(...(await setRpsDecimalsIxIfNecessary(payer, farmsClient, farmConfig, farmState, rewardIndex, mode))); ixs.push(...(await setRewardTypeIxIfNecessary(payer, farmsClient, farmConfig, farmState, rewardIndex, mode))); ixs.push(...(await setRewardMinClaimDurationIxIfNecessary(payer, farmsClient, farmConfig, farmState, rewardIndex, mode))); const { rewardCurve, rewardPerSecond } = getConfigRewardRps(farmState, farmConfig, rewardIndex); if (rewardPerSecond == -1 && !rewardCurve) { throw new Error("Must have either reward per second or reward curve"); } const setRewardCurveIxn = await setRewardCurveIfNecessary(env, payer, farmsClient, farmState, farmConfig, rewardCurve, rewardIndex, rewardDecimals, slot, mode); ixs.push(...setRewardCurveIxn); let setRewardRpsIxns = []; if (setRewardCurveIxn.length == 0) { setRewardRpsIxns.push(...(await setRewardPerSecondIfNecessary(payer, farmsClient, farmState, farmConfig, rewardPerSecond, rewardIndex, rewardDecimals, mode))); } ixs.push(...setRewardRpsIxns); // Topup ixs.push(...(await topupRewardAmountNecessary(env, topUpPayer, farmsClient, farmState, farmConfig, rewardIndex, rewardDecimals, rewardTokenProgram, slot, setRewardCurveIxn.length == 0 && setRewardRpsIxns.length == 0, mode))); } // 5. Send or print txn const { blockhash } = await (0, utils_2.retryAsync)(async () => (0, utils_2.noopProfiledFunctionExecution)(env.provider.connection.getLatestBlockhash("confirmed"))); let txn = new web3_js_1.Transaction(); txn.recentBlockhash = blockhash; txn.feePayer = mode === "multisig" ? new web3_js_1.PublicKey(process.env.MULTISIG) : env.initialOwner.publicKey; if (ixs.length !== 0) { if (mode !== "multisig") { const priorityFeeIxn = (0, utils_1.createAddExtraComputeUnitFeeTransaction)(computeUnits, microLamportsPrioritizationFee * priorityFeeMultiplier); txn.add(...priorityFeeIxn); } txn.add(...ixs); if (mode === "simulate") { await (0, Farms_1.printSimulateTx)(env.provider.connection, txn); } else if (mode === "multisig") { await (0, Farms_1.printMultisigTx)(txn); } else if (mode === "execute") { console.log("Sending."); const sig = await (0, utils_2.retryAsync)(async () => (0, utils_2.noopProfiledFunctionExecution)(sendAndConfirmTxn(env, new web3_js_1.VersionedTransaction(txn.compileMessage()), signers))); console.log("Signature", sig); } } mode !== "multisig" && ixs.length > 0 && console.log("-".repeat(150)); } async function sendAndConfirmTxn(env, txn, signers) { const sig = await (0, utils_2.signSendAndConfirmRawTransactionWithRetry)({ mainConnection: env.web3Client.sendConnection, extraConnections: env.web3Client.sendConnectionsExtra, tx: txn, signers, commitment: "confirmed", sendTransactionOptions: { maxRetries: 5, preflightCommitment: "confirmed", }, }); return sig; } function validateStakingTokenMint(farmConfig, farmState) { if (!farmState.token.mint.equals(farmConfig.stakingTokenMint)) { throw ("Staking token mint mismatch Config: " + farmConfig.stakingTokenMint.toString() + " FarmState: " + farmState.token.mint.toString()); } } async function setDepositCapIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let depositCapAmountIx = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.DepositCapAmount.kind]: "", }), farmConfig.depositCapAmount, 0); const currentDepositCapAmount = new decimal_js_1.default(farmState.depositCapAmount.toString()).toNumber(); if (currentDepositCapAmount != farmConfig.depositCapAmount) { const valueStringBefore = "depositCapAmount before".padEnd(40) + ": " + currentDepositCapAmount; const valueStringAfter = "depositCapAmount after".padEnd(40) + ": " + farmConfig.depositCapAmount; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [depositCapAmountIx]; } return []; } async function setLockingModeIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setLockingModeIx = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.LockingMode.kind]: "", }), farmConfig.lockingMode, 0); const currentLockingMode = new decimal_js_1.default(farmState.lockingMode.toString()).toNumber(); if (currentLockingMode != farmConfig.lockingMode) { const valueStringBefore = "lockingMode before".padEnd(40) + ": " + currentLockingMode; const valueStringAfter = "lockingMode after".padEnd(40) + ": " + farmConfig.lockingMode; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setLockingModeIx]; } return []; } async function setLockingStartIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setLockingStart = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.LockingStartTimestamp.kind]: "", }), farmConfig.lockingStart, 0); const currentLockingStartTimestamp = new decimal_js_1.default(farmState.lockingStartTimestamp.toString()).toNumber(); if (currentLockingStartTimestamp != farmConfig.lockingStart) { const valueStringBefore = "lockingStartTimestamp before".padEnd(40) + ": " + currentLockingStartTimestamp; const valueStringAfter = "lockingStartTimestamp after".padEnd(40) + ": " + farmConfig.lockingStart; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setLockingStart]; } return []; } async function setLockingDurationIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setLockingDuration = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.LockingDuration.kind]: "", }), farmConfig.lockingDuration, 0); const currentLockingDuration = new decimal_js_1.default(farmState.lockingDuration.toString()).toNumber(); if (currentLockingDuration != farmConfig.lockingDuration) { const valueStringBefore = "lockingDuration before".padEnd(40) + ": " + currentLockingDuration; const valueStringAfter = "lockingDuration after".padEnd(40) + ": " + farmConfig.lockingDuration; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setLockingDuration]; } return []; } async function setLockingEarlyWithdrawalPenaltyIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setLockingEarlyWithdrawalPenalty = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.LockingEarlyWithdrawalPenaltyBps.kind]: "", }), farmConfig.lockingEarlyWithdrawalPenaltyBps, 0); const currentLockingEarlyWithdrawalPenalty = new decimal_js_1.default(farmState.lockingEarlyWithdrawalPenaltyBps.toString()).toNumber(); if (currentLockingEarlyWithdrawalPenalty != farmConfig.lockingEarlyWithdrawalPenaltyBps) { const valueStringBefore = "lockingEarlyWithdrawalPenaltyBps before".padEnd(40) + ": " + currentLockingEarlyWithdrawalPenalty; const valueStringAfter = "lockingEarlyWithdrawalPenaltyBps after".padEnd(40) + ": " + farmConfig.lockingEarlyWithdrawalPenaltyBps; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setLockingEarlyWithdrawalPenalty]; } return []; } async function setStrategyIdIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setStrategyId = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.UpdateStrategyId.kind]: "", }), farmConfig.strategyId, 0); if (!farmState.strategyId.equals(farmConfig.strategyId)) { const valueStringBefore = "strategyId before".padEnd(40) + ": " + farmState.strategyId.toString(); const valueStringAfter = "strategyId after".padEnd(40) + ": " + farmConfig.strategyId.toString(); printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setStrategyId]; } return []; } async function setWithdrawAuthorityIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setWithdrawAuthority = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.WithdrawAuthority.kind]: "", }), farmConfig.withdrawAuthority, 0); if (!farmState.withdrawAuthority.equals(farmConfig.withdrawAuthority)) { const valueStringBefore = "withdrawAuthority before".padEnd(40) + ": " + farmState.withdrawAuthority.toString(); const valueStringAfter = "withdrawAuthority after".padEnd(40) + ": " + farmConfig.withdrawAuthority.toString(); printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setWithdrawAuthority]; } return []; } async function setSlashedAmountSpillAddressIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setSlashedAmountSpillAddress = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.SlashedAmountSpillAddress.kind]: "", }), farmConfig.slashedAmountSpillAddress, 0); if (!farmState.slashedAmountSpillAddress.equals(farmConfig.slashedAmountSpillAddress)) { const valueStringBefore = "slashedAmountSpillAddress before".padEnd(40) + ": " + farmState.slashedAmountSpillAddress.toString(); const valueStringAfter = "slashedAmountSpillAddress after".padEnd(40) + ": " + farmConfig.slashedAmountSpillAddress.toString(); printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setSlashedAmountSpillAddress]; } return []; } async function setScopePricesAccountIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setScopePricesAccount = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.ScopePricesAccount.kind]: "", }), farmConfig.scopePrices, 0); if (!farmState.scopePrices.equals(farmConfig.scopePrices)) { const valueStringBefore = "scopePrices before".padEnd(40) + ": " + farmState.scopePrices.toString(); const valueStringAfter = "scopePrices after".padEnd(40) + ": " + farmConfig.scopePrices.toString(); printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setScopePricesAccount]; } return []; } async function setPendingFarmAdminIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setPendingFarmAdmin = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.UpdatePendingFarmAdmin.kind]: "", }), farmConfig.pendingFarmAdmin, 0); if (!farmState.pendingFarmAdmin.equals(farmConfig.pendingFarmAdmin)) { const valueStringBefore = "pendingFarmAdmin before".padEnd(40) + ": " + farmState.pendingFarmAdmin.toString(); const valueStringAfter = "pendingFarmAdmin after".padEnd(40) + ": " + farmConfig.pendingFarmAdmin.toString(); printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setPendingFarmAdmin]; } return []; } async function setDelegateRpsAdminIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setDelegateRpsAdmin = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.UpdateDelegatedRpsAdmin.kind]: "", }), farmConfig.delegatedRpsAdmin, 0); if (!farmState.delegatedRpsAdmin.equals(farmConfig.delegatedRpsAdmin)) { const valueStringBefore = "delegatedRpsAdmin before".padEnd(40) + ": " + farmState.delegatedRpsAdmin.toString(); const valueStringAfter = "delegatedRpsAdmin after".padEnd(40) + ": " + farmConfig.delegatedRpsAdmin.toString(); printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setDelegateRpsAdmin]; } return []; } async function setScopeOraclePriceIdIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { const currentScopeOraclePriceId = new decimal_js_1.default(farmState.scopeOraclePriceId.toString()); if (!currentScopeOraclePriceId.eq(new decimal_js_1.default(farmConfig.scopePriceOracleId))) { const valueStringBefore = "scopeOraclePriceId before".padEnd(40) + ": " + farmState.scopeOraclePriceId; const valueStringAfter = "scopeOraclePriceId after".padEnd(40) + ": " + farmConfig.scopePriceOracleId; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); let setScopeOraclePriceId = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.ScopeOraclePriceId.kind]: "", }), new anchor_1.BN(farmConfig.scopePriceOracleId), 0); return [setScopeOraclePriceId]; } return []; } async function setScopeOracleMaxAgeIxIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setScopeOracleMaxAge = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.ScopeOracleMaxAge.kind]: "", }), farmConfig.scopeOracleMaxAge, 0); const currentScopeOracleMaxAge = new decimal_js_1.default(farmState.scopeOracleMaxAge.toString()).toNumber(); if (currentScopeOracleMaxAge != farmConfig.scopeOracleMaxAge) { const valueStringBefore = "scopeOracleMaxAge before".padEnd(40) + ": " + currentScopeOracleMaxAge; const valueStringAfter = "scopeOracleMaxAge after".padEnd(40) + ": " + farmConfig.lockingDuration; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setScopeOracleMaxAge]; } return []; } async function setDepositWarmupPeriodIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setDepositWarmupPeriodIx = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.DepositWarmupPeriod.kind]: "", }), farmConfig.depositWarmupPeriod, 0); const currentDepositWarmupPeriod = new decimal_js_1.default(farmState.depositWarmupPeriod.toString()).toNumber(); if (currentDepositWarmupPeriod != farmConfig.depositWarmupPeriod) { const valueStringBefore = "depositWarmupPeriod before".padEnd(40) + ": " + currentDepositWarmupPeriod; const valueStringAfter = "depositWarmupPeriod after".padEnd(40) + ": " + farmConfig.depositWarmupPeriod; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setDepositWarmupPeriodIx]; } return []; } async function setWithdrawCooldownPeriodIfNecessary(payer, farmsClient, farmConfig, farmState, mode) { let setWithdrawCooldownPeriodIx = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, web3_js_1.PublicKey.default, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.WithdrawCooldownPeriod.kind]: "", }), farmConfig.withdrawCooldownPeriod, 0); const currentWithdrawCooldownPeriod = new decimal_js_1.default(farmState.withdrawalCooldownPeriod.toString()).toNumber(); if (currentWithdrawCooldownPeriod != farmConfig.depositWarmupPeriod) { const valueStringBefore = "withdrawalCooldownPeriod before".padEnd(40) + ": " + currentWithdrawCooldownPeriod; const valueStringAfter = "withdrawalCooldownPeriod after".padEnd(40) + ": " + farmConfig.withdrawCooldownPeriod; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString()); return [setWithdrawCooldownPeriodIx]; } return []; } async function initRewardIfNecessary(payer, farmsClient, farmConfig, farmState, rewardIndex, mode) { if (!farmState.rewardInfos[rewardIndex].token.mint.equals(web3_js_1.PublicKey.default) && !farmConfig.rewards[rewardIndex]?.rewardTokenMint.equals(farmState.rewardInfos[rewardIndex].token.mint)) { throw new Error("Reward token mint already initialized and does not match config"); } if (!farmConfig.rewards[rewardIndex]?.rewardTokenMint.equals(farmState.rewardInfos[rewardIndex].token.mint)) { const rewardMint = farmConfig.rewards[rewardIndex]?.rewardTokenMint; const tokenProgram = (await farmsClient .getConnection() .getAccountInfo(rewardMint)).owner; const initRewardIx = await farmsClient.addRewardToFarmIx(payer, farmState.globalConfig, farmConfig.farmPubkey, rewardMint, tokenProgram); mode !== "multisig" && console.log("Change for farm: ", cli_color_1.default.yellow(farmConfig.farmPubkey.toString().padEnd(50))); mode !== "multisig" && console.log(cli_color_1.default.green("initialize new reward with mint".padEnd(40) + ": "), cli_color_1.default.yellow(rewardMint.toString()), "\n"); return [initRewardIx]; } return []; } async function setRpsDecimalsIxIfNecessary(payer, farmsClient, farmConfig, farmState, rewardIndex, mode) { let setRpsDecimals = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, farmConfig.rewards[rewardIndex]?.rewardTokenMint, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.RpsDecimals.kind]: "", }), farmConfig.rewards[rewardIndex]?.rewardPerSecondDecimals, rewardIndex); const currentRpsDecimals = new decimal_js_1.default(farmState.rewardInfos[rewardIndex]?.rewardsPerSecondDecimals.toString()).toNumber(); if (currentRpsDecimals != farmConfig.rewards[rewardIndex]?.rewardPerSecondDecimals) { const valueStringBefore = "rpsDecimals before".padEnd(40) + ": " + currentRpsDecimals; const valueStringAfter = "rpsDecimals after".padEnd(40) + ": " + farmConfig.rewards[rewardIndex]?.rewardPerSecondDecimals; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString(), rewardIndex, farmConfig.rewards[rewardIndex]?.rewardTokenMint.toString()); return [setRpsDecimals]; } return []; } async function setRewardTypeIxIfNecessary(payer, farmsClient, farmConfig, farmState, rewardIndex, mode) { let configRewardType = 0; if (farmConfig.rewards[rewardIndex]?.rewardType === "Proportional") { configRewardType = 0; } else if (farmConfig.rewards[rewardIndex]?.rewardType === "Constant") { configRewardType = 1; } else { throw new Error(`Reward type invalid for farm ${farmConfig.farmPubkey.toString()}`); } let setRewardType = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, farmConfig.rewards[rewardIndex]?.rewardTokenMint, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.RewardType.kind]: "", }), configRewardType, rewardIndex); const currentRewardTypeString = farmState.rewardInfos[rewardIndex]?.rewardType === 0 ? "Proportional" : "Constant"; if (farmState.rewardInfos[rewardIndex]?.rewardType != configRewardType) { const valueStringBefore = "rewardType before".padEnd(40) + ": " + currentRewardTypeString; const valueStringAfter = "rewardType after".padEnd(40) + ": " + farmConfig.rewards[rewardIndex]?.rewardType; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString(), rewardIndex, farmConfig.rewards[rewardIndex]?.rewardTokenMint.toString()); return [setRewardType]; } return []; } async function setRewardMinClaimDurationIxIfNecessary(payer, farmsClient, farmConfig, farmState, rewardIndex, mode) { let setRewardMinClaimDuration = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, farmConfig.rewards[rewardIndex]?.rewardTokenMint, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.UpdateRewardMinClaimDuration.kind]: "", }), farmConfig.rewards[rewardIndex]?.minClaimDurationSeconds, rewardIndex); const currentMinClaimDurationSeconds = new decimal_js_1.default(farmState.rewardInfos[rewardIndex]?.minClaimDurationSeconds.toString()).toNumber(); if (currentMinClaimDurationSeconds != farmConfig.rewards[rewardIndex]?.minClaimDurationSeconds) { const valueStringBefore = "minClaimDurationSeconds before".padEnd(40) + ": " + currentMinClaimDurationSeconds; const valueStringAfter = "minClaimDurationSeconds after".padEnd(40) + ": " + farmConfig.rewards[rewardIndex]?.minClaimDurationSeconds; printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString(), rewardIndex, farmConfig.rewards[rewardIndex]?.rewardTokenMint.toString()); return [setRewardMinClaimDuration]; } return []; } function getCurrentRewardInfo(farmState, rewardIndex) { let rpsDecimalsCurrent = farmState?.rewardInfos[rewardIndex].rewardsPerSecondDecimals ?? 0; let rewardsAvailableLamports = 0; if (farmState) { let rewardInfo = farmState.rewardInfos[rewardIndex]; rewardsAvailableLamports = new decimal_js_1.default(rewardInfo.rewardsAvailable.toString()).toNumber(); } let rpsCurrent = farmState?.rewardInfos[rewardIndex].rewardScheduleCurve.points[0].rewardPerTimeUnit.toNumber() ?? 0; return { rpsDecimalsCurrent, rewardsAvailableLamports, rpsCurrent, }; } function getConfigRewardRps(farmState, farmConfig, rewardIndex) { const rewardCurve = []; farmConfig.rewards[rewardIndex]?.rewardCurve.forEach((point) => { rewardCurve.push({ startTs: point.startTs, rps: point.rps, }); }); const rewardToTopUpDurationSeconds = farmConfig.rewards[rewardIndex] ?.rewardToTopUpDurationDays ? farmConfig.rewards[rewardIndex]?.rewardToTopUpDurationDays * 24 * 60 * 60 : 0; const rewardPerSecond = rewardToTopUpDurationSeconds == 0 ? -1 : Math.round((farmConfig.rewards[rewardIndex]?.rewardToTopUp / rewardToTopUpDurationSeconds) * Math.pow(10, new decimal_js_1.default(farmState.rewardInfos[rewardIndex].token.decimals.toString()).toNumber())); if (rewardCurve.length == 1 && rewardPerSecond != -1) { rewardCurve[0].rps = Number(rewardPerSecond.toFixed(0)); } return { rewardCurve, rewardPerSecond, }; } async function setRewardPerSecondIfNecessary(payer, farmsClient, farmState, farmConfig, rewardPerSecond, rewardIndex, rewardDecimals, mode) { const { rewardsAvailableLamports } = getCurrentRewardInfo(farmState, rewardIndex); const rpsCurrent = farmState?.rewardInfos[rewardIndex].rewardScheduleCurve.points[0].rewardPerTimeUnit.toNumber() ?? 0; if (rpsCurrent != rewardPerSecond && rewardPerSecond != -1) { const valueStringBefore = "rewardPerSecond before".padEnd(40) + ": " + rpsCurrent; const valueStringAfter = "rewardPerSecond after".padEnd(40) + ": " + Math.round(rewardPerSecond); printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString(), rewardIndex, farmConfig.rewards[rewardIndex]?.rewardTokenMint.toString()); const rpsDecimalsCurrent = farmConfig.rewards[rewardIndex].rewardPerSecondDecimals; if (rewardPerSecond) { estimateRewardDuration(rewardDecimals, farmConfig.rewards[rewardIndex]?.rewardToTopUp, rewardsAvailableLamports, rpsCurrent / 10 ** rpsCurrent, rewardPerSecond / 10 ** farmConfig.rewards[rewardIndex]?.rewardPerSecondDecimals, rpsDecimalsCurrent, mode); } let setRewardPerSecond = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, farmConfig.rewards[rewardIndex]?.rewardTokenMint, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.UpdateRewardRps.kind]: "", }), rewardPerSecond, rewardIndex); return [setRewardPerSecond]; } return []; } async function setRewardCurveIfNecessary(env, payer, farmsClient, farmState, farmConfig, rewardCurve, rewardIndex, rewardDecimals, slot, mode) { const { rewardsAvailableLamports } = getCurrentRewardInfo(farmState, rewardIndex); const { currentRewardCurve, ts } = await getCurrentRewardCurveAndTs(env, farmState, rewardIndex, slot); let setRewardCurvePoint = await farmsClient.updateFarmConfigIx(payer, farmConfig.farmPubkey, farmConfig.rewards[rewardIndex]?.rewardTokenMint, types_1.FarmConfigOption.fromDecoded({ [types_1.FarmConfigOption.UpdateRewardScheduleCurvePoints.kind]: "", }), rewardCurve, rewardIndex); let newCurve = false; if (rewardCurve.length > currentRewardCurve.length) { newCurve = true; } for (let i = 0; i < Math.min(rewardCurve.length, currentRewardCurve.length); i++) { if ((currentRewardCurve[i].startTs != rewardCurve[i].startTs || currentRewardCurve[i].rps != rewardCurve[i].rps) && !(rewardCurve[i].startTs == 0 && currentRewardCurve[i].startTs == Number(klend_sdk_1.U64_MAX))) { newCurve = true; break; } } if (newCurve) { const valueStringBefore = "rewardCurve before".padEnd(40) + ": " + JSON.stringify(currentRewardCurve); const valueStringAfter = "rewardCurve after".padEnd(40) + ": " + JSON.stringify(rewardCurve); printDiff(mode, valueStringBefore, valueStringAfter, farmConfig.farmPubkey.toString(), rewardIndex, farmConfig.rewards[rewardIndex]?.rewardTokenMint.toString()); const rpsDecimalsCurrent = farmConfig.rewards[rewardIndex].rewardPerSecondDecimals; estimateRewardDurationRewardCurve(rewardDecimals, farmConfig.rewards[rewardIndex]?.rewardToTopUp, rewardsAvailableLamports, currentRewardCurve, rewardCurve, rpsDecimalsCurrent, ts, mode); return [setRewardCurvePoint]; } return []; } async function topupRewardAmountNecessary(env, payer, farmsClient, farmState, farmConfig, rewardIndex, rewardDecimals, tokenProgram, slot, topUpOnly, mode) { if (farmConfig.rewards[rewardIndex]?.rewardToTopUp > 0) { let topupIx = await farmsClient.addRewardAmountToFarmIx(payer, farmConfig.farmPubkey, farmConfig.rewards[rewardIndex]?.rewardTokenMint, new decimal_js_1.default(farmConfig.rewards[rewardIndex]?.rewardToTopUp), rewardIndex, rewardDecimals, tokenProgram, farmConfig.scopePrices.equals(web3_js_1.PublicKey.default) ? undefined : farmConfig.scopePrices); const rewardIndexString = rewardIndex == -1 ? "" : " - rewardIndex[" + rewardIndex + "] : " + farmConfig.rewards[rewardIndex]?.rewardTokenMint.toString(); const valueStringAfter = "reward to top up".padEnd(40) + ": " + farmConfig.rewards[rewardIndex]?.rewardToTopUp; mode !== "multisig" && console.log("Change for farm: ", cli_color_1.default.yellow(farmConfig.farmPubkey.toString().concat(rewardIndexString).padEnd(65))); mode !== "multisig" && console.log(cli_color_1.default.green(valueStringAfter), "\n"); if (topUpOnly) { const { rewardsAvailableLamports } = getCurrentRewardInfo(farmState, rewardIndex); const { currentRewardCurve, ts } = await getCurrentRewardCurveAndTs(env, farmState, rewardIndex, slot); const rpsDecimalsCurrent = farmConfig.rewards[rewardIndex].rewardPerSecondDecimals; estimateRewardDurationRewardCurve(rewardDecimals, farmConfig.rewards[rewardIndex]?.rewardToTopUp, rewardsAvailableLamports, currentRewardCurve, currentRewardCurve, rpsDecimalsCurrent, ts, mode); } return [topupIx]; } return []; } function estimateRewardDurationRewardCurve(rewardDecimals, rewardToTopUp, remainingLamports, rewardCurveCurrent, newRewardCurve, rpsDecimalsCurrent, ts, mode) { const rewardFactor = 10 ** rewardDecimals; /// Rewards available const newLamports = remainingLamports + rewardToTopUp * rewardFactor; // If we change the number of lamports available const numDaysBalanceRemaining = calculateDaysBalanceRemaining(remainingLamports, rewardCurveCurrent, rpsDecimalsCurrent, ts); const numDaysTopupRemaining = calculateDaysBalanceRemaining(newLamports, rewardCurveCurrent, rpsDecimalsCurrent, ts); mode !== "multisig" && console.log("Days with current balance:", numDaysBalanceRemaining); mode !== "multisig" && console.log("Days with new balance:", numDaysTopupRemaining); // If we change the number of rps const daysWithNewRps = calculateDaysBalanceRemaining(remainingLamports, newRewardCurve, rpsDecimalsCurrent, ts); const daysWithNewRpsAndTopup = calculateDaysBalanceRemaining(newLamports, newRewardCurve, rpsDecimalsCurrent, ts); mode !== "multisig" && console.log("Days with current balance and new reward curve", daysWithNewRps); mode !== "multisig" && console.log("Days with new balance and new reward curve", daysWithNewRpsAndTopup, "\n"); return { remainingLamports, rewardFactor, }; } function calculateDaysBalanceRemaining(lamports, rewardCurve, rpsDecimalsCurrent, ts) { let numDays = 0; let remainingLamports = lamports; for (const [index, _curvePoint] of rewardCurve.entries()) { const rps = rewardCurve[index].rps / 10 ** rpsDecimalsCurrent; if (index == rewardCurve.length - 1) { return numDays + remainingLamports / rps / 86400; } if (remainingLamports > 0) { if (ts >= rewardCurve[index].startTs && ts < rewardCurve[index + 1].startTs) { const secondsWithCurrentRps = remainingLamports / rps; if (ts + secondsWithCurrentRps <= rewardCurve[index + 1].startTs) { numDays += secondsWithCurrentRps / 86400; return numDays; // all remaining lamports elapsed } else { const actualSecondsAtCurrentRps = rewardCurve[index + 1].startTs - ts; numDays += actualSecondsAtCurrentRps / 86400; remainingLamports -= actualSecondsAtCurrentRps * rps; ts = rewardCurve[index + 1].startTs; // advance time to next curve point for estimation } } } } return numDays; } function parseFarmConfigFromFile(farmConfigFromFile) { const farmConfig = { farmMetadata: { type: farmConfigFromFile.farmMetadata.type, reserve: farmConfigFromFile.reserve ? new web3_js_1.PublicKey(farmConfigFromFile.reserve) : undefined, market: farmConfigFromFile.market ? new web3_js_1.PublicKey(farmConfigFromFile.market) : undefined, strategy: farmConfigFromFile.strategy ? new web3_js_1.PublicKey(farmConfigFromFile.strategy) : undefined, }, farmPubkey: new web3_js_1.PublicKey(farmConfigFromFile.farmPubkey), stakingTokenMint: new web3_js_1.PublicKey(farmConfigFromFile.stakingTokenMint), withdrawAuthority: new web3_js_1.PublicKey(farmConfigFromFile.withdrawAuthority), globalConfig: new web3_js_1.PublicKey(farmConfigFromFile.globalConfig), strategyId: new web3_js_1.PublicKey(farmConfigFromFile.strategyId), depositCapAmount: farmConfigFromFile.depositCapAmount, rewards: farmConfigFromFile.rewards.map((reward) => { return { rewardTokenMint: new web3_js_1.PublicKey(reward.rewardTokenMint), rewardType: reward.rewardType, rewardPerSecondDecimals: reward.rewardPerSecondDecimals, minClaimDurationSeconds: reward.minClaimDurationSeconds, rewardCurve: reward.rewardCurve, rewardAvailable: reward.rewardAvailable, rewardToTopUp: reward.rewardToTopUp, rewardToTopUpDurationDays: reward.rewardToTopUpDurationDays, }; }), pendingFarmAdmin: new web3_js_1.PublicKey(farmConfigFromFile.pendingFarmAdmin), scopePrices: new web3_js_1.PublicKey(farmConfigFromFile.scopePrices), scopePriceOracleId: farmConfigFromFile.scopePriceOracleId, scopeOracleMaxAge: farmConfigFromFile.scopeOracleMaxAge, lockingMode: farmConfigFromFile.lockingMode, lockingStart: farmConfigFromFile.lockingStart, lockingDuration: farmConfigFromFile.lockingDuration, lockingEarlyWithdrawalPenaltyBps: farmConfigFromFile.lockingEarlyWithdrawalPenaltyBps, depositWarmupPeriod: farmConfigFromFile.depositWarmupPeriod, withdrawCooldownPeriod: farmConfigFromFile.withdrawCooldownPeriod, slashedAmountSpillAddress: new web3_js_1.PublicKey(farmConfigFromFile.slashedAmountSpillAddress), delegatedRpsAdmin: new web3_js_1.PublicKey(farmConfigFromFile.delegatedRpsAdmin), }; return farmConfig; } function recursiveReadDirectoryOfConfigs(targetPath) { const farmConfigs = []; fs_1.default.readdirSync(targetPath, { withFileTypes: true }).forEach((file) => { if (file.isDirectory()) { farmConfigs.push(...recursiveReadDirectoryOfConfigs(targetPath + "/" + file.name)); } else { const farmConfigFromFile = JSON.parse(fs_1.default.readFileSync(targetPath + "/" + file.name, "utf8")); farmConfigs.push(parseFarmConfigFromFile(farmConfigFromFile)); } }); return farmConfigs; } function printDiff(mode, valueStringBefore, valueStringAfter, farmPubkeyString, rewardIndex = -1, rewardMint = "") { const rewardIndexString = rewardIndex == -1 ? "" : " - rewardIndex[" + rewardIndex + "] : " + rewardMint; mode !== "multisig" && console.log("Change for farm: ", cli_color_1.default.yellow(farmPubkeyString.concat(rewardIndexString).padEnd(65))); mode !== "multisig" && console.log(cli_color_1.default.red(valueStringBefore)); mode !== "multisig" && console.log(cli_color_1.default.green(valueStringAfter), "\n"); } function estimateRewardDuration(rewardDecimals, rewardToTopUp, remainingLamports, rpsCurrent, newRps, rpsDecimalsCurrent, mode) { rpsCurrent = rpsCurrent / 10 ** rpsDecimalsCurrent; newRps = newRps / 10 ** rpsDecimalsCurrent; const rewardFactor = 10 ** rewardDecimals; /// Rewards available const newLamports = remainingLamports + rewardToTopUp * rewardFactor; // If we change the number of lamports available const numDaysBalanceRemaining = remainingLamports / rpsCurrent / 86400; const numDaysTopupRemaining = newLamports / rpsCurrent / 86400; mode !== "multisig" && console.log("Days with current balance:", numDaysBalanceRemaining); mode !== "multisig" && console.log("Days with new balance:", numDaysTopupRemaining); // If we change the number of rps const daysWithNewRps = remainingLamports / newRps / 86400; const daysWithNewRpsAndTopup = newLamports / newRps / 86400; mode !== "multisig" && console.log("Days with current balance and new rps", daysWithNewRps); mode !== "multisig" && console.log("Days with new balance and new rps", daysWithNewRpsAndTopup, "\n"); return { remainingLamports, rewardFactor, }; } async function getCurrentRewardCurveAndTs(env, farmState, rewardIndex, slot) { let currentRewardCurve = []; if (farmState) { for (const point of farmState.rewardInfos[rewardIndex].rewardScheduleCurve .points) { if (point.tsStart.toString() === klend_sdk_1.U64_MAX && new decimal_js_1.default(point.rewardPerTimeUnit.toString()).toNumber() === 0) { continue; } currentRewardCurve.push({ startTs: new decimal_js_1.default(point.tsStart.toString()).toNumber(), rps: new decimal_js_1.default(point.rewardPerTimeUnit.toString()).toNumber(), }); } } let ts = await env.provider.connection.getBlockTime(slot); return { currentRewardCurve, ts: ts, }; } //# sourceMappingURL=upsert_all_farm_configs.js.map