UNPKG

client-aftermath-ts-sdk

Version:
306 lines (305 loc) 16.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FarmsStakingPool = void 0; const caller_1 = require("../../general/utils/caller"); const utils_1 = require("../../general/utils"); const dayjs_1 = __importDefault(require("dayjs")); const duration_1 = __importDefault(require("dayjs/plugin/duration")); const fixedUtils_1 = require("../../general/utils/fixedUtils"); const coin_1 = require("../coin/coin"); const farms_1 = require("./farms"); class FarmsStakingPool extends caller_1.Caller { // ========================================================================= // Constructor // ========================================================================= constructor(stakingPool, config, Provider) { super(config, "farms"); this.stakingPool = stakingPool; this.Provider = Provider; // ========================================================================= // Getters // ========================================================================= this.rewardCoinTypes = () => { return this.stakingPool.rewardCoins.map((coin) => coin.coinType); }; this.nonZeroRewardCoinTypes = () => { return this.stakingPool.rewardCoins .filter((coin) => coin.emissionRate <= coin.actualRewards && coin.actualRewards > BigInt(0)) .map((coin) => coin.coinType); }; this.rewardCoin = (inputs) => { const foundCoin = this.stakingPool.rewardCoins.find((coin) => coin.coinType === inputs.coinType); if (!foundCoin) throw new Error("Invalid coin type"); return foundCoin; }; this.maxLockDurationMs = () => { return Math.max(Math.min(this.stakingPool.maxLockDurationMs, this.stakingPool.emissionEndTimestamp - (0, dayjs_1.default)().valueOf()), 0); }; // ========================================================================= // Calculations // ========================================================================= // Calculates the amount of rewards that have emitted since the last time this function has been // called. Updates `rewards_accumulated_per_share`. this.emitRewards = () => { const currentTimestamp = (0, dayjs_1.default)().valueOf(); // ia. Check that the vault has deposits. if (this.stakingPool.stakedAmount === BigInt(0)) return; const rewardCoins = utils_1.Helpers.deepCopy(this.stakingPool.rewardCoins); for (const [rewardCoinIndex, rewardCoin] of rewardCoins.entries()) { // ib. Check that enough time has passed since the last emission. if (currentTimestamp < rewardCoin.lastRewardTimestamp + rewardCoin.emissionSchedulesMs) continue; // iia. Calculate how many rewards have to be emitted. const rewardsToEmit = this.calcRewardsToEmit({ rewardCoin }); if (rewardsToEmit === BigInt(0)) continue; // iii. Increase the amount of rewards emitted per share. this.increaseRewardsAccumulatedPerShare({ rewardsToEmit, rewardCoinIndex, }); const numberOfEmissions = (currentTimestamp - rewardCoin.lastRewardTimestamp) / rewardCoin.emissionSchedulesMs; // IMPORTANT: only increase by multiples of `emission_schedule_ms`. // // iv. Update reward's `last_reward_timestamp`. this.stakingPool.rewardCoins[rewardCoinIndex].lastRewardTimestamp = rewardCoin.lastRewardTimestamp + numberOfEmissions * rewardCoin.emissionSchedulesMs; } }; this.calcApr = (inputs) => { const { coinType, price, decimals, tvlUsd } = inputs; if (price <= 0 || tvlUsd <= 0) return 0; // this.emitRewards(); const rewardCoin = this.rewardCoin({ coinType }); const currentTimestamp = (0, dayjs_1.default)().valueOf(); if (rewardCoin.emissionRate > rewardCoin.actualRewards) return 0; if (rewardCoin.emissionStartTimestamp > currentTimestamp || currentTimestamp > this.stakingPool.emissionEndTimestamp) return 0; const emissionRateUsd = coin_1.Coin.balanceWithDecimals(rewardCoin.emissionRate, decimals) * price; dayjs_1.default.extend(duration_1.default); const oneYearMs = dayjs_1.default.duration(1, "year").asMilliseconds(); const rewardsUsdOneYear = emissionRateUsd * (oneYearMs / rewardCoin.emissionSchedulesMs); const apr = rewardsUsdOneYear / tvlUsd / utils_1.Casting.bigIntToFixedNumber(this.stakingPool.maxLockMultiplier); return apr < 0 ? 0 : isNaN(apr) ? 0 : apr; }; this.calcTotalApr = (inputs) => { const { coinsToPrice, coinsToDecimals, tvlUsd } = inputs; const aprs = this.rewardCoinTypes().map((coinType) => this.calcApr({ coinType, price: coinsToPrice[coinType], decimals: coinsToDecimals[coinType], tvlUsd, })); return utils_1.Helpers.sum(aprs); }; this.calcMultiplier = (inputs) => { const lockDurationMs = inputs.lockDurationMs > this.stakingPool.maxLockDurationMs ? this.stakingPool.maxLockDurationMs : inputs.lockDurationMs < this.stakingPool.minLockDurationMs ? this.stakingPool.minLockDurationMs : inputs.lockDurationMs; const totalPossibleLockDurationMs = this.stakingPool.maxLockDurationMs - this.stakingPool.minLockDurationMs; const newMultiplier = 1 + ((lockDurationMs - this.stakingPool.minLockDurationMs) / (totalPossibleLockDurationMs <= 0 ? 1 : totalPossibleLockDurationMs)) * (utils_1.Casting.bigIntToFixedNumber(this.stakingPool.maxLockMultiplier) - 1); const multiplier = utils_1.Casting.numberToFixedBigInt(newMultiplier); return multiplier < fixedUtils_1.FixedUtils.fixedOneB ? fixedUtils_1.FixedUtils.fixedOneB : utils_1.Helpers.minBigInt(multiplier, this.stakingPool.maxLockMultiplier); }; // ========================================================================= // Helpers // ========================================================================= this.useProvider = () => { var _a; const provider = (_a = this.Provider) === null || _a === void 0 ? void 0 : _a.Farms(); if (!provider) throw new Error("missing AftermathApi Provider"); return provider; }; this.stakingPool = stakingPool; // this.emitRewards(); } // ========================================================================= // Public // ========================================================================= // ========================================================================= // Stats // ========================================================================= getTVL() { return __awaiter(this, void 0, void 0, function* () { return new farms_1.Farms(this.config, this.Provider).getTVL({ farmIds: [this.stakingPool.objectId], }); }); } getRewardsTVL() { return __awaiter(this, void 0, void 0, function* () { return new farms_1.Farms(this.config, this.Provider).getRewardsTVL({ farmIds: [this.stakingPool.objectId], }); }); } // public calc_emitted_rewards(): bigint[] { // let emitted_rewards: bigint[] = []; // let length = this.stakingPool.rewardCoins.length; // let index = 0; // while (index < length) { // let emission_start_timestamp_ms = // this.stakingPool.rewardCoins[index].emissionStartTimestamp; // let last_reward_timestamp_ms = // this.stakingPool.rewardCoins[index].lastRewardTimestamp; // emitted_rewards.push( // this.calcRewardsEmittedFromTimeTmToTn({ // timestampTm: emission_start_timestamp_ms, // timestampTn: last_reward_timestamp_ms, // rewardCoin: this.stakingPool.rewardCoins[index], // }) // ); // index = index + 1; // } // return emitted_rewards; // } // public calc_emitted_rewards_for(inputs: { // rewardCoinIndex: number; // }): bigint { // let reward_index = inputs.rewardCoinIndex; // let emission_start_timestamp_ms = // this.stakingPool.rewardCoins[reward_index].emissionStartTimestamp; // let last_reward_timestamp_ms = // this.stakingPool.rewardCoins[reward_index].lastRewardTimestamp; // return this.calcRewardsEmittedFromTimeTmToTn({ // timestampTm: emission_start_timestamp_ms, // timestampTn: last_reward_timestamp_ms, // rewardCoin: this.stakingPool.rewardCoins[reward_index], // }); // } // ========================================================================= // Transactions // ========================================================================= // ========================================================================= // Staking Transactions // ========================================================================= getStakeTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchBuildStakeTx(Object.assign(Object.assign({}, inputs), { stakeCoinType: this.stakingPool.stakeCoinType, stakingPoolId: this.stakingPool.objectId })); }); } // ========================================================================= // Reward Harvesting Transactions // ========================================================================= getHarvestRewardsTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchBuildHarvestRewardsTx(Object.assign(Object.assign({}, inputs), { stakeCoinType: this.stakingPool.stakeCoinType, stakingPoolId: this.stakingPool.objectId, rewardCoinTypes: this.nonZeroRewardCoinTypes() })); }); } // ========================================================================= // Mutation/Creation Transactions (Owner Only) // ========================================================================= getIncreaseRewardsEmissionsTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchIncreaseStakingPoolRewardsEmissionsTx(Object.assign(Object.assign({}, inputs), { stakeCoinType: this.stakingPool.stakeCoinType, stakingPoolId: this.stakingPool.objectId })); }); } getUpdateMinStakeAmountTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().buildSetStakingPoolMinStakeAmountTx(Object.assign(Object.assign({}, inputs), { stakeCoinType: this.stakingPool.stakeCoinType, stakingPoolId: this.stakingPool.objectId })); }); } getGrantOneTimeAdminCapTransaction(inputs) { return this.useProvider().buildGrantOneTimeAdminCapTx(inputs); } // ========================================================================= // Mutation Transactions (Owner/Admin Only) // ========================================================================= getInitializeRewardTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchBuildInitializeStakingPoolRewardTx(Object.assign(Object.assign({}, inputs), { stakeCoinType: this.stakingPool.stakeCoinType, stakingPoolId: this.stakingPool.objectId })); }); } getTopUpRewardsTransaction(inputs) { return __awaiter(this, void 0, void 0, function* () { return this.useProvider().fetchBuildTopUpStakingPoolRewardsTx(Object.assign(Object.assign({}, inputs), { stakeCoinType: this.stakingPool.stakeCoinType, stakingPoolId: this.stakingPool.objectId })); }); } // ========================================================================= // Private // ========================================================================= // ========================================================================= // Calculations // ========================================================================= increaseRewardsAccumulatedPerShare(inputs) { const { rewardsToEmit, rewardCoinIndex } = inputs; // i. Calculate the pro-rata amount of rewards allocated to each staked amount. const newRewardsAccumulatedPerShare = (rewardsToEmit * BigInt(1000000000000000000)) / this.stakingPool.stakedAmountWithMultiplier; if (newRewardsAccumulatedPerShare === BigInt(0)) return; // ii. Increase the amount of rewards emitted per share. this.stakingPool.rewardCoins[rewardCoinIndex].rewardsAccumulatedPerShare += newRewardsAccumulatedPerShare; } rewardsRemaining(inputs) { const rewardCoin = this.stakingPool.rewardCoins[inputs.rewardCoinIndex]; const currentTimestamp = (0, dayjs_1.default)().valueOf(); const numberOfEmissions = BigInt(Math.floor((currentTimestamp - rewardCoin.emissionStartTimestamp) / rewardCoin.emissionSchedulesMs)); const emittedRewards = rewardCoin.emissionRate * numberOfEmissions; const totalRewards = rewardCoin.rewards; if (totalRewards <= emittedRewards) return BigInt(0); return totalRewards - emittedRewards; } calcRewardsToEmit(inputs) { const { rewardCoin } = inputs; const currentTimestamp = (0, dayjs_1.default)().valueOf(); // Calculate the number of rewards that have been emitted since the last time this reward was emitted. const rewardsToEmit = this.calcRewardsEmittedFromTimeTmToTn({ timestampTm: rewardCoin.lastRewardTimestamp, timestampTn: currentTimestamp, rewardCoin, }); const rewardsRemaining = rewardCoin.rewardsRemaining; // IMPORTANT: Cap the amount of rewards to emit by the amount of remaining rewards. // return rewardsRemaining < rewardsToEmit ? rewardsRemaining : rewardsToEmit; } calcRewardsEmittedFromTimeTmToTn(inputs) { const { timestampTm, timestampTn, rewardCoin } = inputs; const numberOfEmissionsFromTimeTmToTn = (timestampTn - timestampTm) / // ----------------------------------------------- rewardCoin.emissionSchedulesMs; return (BigInt(Math.floor(numberOfEmissionsFromTimeTmToTn)) * rewardCoin.emissionRate); } } exports.FarmsStakingPool = FarmsStakingPool;