"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LPFeeCalculator = void 0;
exports.truncate18DecimalBN = truncate18DecimalBN;
exports.calculateInstantaneousRate = calculateInstantaneousRate;
exports.calculateApyFromUtilization = calculateApyFromUtilization;
exports.calculateRealizedLpFeePct = calculateRealizedLpFeePct;
const tslib_1 = require("tslib");
const decimal_js_1 = tslib_1.__importDefault(require("decimal.js"));
const utils_1 = require("../utils");
const ONE_HUNDRED_PERCENT = (0, utils_1.toBN)(10).pow(18);
function convertApyToWeeklyFee(apy) {
    const weeklyFeePct = decimal_js_1.default.pow(new decimal_js_1.default("1").plus((0, utils_1.fromWei)(apy)), new decimal_js_1.default("1").dividedBy(new decimal_js_1.default("52"))).minus(new decimal_js_1.default("1"));
    return (0, utils_1.toBN)(weeklyFeePct.times(utils_1.fixedPointAdjustment.toString()).floor().toString());
}
function truncate18DecimalBN(input, digits) {
    const digitsToDrop = 18 - digits;
    const multiplier = (0, utils_1.toBN)(10).pow(digitsToDrop);
    return input.div(multiplier).mul(multiplier);
}
class LPFeeCalculator {
    rateModel;
    constructor(rateModel) {
        this.rateModel = rateModel;
    }
    calculateAreaUnderRateCurve(utilization) {
        const utilizationBeforeKink = (0, utils_1.min)(utilization, this.rateModel.UBar);
        const rectangle1Area = utilizationBeforeKink.mul(this.rateModel.R0).div(utils_1.fixedPointAdjustment);
        const triangle1Area = (0, utils_1.toBNWei)("0.5")
            .mul(this.calculateInstantaneousRate(utilizationBeforeKink).sub(this.rateModel.R0))
            .mul(utilizationBeforeKink)
            .div(utils_1.fixedPointAdjustment)
            .div(utils_1.fixedPointAdjustment);
        const utilizationAfter = (0, utils_1.max)((0, utils_1.toBN)("0"), utilization.sub(this.rateModel.UBar));
        const rectangle2Area = utilizationAfter
            .mul((0, utils_1.toBN)(this.rateModel.R0).add(this.rateModel.R1))
            .div(utils_1.fixedPointAdjustment);
        const triangle2Area = (0, utils_1.toBNWei)("0.5")
            .mul(this.calculateInstantaneousRate(utilization).sub((0, utils_1.toBN)(this.rateModel.R0).add(this.rateModel.R1)))
            .mul(utilizationAfter)
            .div(utils_1.fixedPointAdjustment)
            .div(utils_1.fixedPointAdjustment);
        return rectangle1Area.add(triangle1Area).add(rectangle2Area).add(triangle2Area);
    }
    calculateInstantaneousRate(utilization) {
        const beforeKink = this.rateModel.UBar.toString() === "0"
            ? (0, utils_1.toBN)(0)
            : (0, utils_1.min)(utilization, this.rateModel.UBar).mul(this.rateModel.R1).div(this.rateModel.UBar);
        const afterKink = (0, utils_1.max)((0, utils_1.toBN)("0"), (0, utils_1.toBN)(utilization).sub(this.rateModel.UBar))
            .mul(this.rateModel.R2)
            .div((0, utils_1.toBNWei)("1").sub(this.rateModel.UBar));
        return (0, utils_1.toBN)(this.rateModel.R0).add(beforeKink).add(afterKink);
    }
    calculateRealizedLpFeePct(utilizationBeforeDeposit, utilizationAfterDeposit, truncateDecimals = false) {
        const apy = this.calculateApyFromUtilization((0, utils_1.toBN)(utilizationBeforeDeposit), (0, utils_1.toBN)(utilizationAfterDeposit));
        return (0, utils_1.min)(ONE_HUNDRED_PERCENT, truncateDecimals ? truncate18DecimalBN(convertApyToWeeklyFee(apy), 6) : convertApyToWeeklyFee(apy));
    }
    calculateApyFromUtilization(utilizationBeforeDeposit, utilizationAfterDeposit) {
        if (utilizationBeforeDeposit.eq(utilizationAfterDeposit))
            return this.calculateInstantaneousRate(utilizationBeforeDeposit);
        const areaBeforeDeposit = this.calculateAreaUnderRateCurve(utilizationBeforeDeposit);
        const areaAfterDeposit = this.calculateAreaUnderRateCurve(utilizationAfterDeposit);
        const numerator = areaAfterDeposit.sub(areaBeforeDeposit);
        const denominator = utilizationAfterDeposit.sub(utilizationBeforeDeposit);
        return numerator.mul(utils_1.fixedPointAdjustment).div(denominator);
    }
}
exports.LPFeeCalculator = LPFeeCalculator;
function calculateInstantaneousRate(rateModel, utilization) {
    return new LPFeeCalculator(rateModel).calculateInstantaneousRate(utilization);
}
function calculateApyFromUtilization(rateModel, utilizationBeforeDeposit, utilizationAfterDeposit) {
    return new LPFeeCalculator(rateModel).calculateApyFromUtilization(utilizationBeforeDeposit, utilizationAfterDeposit);
}
function calculateRealizedLpFeePct(rateModel, utilizationBeforeDeposit, utilizationAfterDeposit, truncateDecimals = false) {
    return new LPFeeCalculator(rateModel).calculateRealizedLpFeePct(utilizationBeforeDeposit, utilizationAfterDeposit, truncateDecimals);
}
//# sourceMappingURL=lpFeeCalculator.js.map