UNPKG

@melonproject/protocol

Version:

Technology Regulated and Operated Investment Funds

189 lines (188 loc) 13.6 kB
"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 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 increaseTime_1 = require("../../utils/evm/increaseTime"); const getAllBalances_1 = require("../utils/getAllBalances"); const registerFees_1 = require("../../contracts/version/transactions/registerFees"); 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.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.performanceFee = getContract_1.getContract(s.environment, Contracts_1.Contracts.PerformanceFee, yield deployContract_1.deployContract(s.environment, Contracts_1.Contracts.PerformanceFee, [])); s.performanceFeePeriod = new token_math_1.BigInteger(1000); s.performanceFeeRate = new token_math_1.BigInteger(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(17)))); const fees = [ { feeAddress: s.managementFee.options.address, feePeriod: new token_math_1.BigInteger(0), feeRate: new token_math_1.BigInteger(0), }, { feeAddress: s.performanceFee.options.address, feePeriod: s.performanceFeePeriod, feeRate: s.performanceFeeRate, }, ]; const envManager = withDifferentAccount_1.withDifferentAccount(s.environment, s.manager); yield registerFees_1.registerFees(s.environment, s.registry.options.address, { addresses: fees.map(f => f.feeAddress), }); yield beginSetup_1.beginSetup(envManager, s.version.options.address, { defaultTokens: [s.wethTokenInterface, s.mlnTokenInterface], exchangeConfigs, fees, fundName: 'Test fund', 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 }); s.wantedShares = token_math_1.power(new token_math_1.BigInteger(10), new token_math_1.BigInteger(20)); const preTotalSupply = yield s.fund.shares.methods.totalSupply().call(); yield s.weth.methods .approve(s.fund.participation.options.address, s.wantedShares) .send({ from: s.investor, gas: s.gas }); yield s.fund.participation.methods .requestInvestment(`${s.wantedShares}`, `${s.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 postTotalSupply = yield s.fund.shares.methods.totalSupply().call(); expect(postTotalSupply).toEqual(token_math_1.add(token_math_1.toBI(preTotalSupply), token_math_1.toBI(s.wantedShares))); })); test(`artificially inflate share price by inflating weth`, () => __awaiter(this, void 0, void 0, function* () { 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.weth.methods .transfer(s.fund.vault.options.address, `${s.wantedShares}`) .send({ from: s.deployer }); const postTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call()); const postFundCalculations = yield s.fund.accounting.methods .performCalculations() .call(); const feeInDenominationAsset = token_math_1.divide(token_math_1.multiply(token_math_1.toBI(postFundCalculations.feesInShares), token_math_1.toBI(postFundCalculations.gav)), token_math_1.add(postTotalSupply, token_math_1.toBI(postFundCalculations.feesInShares))); const sharePriceUsingNav = token_math_1.divide(token_math_1.multiply(new token_math_1.BigInteger(postFundCalculations.nav), precisionUnits), postTotalSupply); const sharePriceUsingGav = token_math_1.divide(token_math_1.multiply(token_math_1.subtract(new token_math_1.BigInteger(postFundCalculations.gav), feeInDenominationAsset), precisionUnits), postTotalSupply); expect(postTotalSupply).toEqual(preTotalSupply); expect(Number(postFundCalculations.sharePrice)).toBeGreaterThan(Number(preFundCalculations.sharePrice)); expect(new token_math_1.BigInteger(postFundCalculations.sharePrice)).toEqual(sharePriceUsingGav); expect(token_math_1.toBI(postFundCalculations.sharePrice).toString()).toEqual(sharePriceUsingNav.toString()); })); test(`performance fee is calculated correctly`, () => __awaiter(this, void 0, void 0, function* () { const lastHWM = yield s.performanceFee.methods .highWaterMark(s.fund.feeManager.options.address) .call(); const currentTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call()); const fundCalculations = yield s.fund.accounting.methods .performCalculations() .call(); const gavPerShare = token_math_1.divide(token_math_1.multiply(token_math_1.toBI(fundCalculations.gav), precisionUnits), currentTotalSupply); const gainInSharePrice = token_math_1.subtract(gavPerShare, token_math_1.toBI(lastHWM)); const expectedPerformanceFee = token_math_1.divide(token_math_1.multiply(token_math_1.divide(token_math_1.multiply(gainInSharePrice, token_math_1.toBI(s.performanceFeeRate)), precisionUnits), currentTotalSupply), precisionUnits); const expectedFeeSharesPreDilution = token_math_1.divide(token_math_1.multiply(currentTotalSupply, expectedPerformanceFee), token_math_1.toBI(fundCalculations.gav)); const expectedFeeShares = token_math_1.divide(token_math_1.multiply(currentTotalSupply, expectedFeeSharesPreDilution), token_math_1.subtract(currentTotalSupply, expectedFeeSharesPreDilution)); expect(new token_math_1.BigInteger(fundCalculations.feesInShares)).toEqual(expectedFeeShares); expect(Number(fundCalculations.feesInDenominationAsset)).toBeCloseTo(Number(expectedPerformanceFee)); })); test(`investor redeems half his shares, performance fee deducted`, () => __awaiter(this, void 0, void 0, function* () { const currentTotalSupply = new token_math_1.BigInteger(yield s.fund.shares.methods.totalSupply().call()); const preManagerShares = new token_math_1.BigInteger(yield s.fund.shares.methods.balanceOf(s.manager).call()); const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); const fundCalculations = yield s.fund.accounting.methods .performCalculations() .call(); const redeemingQuantity = token_math_1.divide(s.wantedShares, new token_math_1.BigInteger(2)); yield s.fund.participation.methods .redeemQuantity(`${redeemingQuantity}`) .send({ from: s.investor, gas: s.gas }); const postManagerShares = new token_math_1.BigInteger(yield s.fund.shares.methods.balanceOf(s.manager).call()); const redeemSharesProportion = token_math_1.divide(token_math_1.multiply(redeemingQuantity, precisionUnits), currentTotalSupply); const redeemSharesProportionAccountingInflation = token_math_1.divide(token_math_1.multiply(redeemingQuantity, precisionUnits), token_math_1.add(currentTotalSupply, token_math_1.toBI(fundCalculations.feesInShares))); const expectedOwedPerformanceFee = token_math_1.divide(token_math_1.multiply(redeemSharesProportionAccountingInflation, token_math_1.toBI(fundCalculations.feesInShares)), precisionUnits); expect(token_math_1.subtract(postManagerShares, preManagerShares).toString()).toEqual(expectedOwedPerformanceFee.toString()); yield s.fund.participation.methods .redeem() .send({ from: s.manager, gas: s.gas }); const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); expect(Number(token_math_1.subtract(post.manager.weth, pre.manager.weth))).toBeCloseTo(Number(token_math_1.divide(token_math_1.multiply(token_math_1.toBI(fundCalculations.feesInDenominationAsset), redeemSharesProportion), precisionUnits))); })); test(`manager calls rewardAllFees to update high watermark`, () => __awaiter(this, void 0, void 0, function* () { yield increaseTime_1.increaseTime(s.environment, Number(s.performanceFeePeriod)); const preManagerShares = new token_math_1.BigInteger(yield s.fund.shares.methods.balanceOf(s.manager).call()); const preFundCalculations = yield s.fund.accounting.methods .performCalculations() .call(); yield s.fund.accounting.methods .triggerRewardAllFees() .send({ from: s.manager, gas: s.gas }); const currentHWM = yield s.performanceFee.methods .highWaterMark(s.fund.feeManager.options.address) .call(); const postManagerShares = new token_math_1.BigInteger(yield s.fund.shares.methods.balanceOf(s.manager).call()); const postFundCalculations = yield s.fund.accounting.methods .performCalculations() .call(); expect(token_math_1.subtract(postManagerShares, preManagerShares)).toEqual(new token_math_1.BigInteger(preFundCalculations.feesInShares)); expect(postFundCalculations.sharePrice).toEqual(preFundCalculations.sharePrice); expect(currentHWM).toEqual(preFundCalculations.gavPerShareNetManagementFee); expect(postFundCalculations.gav).toEqual(preFundCalculations.gav); // expect(new BigInteger(fundCalculations.feesInDenominationAsset)).toEqual( // expectedPerformanceFee, // ); }));