"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 });
const monetary_js_1 = require("@interlay/monetary-js");
const chai_1 = require("chai");
const big_js_1 = __importDefault(require("big.js"));
const src_1 = require("../../../src");
const rewards_1 = require("../../../src/utils/rewards");
const WEEK_IN_BLOCKS = 54000;
const MINIMUM_BLOCK_PERIOD = 6000; // 6s parachain constant
const MAX_PERIOD = 4838400; // Kintsugi max locking period
describe("Escrow", () => {
    let kintCurrency;
    before(() => __awaiter(void 0, void 0, void 0, function* () {
        kintCurrency = monetary_js_1.Kintsugi;
    }));
    it("should calculate rewards for the first staker with only the amount", () => {
        // user input
        const amountToLock = new monetary_js_1.KintsugiAmount(100);
        // existing system
        const userStake = new big_js_1.default(0);
        const totalStake = new big_js_1.default(0);
        const blockReward = src_1.newMonetaryAmount(1, kintCurrency);
        const stakedBalance = {
            amount: src_1.newMonetaryAmount(0, kintCurrency),
            endBlock: 0,
        };
        const currentBlockNumber = 1000;
        // expected outputs
        const amount = "0";
        const apy = "0";
        const resultAmount = rewards_1.estimateReward(kintCurrency, userStake, totalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD, amountToLock, undefined);
        chai_1.assert.equal(resultAmount.amount.toString(), amount);
        chai_1.assert.equal(resultAmount.apy.toString(), apy);
    });
    it("should calculate rewards for the first staker with only a locktime extension", () => {
        // user input
        const blockLockTimeExtension = 5 * WEEK_IN_BLOCKS; // 5 weeks lock
        // existing system
        const userStake = new big_js_1.default(0);
        const totalStake = new big_js_1.default(0);
        const blockReward = src_1.newMonetaryAmount(1, kintCurrency);
        const stakedBalance = {
            amount: src_1.newMonetaryAmount(0, kintCurrency),
            endBlock: 0,
        };
        const currentBlockNumber = 1000;
        // expected outputs
        const amount = "0";
        const apy = "0";
        const resultTime = rewards_1.estimateReward(kintCurrency, userStake, totalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD, undefined, blockLockTimeExtension);
        chai_1.assert.equal(resultTime.amount.toString(), amount);
        chai_1.assert.equal(resultTime.apy.toString(), apy);
    });
    it("should calculate rewards for the first staker with an amount and a locktime extension", () => {
        // user input
        const amountToLock = new monetary_js_1.KintsugiAmount(100);
        const blockLockTimeExtension = 5 * WEEK_IN_BLOCKS; // 5 weeks lock
        // existing system
        const userStake = new big_js_1.default(0);
        const totalStake = new big_js_1.default(0);
        const blockReward = src_1.newMonetaryAmount(1, kintCurrency);
        const stakedBalance = {
            amount: src_1.newMonetaryAmount(0, kintCurrency),
            endBlock: 0,
        };
        const currentBlockNumber = 1000;
        // expected outputs
        // staker gets 100% of rewards
        const amount = blockReward.mul(blockLockTimeExtension);
        // projects block rewards for 1 year
        const apy = blockReward
            .mul(60 * 60 * 24 * 365 * 1000) // ms per year
            .div(MINIMUM_BLOCK_PERIOD * 2)
            .div(amountToLock.toBig())
            .mul(100);
        const resultAmountAndTime = rewards_1.estimateReward(kintCurrency, userStake, totalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD, amountToLock, blockLockTimeExtension);
        chai_1.assert.equal(resultAmountAndTime.amount.toString(), amount.toString());
        chai_1.assert.isTrue(resultAmountAndTime.amount.toBig().gt(0), `${resultAmountAndTime.amount.toString()} not greater than 0`);
        chai_1.assert.equal(resultAmountAndTime.apy.toString(), apy.toBig().toString());
        chai_1.assert.isTrue(resultAmountAndTime.apy.gt(0), `${resultAmountAndTime.apy.toString()} not greater than 0`);
    });
    it("should return 0 rewards", () => {
        // user input
        const amountToLock = new monetary_js_1.KintsugiAmount(0);
        const blockLockTimeExtension = 0;
        // existing system
        const userStake = new big_js_1.default(0);
        const totalStake = new big_js_1.default(20000000);
        const blockReward = src_1.newMonetaryAmount(1, kintCurrency, true);
        const stakedBalance = {
            amount: src_1.newMonetaryAmount(0, kintCurrency),
            endBlock: 0,
        };
        const currentBlockNumber = 1000;
        // expected outputs
        // staker gets 100% of rewards
        const amount = "0";
        // projects block rewards for 1 year
        const apy = "0";
        const resultAmountAndTime = rewards_1.estimateReward(kintCurrency, userStake, totalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD, amountToLock, blockLockTimeExtension);
        chai_1.assert.equal(resultAmountAndTime.amount.toString(), amount);
        chai_1.assert.equal(resultAmountAndTime.apy.toString(), apy);
        const resultAmount = rewards_1.estimateReward(kintCurrency, userStake, totalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD, amountToLock, undefined);
        chai_1.assert.equal(resultAmount.amount.toString(), amount);
        chai_1.assert.equal(resultAmount.apy.toString(), apy);
        const resultTime = rewards_1.estimateReward(kintCurrency, userStake, totalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD, undefined, blockLockTimeExtension);
        chai_1.assert.equal(resultTime.amount.toString(), amount);
        chai_1.assert.equal(resultTime.apy.toString(), apy);
        const resultNoInput = rewards_1.estimateReward(kintCurrency, userStake, totalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD);
        chai_1.assert.equal(resultNoInput.amount.toString(), amount);
        chai_1.assert.equal(resultNoInput.apy.toString(), apy);
    });
    it("should calculate rewards for the next staker", () => {
        // user input
        const amountToLock = new monetary_js_1.KintsugiAmount(500);
        const blockLockTimeExtension = 10 * WEEK_IN_BLOCKS; // 10 weeks lock
        // existing system
        // this user's stake
        const userStake = new big_js_1.default(0); // this user has 0
        // other users have 100 locked for 5 weeks
        const totalStake = new monetary_js_1.KintsugiAmount(100).toBig().mul(5 * WEEK_IN_BLOCKS);
        const blockReward = src_1.newMonetaryAmount(1, kintCurrency, true);
        const stakedBalance = {
            amount: src_1.newMonetaryAmount(0, kintCurrency),
            endBlock: 0,
        };
        const currentBlockNumber = 1000;
        // expected outputs
        // staker gets a share of the rewards
        const newUserStake = amountToLock.toBig(0).mul(blockLockTimeExtension).div(MAX_PERIOD);
        const amount = newUserStake
            .div(newUserStake.add(totalStake)) // new total stake
            .mul(blockReward.toBig())
            .mul(blockLockTimeExtension);
        const blockTime = MINIMUM_BLOCK_PERIOD * 2; // ms
        const blocksPerYear = (60 * 60 * 24 * 365 * 1000) / blockTime;
        // projects block rewards for 1 year
        const apy = amount.div(blockLockTimeExtension).mul(blocksPerYear).div(amountToLock.toBig()).mul(100);
        const result = rewards_1.estimateReward(kintCurrency, userStake, totalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD, amountToLock, blockLockTimeExtension);
        chai_1.assert.equal(result.amount.toString(), amount.round(kintCurrency.decimals, 0).toString());
        chai_1.assert.isTrue(result.amount.toBig().gt(0), `${result.amount.toString()} not greater than 0`);
        chai_1.assert.equal(result.apy.round(0).toString(), apy.round(0).toString());
        chai_1.assert.isTrue(result.apy.gt(0), `${result.apy.toString()} not greater than 0`);
    });
    it("should calculate rewards for increasing existing stake", () => {
        // user input
        const baseAmountToLock = src_1.atomicToBaseAmount(500, monetary_js_1.Kintsugi);
        const amountToLock = new monetary_js_1.KintsugiAmount(baseAmountToLock);
        const blockLockTimeExtension = 10 * WEEK_IN_BLOCKS; // 10 weeks lock
        // existing system
        const currentBlockNumber = 1000;
        // this user's stake is 100
        const stakedBalance = {
            amount: src_1.newMonetaryAmount(100, kintCurrency, true),
            endBlock: 5 * WEEK_IN_BLOCKS,
        };
        const atomicUserStake = stakedBalance.amount
            .toBig(src_1.ATOMIC_UNIT)
            .mul(stakedBalance.endBlock - currentBlockNumber)
            .div(MAX_PERIOD);
        // other users have locked an additional 500 locked for 5 weeks
        const atomicTotalStake = new monetary_js_1.KintsugiAmount(src_1.atomicToBaseAmount(500, monetary_js_1.Kintsugi))
            .toBig(src_1.ATOMIC_UNIT)
            .mul(5 * WEEK_IN_BLOCKS)
            .div(MAX_PERIOD);
        const blockReward = src_1.newMonetaryAmount(1, kintCurrency, true);
        // expected outputs
        // staker gets a share of the rewards
        const newLockDuration = stakedBalance.endBlock + blockLockTimeExtension - currentBlockNumber;
        const newUserStake = stakedBalance.amount
            .toBig(src_1.ATOMIC_UNIT)
            .add(amountToLock.toBig(src_1.ATOMIC_UNIT))
            .mul(newLockDuration)
            .div(MAX_PERIOD);
        const userStakeDifference = newUserStake.sub(atomicUserStake);
        const amount = blockReward
            .mul(newUserStake) // user stake
            .div(userStakeDifference.add(atomicTotalStake)) // new total stake
            .mul(newLockDuration);
        // projects block rewards for 1 year
        const apy = amount
            .toBig()
            .div(newLockDuration)
            .mul(60 * 60 * 24 * 365 * 1000) // ms per year
            .div(MINIMUM_BLOCK_PERIOD * 2)
            .div(stakedBalance.amount.add(amountToLock).toBig())
            .mul(100);
        const result = rewards_1.estimateReward(kintCurrency, atomicUserStake, atomicTotalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD, amountToLock, blockLockTimeExtension);
        chai_1.assert.equal(result.amount.toString(), amount.toString());
        chai_1.assert.isTrue(result.amount.toBig().gt(0), `${result.amount.toString()} not greater than 0`);
        chai_1.assert.equal(result.apy.round(0).toString(), apy.round(0).toString());
        chai_1.assert.isTrue(result.apy.gt(0), `${result.apy.toString()} not greater than 0`);
    });
    it("should calculate rewards for current staked amounts", () => {
        // existing system
        const currentBlockNumber = 1000;
        const blockReward = src_1.newMonetaryAmount(1, kintCurrency, true);
        const stakedBalance = {
            amount: src_1.newMonetaryAmount(100, kintCurrency, true),
            endBlock: 5 * WEEK_IN_BLOCKS,
        };
        // this user's stake is 100
        const atomicUserStake = stakedBalance.amount
            .toBig(src_1.ATOMIC_UNIT)
            .mul(stakedBalance.endBlock - currentBlockNumber)
            .div(MAX_PERIOD);
        // other users have locked an additional 500 locked for 5 weeks
        const atomicTotalStake = new monetary_js_1.KintsugiAmount(600)
            .toBig(src_1.ATOMIC_UNIT)
            .mul(5 * WEEK_IN_BLOCKS)
            .div(MAX_PERIOD);
        // expected outputs
        // staker gets a share of the rewards
        const amount = blockReward
            .mul(atomicUserStake) // user stake
            .div(atomicTotalStake) // total stake
            .mul(stakedBalance.endBlock - currentBlockNumber);
        // projects block rewards for 1 year
        const apy = amount
            .toBig()
            .div(stakedBalance.endBlock - currentBlockNumber)
            .mul(60 * 60 * 24 * 365 * 1000) // ms per year
            .div(MINIMUM_BLOCK_PERIOD * 2)
            .div(stakedBalance.amount.toBig())
            .mul(100);
        const result = rewards_1.estimateReward(kintCurrency, atomicUserStake, atomicTotalStake, blockReward, stakedBalance, currentBlockNumber, MINIMUM_BLOCK_PERIOD, MAX_PERIOD);
        chai_1.assert.equal(result.amount.toString(), amount.toString());
        chai_1.assert.isTrue(result.amount.toBig().gt(0), `${result.amount.toString()} not greater than 0`);
        chai_1.assert.equal(result.apy.round(2).toString(), apy.round(2).toString());
        chai_1.assert.isTrue(result.apy.gt(0), `${result.apy.toString()} not greater than 0`);
    });
});
//# sourceMappingURL=rewards.test.js.map