"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 order_utils_1 = require("@0x/order-utils");
const token_math_1 = require("@melonproject/token-math");
const orderSignatures_1 = require("../../utils/constants/orderSignatures");
const createOrder_1 = require("../../contracts/exchanges/third-party/0x/utils/createOrder");
const signOrder_1 = require("../../contracts/exchanges/third-party/0x/utils/signOrder");
const getAssetProxy_1 = require("../../contracts/exchanges/third-party/0x/calls/getAssetProxy");
const updateTestingPriceFeed_1 = require("../utils/updateTestingPriceFeed");
const getAllBalances_1 = require("../utils/getAllBalances");
const initTestEnvironment_1 = require("../utils/initTestEnvironment");
const deployAndGetSystem_1 = require("../utils/deployAndGetSystem");
const getToken_1 = require("../../contracts/dependencies/token/calls/getToken");
const beginSetup_1 = require("../../contracts/factory/transactions/beginSetup");
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 randomHexOfSize_1 = require("../../utils/helpers/randomHexOfSize");
const Contracts_1 = require("../../Contracts");
const withDifferentAccount_1 = require("../../utils/environment/withDifferentAccount");
const increaseTime_1 = require("../../utils/evm/increaseTime");
const fillOrder_1 = require("../../contracts/exchanges/third-party/0x/transactions/fillOrder");
// mock data
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
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.exchanges = [s.matchingMarket]; // , matchingMarket2];
    s.gas = 8000000;
    s.opts = { from: s.deployer, gas: s.gas };
    s.numberofExchanges = 1;
    s.exchanges = [s.matchingMarket];
    s.erc20ProxyAddress = yield getAssetProxy_1.getAssetProxy(s.environment, s.zeroExExchange.options.address);
    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 = {
        [Contracts_1.Exchanges.ZeroEx]: {
            adapter: s.zeroExAdapter.options.address,
            exchange: s.zeroExExchange.options.address,
            takesCustody: false,
        },
    };
    const envManager = withDifferentAccount_1.withDifferentAccount(s.environment, s.manager);
    yield beginSetup_1.beginSetup(envManager, s.version.options.address, {
        defaultTokens: [s.wethTokenInterface, s.mlnTokenInterface],
        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);
}));
const initialTokenAmount = new token_math_1.BigInteger(Math.pow(10, 19));
test('investor gets initial ethToken for testing)', () => __awaiter(this, void 0, void 0, function* () {
    const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    yield s.weth.methods
        .transfer(s.investor, `${initialTokenAmount}`)
        .send(s.opts);
    const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    expect(post.investor.weth).toEqual(token_math_1.add(pre.investor.weth, initialTokenAmount));
}));
// tslint:disable-next-line:max-line-length
test('fund receives ETH from investment, and gets ZRX from direct transfer', () => __awaiter(this, void 0, void 0, function* () {
    const offeredValue = new token_math_1.BigInteger(Math.pow(10, 18));
    const wantedShares = new token_math_1.BigInteger(Math.pow(10, 18));
    const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    yield s.weth.methods
        .approve(s.fund.participation.options.address, `${offeredValue}`)
        .send({ from: s.investor, gas: s.gas });
    yield s.fund.participation.methods
        .requestInvestment(`${offeredValue}`, `${wantedShares}`, s.weth.options.address)
        .send({ from: s.investor, gas: s.gas, value: '10000000000000000' });
    yield s.fund.participation.methods
        .executeRequestFor(s.investor)
        .send({ from: s.investor, gas: s.gas });
    yield s.zrx.methods
        .transfer(s.fund.vault.options.address, `${initialTokenAmount}`)
        .send({ from: s.deployer, gas: s.gas });
    const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    expect(post.investor.weth).toEqual(token_math_1.subtract(pre.investor.weth, offeredValue));
    expect(post.fund.weth).toEqual(token_math_1.add(pre.fund.weth, offeredValue));
}));
test('third party makes and validates an off-chain order', () => __awaiter(this, void 0, void 0, function* () {
    const makerAddress = s.deployer.toLowerCase();
    const makerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 1);
    const takerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05);
    const unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, {
        makerAddress,
        makerQuantity,
        takerQuantity,
    });
    yield createOrder_1.approveOrder(s.environment, s.zeroExExchange.options.address, unsignedOrder);
    s.signedOrder = yield signOrder_1.signOrder(s.environment, unsignedOrder);
    const signatureValid = yield createOrder_1.isValidSignatureOffChain(s.environment, unsignedOrder, s.signedOrder.signature);
    expect(signatureValid).toBeTruthy();
}));
// tslint:disable-next-line:max-line-length
test('manager takes order (half the total quantity) through 0x adapter', () => __awaiter(this, void 0, void 0, function* () {
    const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const fillQuantity = s.signedOrder.takerAssetAmount;
    yield s.fund.trading.methods
        .callOnExchange(0, orderSignatures_1.takeOrderSignature, [
        s.deployer,
        NULL_ADDRESS,
        s.mln.options.address,
        s.weth.options.address,
        s.signedOrder.feeRecipientAddress,
        NULL_ADDRESS,
    ], [
        s.signedOrder.makerAssetAmount.toFixed(),
        s.signedOrder.takerAssetAmount.toFixed(),
        s.signedOrder.makerFee.toFixed(),
        s.signedOrder.takerFee.toFixed(),
        s.signedOrder.expirationTimeSeconds.toFixed(),
        s.signedOrder.salt.toFixed(),
        `${fillQuantity}`,
        0,
    ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature)
        .send({ from: s.manager, gas: s.gas });
    const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const heldInExchange = yield s.fund.trading.methods
        .updateAndGetQuantityHeldInExchange(s.weth.options.address)
        .call();
    expect(heldInExchange).toBe('0');
    expect(post.deployer.mln).toEqual(token_math_1.subtract(pre.deployer.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount)));
    expect(post.fund.weth).toEqual(token_math_1.subtract(pre.fund.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount)));
    expect(post.fund.mln).toEqual(token_math_1.add(pre.fund.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount)));
    expect(post.deployer.weth).toEqual(token_math_1.add(pre.deployer.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount)));
}));
test('third party makes and validates an off-chain order', () => __awaiter(this, void 0, void 0, function* () {
    const makerAddress = s.deployer.toLowerCase();
    const makerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 1);
    const takerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05);
    const takerFee = new token_math_1.BigInteger(Math.pow(10, 14));
    const unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, {
        feeRecipientAddress: s.investor,
        makerAddress,
        makerQuantity,
        takerFee,
        takerQuantity,
    });
    yield createOrder_1.approveOrder(s.environment, s.zeroExExchange.options.address, unsignedOrder);
    s.signedOrder = yield signOrder_1.signOrder(s.environment, unsignedOrder);
    const signatureValid = yield createOrder_1.isValidSignatureOffChain(s.environment, unsignedOrder, s.signedOrder.signature);
    expect(signatureValid).toBeTruthy();
}));
test('fund with enough ZRX takes the above order', () => __awaiter(this, void 0, void 0, function* () {
    const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const preFundZrx = new token_math_1.BigInteger(yield s.zrx.methods.balanceOf(s.fund.vault.options.address).call());
    const fillQuantity = s.signedOrder.takerAssetAmount;
    yield s.fund.trading.methods
        .callOnExchange(0, orderSignatures_1.takeOrderSignature, [
        s.deployer,
        NULL_ADDRESS,
        s.mln.options.address,
        s.weth.options.address,
        s.signedOrder.feeRecipientAddress,
        NULL_ADDRESS,
    ], [
        s.signedOrder.makerAssetAmount.toFixed(),
        s.signedOrder.takerAssetAmount.toFixed(),
        s.signedOrder.makerFee.toFixed(),
        s.signedOrder.takerFee.toFixed(),
        s.signedOrder.expirationTimeSeconds.toFixed(),
        s.signedOrder.salt.toFixed(),
        `${fillQuantity}`,
        0,
    ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature)
        .send({ from: s.manager, gas: s.gas });
    const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const postFundZrx = new token_math_1.BigInteger(yield s.zrx.methods.balanceOf(s.fund.vault.options.address).call());
    const heldInExchange = yield s.fund.trading.methods
        .updateAndGetQuantityHeldInExchange(s.weth.options.address)
        .call();
    expect(heldInExchange).toBe('0');
    expect(postFundZrx).toEqual(token_math_1.subtract(preFundZrx, token_math_1.toBI(s.signedOrder.takerFee)));
    expect(post.deployer.mln).toEqual(token_math_1.subtract(pre.deployer.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount)));
    expect(post.fund.weth).toEqual(token_math_1.subtract(pre.fund.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount)));
    expect(post.fund.mln).toEqual(token_math_1.add(pre.fund.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount)));
    expect(post.deployer.weth).toEqual(token_math_1.add(pre.deployer.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount)));
}));
test('Make order through the fund', () => __awaiter(this, void 0, void 0, function* () {
    const makerAddress = s.fund.trading.options.address.toLowerCase();
    const makerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 0.5);
    const takerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05);
    const unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, {
        makerAddress,
        makerQuantity,
        takerQuantity,
    });
    s.signedOrder = yield signOrder_1.signOrder(s.environment, unsignedOrder, s.manager);
    yield s.fund.trading.methods
        .callOnExchange(0, orderSignatures_1.makeOrderSignature, [
        makerAddress,
        NULL_ADDRESS,
        s.mln.options.address,
        s.weth.options.address,
        s.signedOrder.feeRecipientAddress,
        NULL_ADDRESS,
    ], [
        s.signedOrder.makerAssetAmount.toFixed(),
        s.signedOrder.takerAssetAmount.toFixed(),
        s.signedOrder.makerFee.toFixed(),
        s.signedOrder.takerFee.toFixed(),
        s.signedOrder.expirationTimeSeconds.toFixed(),
        s.signedOrder.salt.toFixed(),
        0,
        0,
    ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature)
        .send({ from: s.manager, gas: s.gas });
    const makerAssetAllowance = new token_math_1.BigInteger(yield s.mln.methods
        .allowance(s.fund.trading.options.address, s.erc20ProxyAddress.toLowerCase())
        .call());
    expect(makerAssetAllowance).toEqual(new token_math_1.BigInteger(s.signedOrder.makerAssetAmount));
}));
// test.serial(
//   'Fund cannot make multiple orders for same asset unless fulfilled',
//   async t => {
//     await t.throws(
//       fund.trading.methods
//         .callOnExchange(
//           0,
//           makeOrderSignature,
//           [
//             fund.trading.options.address.toLowerCase(),
//             NULL_ADDRESS,
//             mlnToken.options.address,
//             ethToken.options.address,
//             order.feeRecipientAddress,
//             NULL_ADDRESS,
//           ],
//           [
//             order.makerAssetAmount.toFixed(),
//             order.takerAssetAmount.toFixed(),
//             order.makerFee.toFixed(),
//             order.takerFee.toFixed(),
//             order.expirationTimeSeconds.toFixed(),
//             559,
//             0,
//             0,
//           ],
//           web3.utils.padLeft('0x0', 64),
//           order.makerAssetData,
//           order.takerAssetData,
//           orderSignature,
//         )
//         .send({ from: manager, gas: config.gas }),
//     );
//   },
// );
test('Third party takes the order made by the fund', () => __awaiter(this, void 0, void 0, function* () {
    const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    const result = yield fillOrder_1.fillOrder(s.environment, s.zeroExExchange.options.address, {
        signedOrder: s.signedOrder,
    });
    const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment);
    expect(result).toBeTruthy();
    expect(post.fund.weth).toEqual(token_math_1.add(pre.fund.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount)));
    expect(post.fund.mln).toEqual(token_math_1.subtract(pre.fund.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount)));
    expect(post.deployer.weth).toEqual(token_math_1.subtract(pre.deployer.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount)));
    expect(post.deployer.mln).toEqual(token_math_1.add(pre.deployer.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount)));
}));
// tslint:disable-next-line:max-line-length
test("Fund can make another make order for same asset (After it's inactive)", () => __awaiter(this, void 0, void 0, function* () {
    const makerAddress = s.fund.trading.options.address.toLowerCase();
    const makerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05);
    const takerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 0.5);
    s.unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, {
        feeRecipientAddress: s.investor,
        makerAddress,
        makerQuantity,
        takerQuantity,
    });
    s.signedOrder = yield signOrder_1.signOrder(s.environment, s.unsignedOrder, s.manager);
    yield s.fund.trading.methods
        .callOnExchange(0, orderSignatures_1.makeOrderSignature, [
        makerAddress,
        NULL_ADDRESS,
        s.weth.options.address,
        s.mln.options.address,
        s.signedOrder.feeRecipientAddress,
        NULL_ADDRESS,
    ], [
        s.signedOrder.makerAssetAmount.toFixed(),
        s.signedOrder.takerAssetAmount.toFixed(),
        s.signedOrder.makerFee.toFixed(),
        s.signedOrder.takerFee.toFixed(),
        s.signedOrder.expirationTimeSeconds.toFixed(),
        s.signedOrder.salt.toFixed(),
        0,
        0,
    ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature)
        .send({ from: s.manager, gas: s.gas });
    const makerAssetAllowance = new token_math_1.BigInteger(yield s.weth.methods
        .allowance(s.fund.trading.options.address, s.erc20ProxyAddress.toLowerCase())
        .call());
    expect(makerAssetAllowance).toEqual(new token_math_1.BigInteger(s.signedOrder.makerAssetAmount));
}));
test('Fund can cancel the order using just the orderId', () => __awaiter(this, void 0, void 0, function* () {
    const orderHashHex = order_utils_1.orderHashUtils.getOrderHashHex(s.unsignedOrder);
    yield s.fund.trading.methods
        .callOnExchange(0, orderSignatures_1.cancelOrderSignature, [
        NULL_ADDRESS,
        NULL_ADDRESS,
        NULL_ADDRESS,
        NULL_ADDRESS,
        NULL_ADDRESS,
        NULL_ADDRESS,
    ], [0, 0, 0, 0, 0, 0, 0, 0], orderHashHex, '0x0', '0x0', '0x0')
        .send({ from: s.manager, gas: s.gas });
    const isOrderCancelled = yield s.zeroExExchange.methods
        .cancelled(orderHashHex)
        .call();
    const makerAssetAllowance = new token_math_1.BigInteger(yield s.mln.methods
        .allowance(s.fund.trading.options.address, s.erc20ProxyAddress.toLowerCase())
        .call());
    expect(makerAssetAllowance).toEqual(new token_math_1.BigInteger(0));
    expect(isOrderCancelled).toBeTruthy();
}));
test('Expired order is removed from open maker order', () => __awaiter(this, void 0, void 0, function* () {
    const makerAddress = s.fund.trading.options.address.toLowerCase();
    const makerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05);
    const takerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 0.5);
    const duration = 50;
    s.unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, {
        duration,
        feeRecipientAddress: s.investor,
        makerAddress,
        makerQuantity,
        takerQuantity,
    });
    s.signedOrder = yield signOrder_1.signOrder(s.environment, s.unsignedOrder, s.manager);
    yield s.fund.trading.methods
        .callOnExchange(0, orderSignatures_1.makeOrderSignature, [
        makerAddress,
        NULL_ADDRESS,
        s.weth.options.address,
        s.mln.options.address,
        s.signedOrder.feeRecipientAddress,
        NULL_ADDRESS,
    ], [
        s.signedOrder.makerAssetAmount.toFixed(),
        s.signedOrder.takerAssetAmount.toFixed(),
        s.signedOrder.makerFee.toFixed(),
        s.signedOrder.takerFee.toFixed(),
        s.signedOrder.expirationTimeSeconds.toFixed(),
        s.signedOrder.salt.toFixed(),
        0,
        0,
    ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature)
        .send({ from: s.manager, gas: s.gas });
    const makerAssetAllowance = new token_math_1.BigInteger(yield s.weth.methods
        .allowance(s.fund.trading.options.address, s.erc20ProxyAddress.toLowerCase())
        .call());
    expect(makerAssetAllowance).toEqual(new token_math_1.BigInteger(s.signedOrder.makerAssetAmount));
    const orderHashHex = order_utils_1.orderHashUtils.getOrderHashHex(s.unsignedOrder);
    yield increaseTime_1.increaseTime(s.environment, duration);
    yield s.fund.trading.methods
        .callOnExchange(0, orderSignatures_1.cancelOrderSignature, [
        NULL_ADDRESS,
        NULL_ADDRESS,
        NULL_ADDRESS,
        NULL_ADDRESS,
        NULL_ADDRESS,
        NULL_ADDRESS,
    ], [0, 0, 0, 0, 0, 0, 0, 0], orderHashHex, '0x0', '0x0', '0x0')
        .send({ from: s.manager, gas: s.gas });
    yield s.fund.trading.methods
        .updateAndGetQuantityBeingTraded(s.weth.options.address)
        .send({ from: s.manager, gas: s.gas });
    const isInOpenMakeOrder = yield s.fund.trading.methods
        .isInOpenMakeOrder(s.weth.options.address)
        .call();
    expect(isInOpenMakeOrder).toBeFalsy();
}));
