"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 token_math_1 = require("@melonproject/token-math");
const initTestEnvironment_1 = require("../../../tests/utils/initTestEnvironment");
const getToken_1 = require("../../dependencies/token/calls/getToken");
const deploy_1 = require("../../dependencies/token/transactions/deploy");
const approve_1 = require("../../dependencies/token/transactions/approve");
const deployTestingPriceFeed_1 = require("../../prices/transactions/deployTestingPriceFeed");
const update_1 = require("../../prices/transactions/update");
const getContract_1 = require("../../../utils/solidity/getContract");
const deployContract_1 = require("../../../utils/solidity/deployContract");
const Contracts_1 = require("../../../Contracts");
const evm_1 = require("../../../utils/evm");
const thaw_1 = require("./thaw");
const sellAndBurnMln_1 = require("./sellAndBurnMln");
const deployEngine_1 = require("./deployEngine");
describe('sellAndBurnMln', () => {
    const shared = {};
    beforeAll(() => __awaiter(this, void 0, void 0, function* () {
        shared.env = yield initTestEnvironment_1.initTestEnvironment();
        shared.accounts = yield shared.env.eth.getAccounts();
        const wethAddress = yield deploy_1.deployToken(shared.env, 'ETH');
        shared.mln = getContract_1.getContract(shared.env, Contracts_1.Contracts.BurnableToken, yield deployContract_1.deployContract(shared.env, Contracts_1.Contracts.BurnableToken, [
            'MLN',
            18,
            '',
        ]));
        shared.weth = yield getContract_1.getContract(shared.env, Contracts_1.Contracts.StandardToken, wethAddress);
        shared.version = getContract_1.getContract(shared.env, Contracts_1.Contracts.MockVersion, yield deployContract_1.deployContract(shared.env, Contracts_1.Contracts.MockVersion));
        shared.registry = getContract_1.getContract(shared.env, Contracts_1.Contracts.MockRegistry, yield deployContract_1.deployContract(shared.env, Contracts_1.Contracts.MockRegistry));
        const feedAddress = yield deployTestingPriceFeed_1.deployTestingPriceFeed(shared.env, yield getToken_1.getToken(shared.env, wethAddress));
        shared.feed = yield getContract_1.getContract(shared.env, Contracts_1.Contracts.TestingPriceFeed, feedAddress);
        shared.delay = 30 * 24 * 60 * 60;
        shared.engineAddress = yield deployEngine_1.deployEngine(shared.env, {
            delay: shared.delay,
        });
        shared.priceSource = yield getContract_1.getContract(shared.env, Contracts_1.Contracts.TestingPriceFeed, feedAddress);
        shared.engine = getContract_1.getContract(shared.env, Contracts_1.Contracts.Engine, shared.engineAddress);
        yield shared.registry.methods
            .setPriceSource(shared.priceSource.options.address)
            .send({ from: shared.accounts[0] });
        yield shared.registry.methods
            .setMlnToken(shared.mln.options.address)
            .send({ from: shared.accounts[0] });
        yield shared.engine.methods
            .setRegistry(shared.registry.options.address)
            .send({ from: shared.accounts[0], gas: 8000000 });
        yield update_1.update(shared.env, feedAddress, [
            token_math_1.createPrice(token_math_1.createQuantity(yield getToken_1.getToken(shared.env, wethAddress), 1), token_math_1.createQuantity(yield getToken_1.getToken(shared.env, wethAddress), 1)),
            token_math_1.createPrice(token_math_1.createQuantity(yield getToken_1.getToken(shared.env, shared.mln.options.address), 1), token_math_1.createQuantity(yield getToken_1.getToken(shared.env, wethAddress), 2.94)),
        ]);
    }));
    it('directly sending eth fails', () => __awaiter(this, void 0, void 0, function* () {
        yield expect(shared.env.eth.sendTransaction({
            from: shared.env.wallet.address,
            to: shared.engine.options.address,
            value: 1,
        })).rejects.toThrow('revert');
    }));
    it('eth sent via contract selfdestruct is not tracked', () => __awaiter(this, void 0, void 0, function* () {
        const sendEth = new token_math_1.BigInteger('100000000');
        const destructing = getContract_1.getContract(shared.env, Contracts_1.Contracts.SelfDestructing, yield deployContract_1.deployContract(shared.env, Contracts_1.Contracts.SelfDestructing));
        const preHeldEth = yield shared.env.eth.getBalance(shared.engine.options.address);
        expect(Number(preHeldEth)).toBe(0);
        yield shared.env.eth.sendTransaction({
            from: shared.env.wallet.address,
            to: destructing.options.address,
            value: Number(sendEth),
        });
        yield destructing.methods
            .bequeath(shared.engine.options.address)
            .send({ from: shared.env.wallet.address });
        const postHeldEth = yield shared.env.eth.getBalance(shared.engine.options.address);
        const frozenEth = yield shared.engine.methods.frozenEther().call();
        const liquidEth = yield shared.engine.methods.liquidEther().call();
        expect(token_math_1.isEqual(new token_math_1.BigInteger(postHeldEth), sendEth));
        expect(Number(frozenEth)).toBe(0);
        expect(Number(liquidEth)).toBe(0);
    }));
    it('AMGU payment fails when sender not fund', () => __awaiter(this, void 0, void 0, function* () {
        const sender = shared.env.wallet.address;
        const isFund = yield shared.registry.methods.isFund(sender).call();
        expect(isFund).toBe(false);
        yield expect(shared.engine.methods
            .payAmguInEther()
            .send({ from: sender, value: 1000000 })).rejects.toThrow('revert');
    }));
    it('eth sent as AMGU from a "fund" thaws and can be bought', () => __awaiter(this, void 0, void 0, function* () {
        const sender = shared.env.wallet.address;
        const sendEth = new token_math_1.BigInteger('100000');
        yield shared.registry.methods.setIsFund(sender).send({ from: sender });
        const isFund = yield shared.registry.methods.isFund(sender).call();
        expect(isFund).toBe(true);
        yield shared.engine.methods
            .payAmguInEther()
            .send({ from: sender, value: Number(sendEth) });
        const frozenEth = yield shared.engine.methods.frozenEther().call();
        const liquidEth = yield shared.engine.methods.liquidEther().call();
        expect(token_math_1.isEqual(new token_math_1.BigInteger(frozenEth), sendEth));
        expect(Number(liquidEth)).toBe(0);
        yield expect(
        // early call to thaw fails
        shared.engine.methods.thaw().send({ from: shared.accounts[1] })).rejects.toThrow('revert');
        const enginePrice = yield shared.engine.methods.enginePrice().call();
        const premiumPercent = new token_math_1.BigInteger(yield shared.engine.methods.premiumPercent().call());
        const ethPerMln = new token_math_1.BigInteger((yield shared.feed.methods
            .getPrice(shared.mln.options.address)
            .call()).price);
        const premiumPrice = token_math_1.add(ethPerMln, token_math_1.divide(token_math_1.multiply(ethPerMln, premiumPercent), new token_math_1.BigInteger(100)));
        expect(`${enginePrice}`).toBe(`${premiumPrice}`);
        const sendMln = token_math_1.createQuantity(yield getToken_1.getToken(shared.env, shared.mln.options.address), token_math_1.divide(token_math_1.multiply(sendEth, new token_math_1.BigInteger('1000000000000000000')), premiumPrice));
        yield approve_1.approve(shared.env, {
            howMuch: sendMln,
            spender: shared.engine.options.address,
        });
        yield expect(
        // throws when trying to burn without liquid ETH
        sellAndBurnMln_1.sellAndBurnMln(shared.env, shared.engine.options.address, {
            quantity: sendMln,
        })).rejects.toThrow('revert');
        yield evm_1.increaseTime(shared.env, shared.delay);
        yield thaw_1.thaw(shared.env, shared.engine.options.address);
        const frozenEthPost = yield shared.engine.methods.frozenEther().call();
        const liquidEthPost = yield shared.engine.methods.liquidEther().call();
        expect(Number(frozenEthPost)).toBe(0);
        expect(token_math_1.isEqual(new token_math_1.BigInteger(liquidEthPost), sendEth));
        const burnerPreMln = yield shared.mln.methods.balanceOf(sender).call();
        const burnerPreEth = yield shared.env.eth.getBalance(sender);
        const enginePreEth = yield shared.env.eth.getBalance(shared.engine.options.address);
        const preMlnTotalSupply = yield shared.mln.methods.totalSupply().call();
        const ethPurchased = yield shared.engine.methods
            .ethPayoutForMlnAmount(`${sendMln.quantity}`)
            .call();
        const receipt = yield sellAndBurnMln_1.sellAndBurnMln(shared.env, shared.engine.options.address, {
            quantity: sendMln,
        });
        const gasUsed = receipt.gasUsed;
        const burnerPostMln = yield shared.mln.methods.balanceOf(sender).call();
        const burnerPostEth = yield shared.env.eth.getBalance(sender);
        const enginePostMln = yield shared.mln.methods
            .balanceOf(shared.engine.options.address)
            .call();
        const enginePostEth = yield shared.env.eth.getBalance(shared.engine.options.address);
        const postMlnTotalSupply = yield shared.mln.methods.totalSupply().call();
        expect(burnerPostMln).toBe(`${token_math_1.subtract(token_math_1.toBI(burnerPreMln), sendMln.quantity)}`);
        expect(burnerPostEth).toBe(`${token_math_1.subtract(token_math_1.add(token_math_1.toBI(burnerPreEth), token_math_1.toBI(ethPurchased)), token_math_1.multiply(token_math_1.toBI(gasUsed), token_math_1.toBI(shared.env.options.gasPrice)))}`);
        expect(enginePostMln).toBe(enginePostMln);
        expect(`${enginePostMln}`).toBe('0');
        expect(enginePostEth).toBe(`${token_math_1.subtract(token_math_1.toBI(enginePreEth), token_math_1.toBI(ethPurchased))}`);
        expect(postMlnTotalSupply).toBe(`${token_math_1.subtract(token_math_1.toBI(preMlnTotalSupply), sendMln.quantity)}`);
    }));
    it('Other contracts can pay amgu on function calls', () => __awaiter(this, void 0, void 0, function* () { }));
    it('Engine price and premium computes at multiple values', () => __awaiter(this, void 0, void 0, function* () { }));
});
