"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.placeOrders = exports.makeDeposits = exports.setupGenericStableX = exports.applyBalances = exports.sendTxAndGetReturnValue = exports.closeAuction = exports.waitForNSeconds = void 0;
const jsonrpc = "2.0";
const id = 0;
const send = function (method, params, web3Provider) {
    return new Promise(function (resolve, reject) {
        web3Provider.currentProvider.send({ id, jsonrpc, method, params }, (error, result) => {
            if (error) {
                reject(error);
            }
            else {
                resolve(result);
            }
        });
    });
};
// Wait for n blocks to pass
/**
 * Wait for n (evm) seconds to pass
 * @param seconds - time to wait
 * @param web3Provider - potentially different in contract tests and system end-to-end testing.
 */
function waitForNSeconds(seconds, web3Provider = web3) {
    return __awaiter(this, void 0, void 0, function* () {
        yield send("evm_increaseTime", [seconds], web3Provider);
        yield send("evm_mine", [], web3Provider);
    });
}
exports.waitForNSeconds = waitForNSeconds;
function closeAuction(instance, web3Provider = web3) {
    return __awaiter(this, void 0, void 0, function* () {
        const time_remaining = (yield instance.getSecondsRemainingInBatch()).toNumber();
        yield waitForNSeconds(time_remaining + 1, web3Provider);
    });
}
exports.closeAuction = closeAuction;
function sendTxAndGetReturnValue(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
method, 
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...args) {
    return __awaiter(this, void 0, void 0, function* () {
        const result = yield method.call(...args);
        yield method.sendTransaction(...args);
        return result;
    });
}
exports.sendTxAndGetReturnValue = sendTxAndGetReturnValue;
/**
 * Finalizes user's pending deposits by updating user's balances for all input tokens.
 * It assumes no withdrawals have been requested.
 * State of the contract after the execution of this function for tokenAddress:
 * ```
 * balanceState[userAddress][tokenAddress] = {
 *   balance: pendingDepositAmount,
 *   pendingDeposits: null,
 *   pendingWithdraws: null,
 * }
 * ```
 * @param userAddress - address of the user who deposited
 * @param epochTokenLocker - instance of the epoch token locker to which the user deposited
 * @param tokenAddresses - list of token addresses for which a deposit is pending
 */
function applyBalances(userAddress, epochTokenLocker, tokenAddresses) {
    return __awaiter(this, void 0, void 0, function* () {
        yield closeAuction(epochTokenLocker);
        for (const tokenAddress of tokenAddresses) {
            yield epochTokenLocker.withdraw(userAddress, tokenAddress);
        }
    });
}
exports.applyBalances = applyBalances;
/**
 * Makes deposit transactions from a list of Deposit Objects
 * @param numTokens - number of tokens to be registered on this exchange.
 * @param maxTokens - Maximum number of tokens (a contract contructor parameter)
 */
exports.setupGenericStableX = function (numTokens = 2, maxTokens = Math.pow(2, 16) - 1) {
    return __awaiter(this, void 0, void 0, function* () {
        const MockContract = artifacts.require("MockContract");
        const BatchExchange = artifacts.require("BatchExchange");
        const feeToken = yield MockContract.new();
        yield feeToken.givenAnyReturnBool(true);
        const instance = yield BatchExchange.new(maxTokens, feeToken.address);
        const tokens = [feeToken];
        for (let i = 0; i < numTokens - 1; i++) {
            const token = yield MockContract.new();
            yield instance.addToken(token.address);
            yield token.givenAnyReturnBool(true);
            tokens.push(token);
        }
        return instance;
    });
};
/**
 * Makes deposit transactions from a list of Deposit Objects
 * @param contract - BatchExchange smart contract
 * @param accounts - An array of (unlocked) ethereum account addresses
 * @param depositList - Array of Deposit Objects
 * @param sufficiencyFactor - Factor of deposit amount to be deposited (default: 1)
 */
function makeDeposits(contract, accounts, depositList, sufficiencyFactor = 1) {
    return __awaiter(this, void 0, void 0, function* () {
        for (const deposit of depositList) {
            const tokenAddress = yield contract.tokenIdToAddressMap(deposit.token);
            yield contract.deposit(tokenAddress, deposit.amount.muln(sufficiencyFactor), { from: accounts[deposit.user] });
        }
    });
}
exports.makeDeposits = makeDeposits;
/**
 * Makes placeOrder transactions from a list of Order Objects
 * @param contract - BatchExchange smart contract
 * @param accounts - An array of (unlocked) ethereum account addresses
 * @param orderList - an array of Order Objects
 * @param auctionIndex - the auction in which the order should be placed
 */
function placeOrders(contract, accounts, orderList, auctionIndex) {
    return __awaiter(this, void 0, void 0, function* () {
        const orderIds = [];
        for (const order of orderList) {
            orderIds.push((yield sendTxAndGetReturnValue(contract.placeOrder, order.buyToken, order.sellToken, auctionIndex, order.buyAmount, order.sellAmount, { from: accounts[order.user] })).toNumber());
        }
        return orderIds;
    });
}
exports.placeOrders = placeOrders;
