// This Util calculates the across realized LP fees. See https://gist.github.com/chrismaree/a713725e4fe96c531c42ed7b629d4a85
// gist for a python implementation of the logic in this file. This implementation is designed to work with both web3.js
// and ethers BNs in the main entry point function calculateRealizedLpFeePct.
import Decimal from "decimal.js";
import { toBN, toBNWei, fromWei, min, max, fixedPointAdjustment } from "../utils";
const ONE_HUNDRED_PERCENT = toBN(10).pow(18);
// converts an APY rate to a one week rate. Uses the Decimal library to take a fractional exponent
function convertApyToWeeklyFee(apy) {
    // R_week = (1 + apy)^(1/52) - 1
    const weeklyFeePct = Decimal.pow(new Decimal("1").plus(fromWei(apy)), new Decimal("1").dividedBy(new Decimal("52"))).minus(new Decimal("1"));
    // Convert from decimal back to BN, scaled by 1e18.
    return toBN(weeklyFeePct.times(fixedPointAdjustment.toString()).floor().toString());
}
export function truncate18DecimalBN(input, digits) {
    const digitsToDrop = 18 - digits;
    const multiplier = toBN(10).pow(digitsToDrop);
    return input.div(multiplier).mul(multiplier);
}
export class LPFeeCalculator {
    rateModel;
    constructor(rateModel) {
        this.rateModel = rateModel;
    }
    /**
     * Compute area under curve of the piece-wise linear rate model.
     * @param rateModel Rate model to be used in this calculation.
     * @param utilization The current utilization of the pool.
     * @returns The area under the curve of the piece-wise linear rate model.
     */
    calculateAreaUnderRateCurve(utilization) {
        // Area under first piecewise component
        const utilizationBeforeKink = min(utilization, this.rateModel.UBar);
        const rectangle1Area = utilizationBeforeKink.mul(this.rateModel.R0).div(fixedPointAdjustment);
        const triangle1Area = toBNWei("0.5")
            .mul(this.calculateInstantaneousRate(utilizationBeforeKink).sub(this.rateModel.R0))
            .mul(utilizationBeforeKink)
            .div(fixedPointAdjustment)
            .div(fixedPointAdjustment);
        // Area under second piecewise component
        const utilizationAfter = max(toBN("0"), utilization.sub(this.rateModel.UBar));
        const rectangle2Area = utilizationAfter
            .mul(toBN(this.rateModel.R0).add(this.rateModel.R1))
            .div(fixedPointAdjustment);
        const triangle2Area = toBNWei("0.5")
            .mul(this.calculateInstantaneousRate(utilization).sub(toBN(this.rateModel.R0).add(this.rateModel.R1)))
            .mul(utilizationAfter)
            .div(fixedPointAdjustment)
            .div(fixedPointAdjustment);
        return rectangle1Area.add(triangle1Area).add(rectangle2Area).add(triangle2Area);
    }
    /**
     * Calculate the instantaneous rate for a 0 sized deposit (infinitesimally small).
     * @param utilization The current utilization of the pool.
     * @returns The instantaneous rate for a 0 sized deposit.
     */
    calculateInstantaneousRate(utilization) {
        // Assuming utilization >= 0, if UBar = 0 then the value beforeKink is 0 since min(>=0, 0) = 0.
        const beforeKink = this.rateModel.UBar.toString() === "0"
            ? toBN(0)
            : min(utilization, this.rateModel.UBar).mul(this.rateModel.R1).div(this.rateModel.UBar);
        const afterKink = max(toBN("0"), toBN(utilization).sub(this.rateModel.UBar))
            .mul(this.rateModel.R2)
            .div(toBNWei("1").sub(this.rateModel.UBar));
        return toBN(this.rateModel.R0).add(beforeKink).add(afterKink);
    }
    /**
     * Calculate the realized LP Fee Percent for a given rate model, utilization before and after the deposit.
     * @param rateModel Rate model to be used in this calculation.
     * @param utilizationBeforeDeposit The utilization of the pool before the deposit.
     * @param utilizationAfterDeposit The utilization of the pool after the deposit.
     * @param truncateDecimals Whether to truncate the decimals to 6.
     * @returns The realized LP fee percent.
     */
    calculateRealizedLpFeePct(utilizationBeforeDeposit, utilizationAfterDeposit, truncateDecimals = false) {
        const apy = this.calculateApyFromUtilization(toBN(utilizationBeforeDeposit), toBN(utilizationAfterDeposit));
        // ACROSS UMIP requires that the realized fee percent is floor rounded as decimal to 6 decimals.
        return min(ONE_HUNDRED_PERCENT, truncateDecimals ? truncate18DecimalBN(convertApyToWeeklyFee(apy), 6) : convertApyToWeeklyFee(apy));
    }
    /**
     * Calculate the realized yearly LP Fee APY Percent for a given rate model, utilization before and after the deposit.
     * @param rateModel Rate model to be used in this calculation.
     * @param utilizationBeforeDeposit The utilization of the pool before the deposit.
     * @param utilizationAfterDeposit The utilization of the pool after the deposit.
     * @returns The realized LP fee APY percent.
     */
    calculateApyFromUtilization(utilizationBeforeDeposit, utilizationAfterDeposit) {
        if (utilizationBeforeDeposit.eq(utilizationAfterDeposit))
            return this.calculateInstantaneousRate(utilizationBeforeDeposit);
        // Get the area of [0, utilizationBeforeDeposit] and [0, utilizationAfterDeposit]
        const areaBeforeDeposit = this.calculateAreaUnderRateCurve(utilizationBeforeDeposit);
        const areaAfterDeposit = this.calculateAreaUnderRateCurve(utilizationAfterDeposit);
        const numerator = areaAfterDeposit.sub(areaBeforeDeposit);
        const denominator = utilizationAfterDeposit.sub(utilizationBeforeDeposit);
        return numerator.mul(fixedPointAdjustment).div(denominator);
    }
}
/**
 * Calculate the instantaneous rate for a 0 sized deposit (infinitesimally small).
 * @param rateModel Rate model to be used in this calculation.
 * @param utilization The current utilization of the pool.
 * @returns The instantaneous rate for a 0 sized deposit.
 */
export function calculateInstantaneousRate(rateModel, utilization) {
    return new LPFeeCalculator(rateModel).calculateInstantaneousRate(utilization);
}
/**
 * Calculate the realized yearly LP Fee APY Percent for a given rate model, utilization before and after the deposit.
 * @param rateModel Rate model to be used in this calculation.
 * @param utilizationBeforeDeposit The utilization of the pool before the deposit.
 * @param utilizationAfterDeposit The utilization of the pool after the deposit.
 * @returns The realized LP fee APY percent.
 */
export function calculateApyFromUtilization(rateModel, utilizationBeforeDeposit, utilizationAfterDeposit) {
    return new LPFeeCalculator(rateModel).calculateApyFromUtilization(utilizationBeforeDeposit, utilizationAfterDeposit);
}
/**
 * Calculate the realized LP Fee Percent for a given rate model, utilization before and after the deposit.
 * @param rateModel Rate model to be used in this calculation.
 * @param utilizationBeforeDeposit The utilization of the pool before the deposit.
 * @param utilizationAfterDeposit The utilization of the pool after the deposit.
 * @param truncateDecimals Whether to truncate the decimals to 6.
 * @returns The realized LP fee percent.
 */
export function calculateRealizedLpFeePct(rateModel, utilizationBeforeDeposit, utilizationAfterDeposit, truncateDecimals = false) {
    return new LPFeeCalculator(rateModel).calculateRealizedLpFeePct(utilizationBeforeDeposit, utilizationAfterDeposit, truncateDecimals);
}
//# sourceMappingURL=lpFeeCalculator.js.map