"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const initTestEnvironment_1 = require("../utils/initTestEnvironment");
const token_math_1 = require("@melonproject/token-math");
const updateTestingPriceFeed_1 = require("../utils/updateTestingPriceFeed");
const getAllBalances_1 = require("../utils/getAllBalances");
const beginSetup_1 = require("../../contracts/factory/transactions/beginSetup");
const getToken_1 = require("../../contracts/dependencies/token/calls/getToken");
const completeSetup_1 = require("../../contracts/factory/transactions/completeSetup");
const createAccounting_1 = require("../../contracts/factory/transactions/createAccounting");
const createFeeManager_1 = require("../../contracts/factory/transactions/createFeeManager");
const createParticipation_1 = require("../../contracts/factory/transactions/createParticipation");
const createPolicyManager_1 = require("../../contracts/factory/transactions/createPolicyManager");
const createShares_1 = require("../../contracts/factory/transactions/createShares");
const createTrading_1 = require("../../contracts/factory/transactions/createTrading");
const createVault_1 = require("../../contracts/factory/transactions/createVault");
const getFundComponents_1 = require("../../utils/getFundComponents");
const withDifferentAccount_1 = require("../../utils/environment/withDifferentAccount");
const deployAndGetSystem_1 = require("../utils/deployAndGetSystem");
const getContract_1 = require("../../utils/solidity/getContract");
const deployContract_1 = require("../../utils/solidity/deployContract");
const Contracts_1 = require("../../Contracts");
const precisionUnits = token_math_1.power(new token_math_1.BigInteger(10), new token_math_1.BigInteger(18));
let s = {};
beforeAll(() => __awaiter(this, void 0, void 0, function* () {
    s.environment = yield initTestEnvironment_1.initTestEnvironment();
    s.accounts = yield s.environment.eth.getAccounts();
    const { addresses, contracts } = yield deployAndGetSystem_1.deployAndGetSystem(s.environment);
    s.addresses = addresses;
    s = Object.assign(s, contracts);
    [s.deployer, s.manager, s.investor] = s.accounts;
    s.gas = 8000000;
    s.mlnTokenInterface = yield getToken_1.getToken(s.environment, s.mln.options.address);
    s.dgxTokenInterface = yield getToken_1.getToken(s.environment, s.dgx.options.address);
    s.wethTokenInterface = yield getToken_1.getToken(s.environment, s.weth.options.address);
    const exchangeConfigs = {};
    // Init fees
    s.yearInSeconds = new token_math_1.BigInteger(31536000);
    s.managementFee = getContract_1.getContract(s.environment, Contracts_1.Contracts.ManagementFee, yield deployContract_1.deployContract(s.environment, Contracts_1.Contracts.ManagementFee, []));
    s.managementFeeRate = token_math_1.multiply(new token_math_1.BigInteger(2), token_math_1.power(new token_math_1.BigInteger(10), new token_math_1.BigInteger(16)));
    const fees = [
        {
            feeAddress: s.managementFee.options.address,
            feePeriod: new token_math_1.BigInteger(0),
            feeRate: s.managementFeeRate,
        },
    ];
    const envManager = withDifferentAccount_1.withDifferentAccount(s.environment, s.manager);
    yield beginSetup_1.beginSetup(envManager, s.version.options.address, {
        defaultTokens: [
            s.wethTokenInterface,
            s.mlnTokenInterface,
            s.dgxTokenInterface,
        ],
        exchangeConfigs,
        fees,
        fundName: 'Test fund',
        nativeToken: s.wethTokenInterface,
        priceSource: s.priceSource.options.address,
        quoteToken: s.wethTokenInterface,
    });
    yield createAccounting_1.createAccounting(envManager, s.version.options.address);
    yield createFeeManager_1.createFeeManager(envManager, s.version.options.address);
    yield createParticipation_1.createParticipation(envManager, s.version.options.address);
    yield createPolicyManager_1.createPolicyManager(envManager, s.version.options.address);
    yield createShares_1.createShares(envManager, s.version.options.address);
    yield createTrading_1.createTrading(envManager, s.version.options.address);
    yield createVault_1.createVault(envManager, s.version.options.address);
    const hubAddress = yield completeSetup_1.completeSetup(envManager, s.version.options.address);
    s.fund = yield getFundComponents_1.getFundComponents(envManager, hubAddress);
    yield updateTestingPriceFeed_1.updateTestingPriceFeed(s, s.environment);
}));
test(`fund gets ethToken from investment`, () => __awaiter(this, void 0, void 0, function* () {
    const initialTokenAmount = token_math_1.power(new token_math_1.BigInteger(10), new token_math_1.BigInteger(21));
    yield s.weth.methods
        .transfer(s.investor, `${initialTokenAmount}`)
        .send({ from: s.deployer });
    const wantedShares = token_math_1.power(new token_math_1.BigInteger(10), new token_math_1.BigInteger(20));
    // const pre = await getAllBalances(s, s.accounts, s.fund, s.environment);
    const preTotalSupply = yield s.fund.shares.methods.totalSupply().call();
    yield s.weth.methods
        .approve(s.fund.participation.options.address, wantedShares)
        .send({ from: s.investor, gas: s.gas });
    yield s.fund.participation.methods
        .requestInvestment(`${wantedShares}`, `${wantedShares}`, s.weth.options.address)
        .send({ from: s.investor, gas: s.gas, value: '10000000000000000' });
    yield updateTestingPriceFeed_1.updateTestingPriceFeed(s, s.environment);
    yield updateTestingPriceFeed_1.updateTestingPriceFeed(s, s.environment);
    yield s.fund.participation.methods
        .executeRequestFor(s.investor)
        .send({ from: s.investor, gas: s.gas });
    // const post = await getAllBalances(s, s.accounts, s.fund, s.environment);
    const postTotalSupply = yield s.fund.shares.methods.totalSupply().call();
    expect(postTotalSupply).toEqual(token_math_1.add(token_math_1.toBI(preTotalSupply), wantedShares));
}));
test(`Reward fee rewards management fee in the form of shares`, () => __awaiter(this, void 0, void 0, function* () {
    const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const preManagerShares = new token_math_1.BigInteger(yield s.fund.shares.methods.balanceOf(s.manager).call());
    const fundCreationTime = new token_math_1.BigInteger(yield s.managementFee.methods
        .lastPayoutTime(s.fund.feeManager.options.address)
        .call());
    const preTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call());
    const preFundCalculations = yield s.fund.accounting.methods
        .performCalculations()
        .call();
    yield s.fund.feeManager.methods
        .rewardManagementFee()
        .send({ from: s.manager, gas: s.gas });
    const payoutTime = new token_math_1.BigInteger(yield s.managementFee.methods
        .lastPayoutTime(s.fund.feeManager.options.address)
        .call());
    const expectedPreDilutionFeeShares = token_math_1.divide(token_math_1.multiply(token_math_1.divide(token_math_1.multiply(preTotalSupply, token_math_1.toBI(s.managementFeeRate)), precisionUnits), token_math_1.subtract(payoutTime, fundCreationTime)), token_math_1.toBI(s.yearInSeconds));
    const expectedFeeShares = token_math_1.divide(token_math_1.multiply(preTotalSupply, expectedPreDilutionFeeShares), token_math_1.subtract(preTotalSupply, expectedPreDilutionFeeShares));
    const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const postManagerShares = new token_math_1.BigInteger(yield s.fund.shares.methods.balanceOf(s.manager).call());
    const postTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call());
    const postFundCalculations = yield s.fund.accounting.methods
        .performCalculations()
        .call();
    expect(postManagerShares).toEqual(token_math_1.add(preManagerShares, expectedFeeShares));
    expect(postTotalSupply).toEqual(token_math_1.add(preTotalSupply, expectedFeeShares));
    expect(postFundCalculations.gav).toEqual(preFundCalculations.gav);
    // expect(postFundCalculations.sharePrice).toEqual(
    //   preFundCalculations.sharePrice,
    // );
    expect(post.fund.weth).toEqual(pre.fund.weth);
    expect(post.manager.weth).toEqual(pre.manager.weth);
}));
test(`Claims fee using triggerRewardAllFees`, () => __awaiter(this, void 0, void 0, function* () {
    const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const preManagerShares = new token_math_1.BigInteger(yield s.fund.shares.methods.balanceOf(s.manager).call());
    const lastFeeConversion = new token_math_1.BigInteger(yield s.managementFee.methods
        .lastPayoutTime(s.fund.feeManager.options.address)
        .call());
    const preTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call());
    const preFundCalculations = yield s.fund.accounting.methods
        .performCalculations()
        .call();
    yield s.fund.accounting.methods
        .triggerRewardAllFees()
        .send({ from: s.manager, gas: s.gas });
    const payoutTime = new token_math_1.BigInteger(yield s.managementFee.methods
        .lastPayoutTime(s.fund.feeManager.options.address)
        .call());
    const expectedPreDilutionFeeShares = token_math_1.divide(token_math_1.multiply(token_math_1.divide(token_math_1.multiply(preTotalSupply, token_math_1.toBI(s.managementFeeRate)), precisionUnits), token_math_1.subtract(payoutTime, lastFeeConversion)), token_math_1.toBI(s.yearInSeconds));
    const expectedFeeShares = token_math_1.divide(token_math_1.multiply(preTotalSupply, expectedPreDilutionFeeShares), token_math_1.subtract(preTotalSupply, expectedPreDilutionFeeShares));
    // const expectedFeeInDenominationAsset = divide(
    //   multiply(expectedFeeShares, toBI(preFundCalculations.gav)),
    //   add(preTotalSupply, expectedFeeShares),
    // );
    const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const postManagerShares = new token_math_1.BigInteger(yield s.fund.shares.methods.balanceOf(s.manager).call());
    const postTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call());
    const postFundCalculations = yield s.fund.accounting.methods
        .performCalculations()
        .call();
    // const lastConversionCalculations = await s.fund.accounting.methods
    //   .atLastAllocation()
    //   .call();
    expect(postManagerShares).toEqual(token_math_1.add(preManagerShares, expectedFeeShares));
    expect(postTotalSupply).toEqual(token_math_1.add(preTotalSupply, expectedFeeShares));
    expect(postFundCalculations.gav).toEqual(preFundCalculations.gav);
    // TODO: Uncomment these and fix them
    // expect(postFundCalculations.sharePrice).toEqual(
    //   preFundCalculations.sharePrice,
    // );
    // expect(new BigInteger(preFundCalculations.feesInDenominationAsset)).toBe(
    //   expectedFeeInDenominationAsset,
    // );
    // expect(new BigInteger(lastConversionCalculations.allocatedFees)).toEqual(
    //   expectedFeeInDenominationAsset,
    // );
    expect(post.fund.weth).toEqual(pre.fund.weth);
    expect(post.manager.weth).toEqual(pre.manager.weth);
}));
test(`investor redeems his shares`, () => __awaiter(this, void 0, void 0, function* () {
    const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const investorShares = yield s.fund.shares.methods
        .balanceOf(s.investor)
        .call();
    const fundCreationTime = new token_math_1.BigInteger(yield s.managementFee.methods
        .lastPayoutTime(s.fund.feeManager.options.address)
        .call());
    const preTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call());
    yield s.fund.participation.methods
        .redeem()
        .send({ from: s.investor, gas: s.gas });
    const payoutTime = new token_math_1.BigInteger(yield s.managementFee.methods
        .lastPayoutTime(s.fund.feeManager.options.address)
        .call());
    const expectedPreDilutionFeeShares = token_math_1.divide(token_math_1.multiply(token_math_1.divide(token_math_1.multiply(preTotalSupply, token_math_1.toBI(s.managementFeeRate)), precisionUnits), token_math_1.subtract(payoutTime, fundCreationTime)), s.yearInSeconds);
    const expectedFeeShares = token_math_1.divide(token_math_1.multiply(preTotalSupply, expectedPreDilutionFeeShares), token_math_1.subtract(preTotalSupply, expectedPreDilutionFeeShares));
    const postFundGav = new token_math_1.BigInteger(yield s.fund.accounting.methods.calcGav().call());
    const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const postTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call());
    // const expectedFeeShareProportion = divide(
    //   multiply(expectedFeeShares, precisionUnits),
    //   add(preTotalSupply, expectedFeeShares),
    // );
    expect(postTotalSupply).toEqual(token_math_1.add(token_math_1.subtract(preTotalSupply, token_math_1.toBI(investorShares)), expectedFeeShares));
    // expect(post.investor.weth).toEqual(
    //   add(
    //     pre.investor.weth,
    //     divide(
    //       multiply(
    //         pre.fund.weth,
    //         subtract(precisionUnits, expectedFeeShareProportion),
    //       ),
    //       precisionUnits,
    //     ),
    //   ),
    // );
    expect(post.fund.weth).toEqual(token_math_1.subtract(pre.fund.weth, token_math_1.subtract(post.investor.weth, pre.investor.weth)));
    expect(postFundGav).toEqual(post.fund.weth);
}));
