"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mintOwl = exports.mintTokens = exports.createMintableToken = exports.getBatchId = exports.submitSolution = exports.deleteOrders = exports.addTokens = exports.setAllowances = exports.getOwl = exports.getBatchExchange = void 0;
const bn_js_1 = __importDefault(require("bn.js"));
const logging_1 = require("../src/logging");
const log = logging_1.factory.getLogger("scripts.util");
function getBatchExchange(artifacts) {
    return __awaiter(this, void 0, void 0, function* () {
        const BatchExchange = artifacts.require("BatchExchange");
        return BatchExchange.deployed();
    });
}
exports.getBatchExchange = getBatchExchange;
function getOwl(artifacts) {
    return __awaiter(this, void 0, void 0, function* () {
        const TokenOWL = artifacts.require("TokenOWL");
        const batchExchange = yield getBatchExchange(artifacts);
        const owlAddress = yield batchExchange.feeToken();
        return TokenOWL.at(owlAddress);
    });
}
exports.getOwl = getOwl;
function setAllowance(token, account, amount, exchange) {
    return __awaiter(this, void 0, void 0, function* () {
        log.info(`Approving BatchExchange at ${exchange.address} for ${amount} token ${token.address}`);
        yield token.approve(exchange.address, amount, {
            from: account,
        });
    });
}
function setAllowances(users, amount, exchange, tokens) {
    return __awaiter(this, void 0, void 0, function* () {
        for (let i = 0; i < users.length; i++) {
            for (let j = 0; j < tokens.length; j++) {
                yield setAllowance(tokens[j], users[i], amount, exchange);
            }
        }
    });
}
exports.setAllowances = setAllowances;
function addTokens(tokenAddresses, account, exchange, owl) {
    return __awaiter(this, void 0, void 0, function* () {
        // Get amount of required OWL for listing all tokens
        const feeForAddingToken = yield exchange.FEE_FOR_LISTING_TOKEN_IN_OWL();
        const totalFees = feeForAddingToken.mul(new bn_js_1.default(tokenAddresses.length));
        // Ensure the user has enough OWL balance
        const balanceOfOWL = yield owl.balanceOf(account);
        if (totalFees.gt(balanceOfOWL)) {
            throw new Error("Insufficient balance of fee token to complete request.");
        }
        // Set OWL allowance if necessary
        const allowanceOfOWL = yield owl.allowance(account, exchange.address);
        if (totalFees.gt(allowanceOfOWL)) {
            // TODO: Only approve the minimum required amount totalFees.sub(allowanceOfOWL)
            yield setAllowance(owl, account, totalFees, exchange);
        }
        // List all tokens (if not listed previously)
        const tokens = [];
        for (const tokenAddress of tokenAddresses) {
            const isTokenListed = yield exchange.hasToken(tokenAddress);
            if (!isTokenListed) {
                yield exchange.addToken(tokenAddress, { from: account });
                log.info(`Successfully added token ${tokenAddress}`);
            }
            else {
                log.info(`The token ${tokenAddress} was already added`);
            }
            // Get token information
            const tokenId = yield exchange.tokenAddressToIdMap(tokenAddress);
            tokens.push({
                id: tokenId.toNumber(),
                address: tokenAddress,
            });
        }
        // Return token information
        return tokens;
    });
}
exports.addTokens = addTokens;
function deleteOrders(orderIds, accounts, exchange) {
    return __awaiter(this, void 0, void 0, function* () {
        log.info(`Canceling ${orderIds.length} orders for ${accounts.length} users`);
        for (let i = 0; i < orderIds.length; i++) {
            const orderId = orderIds[i];
            const account = accounts[i];
            const cancelReceipt = yield exchange.cancelOrders([orderId], {
                from: account,
            });
            const events = cancelReceipt.logs
                .map((log) => log.event)
                .join(", ");
            log.info(`Canceled/Deleted order ${orderId} for user {${account}}. Emitted events: ${events}`);
        }
    });
}
exports.deleteOrders = deleteOrders;
function submitSolution(name, batchId, solution, solverAddress, exchange) {
    return __awaiter(this, void 0, void 0, function* () {
        log.info(`Submit "${name}":
  - Objective value: ${solution.objectiveValue}
  - Touched orders: ${solution.touchedorderIds.join(", ")}
  - Volumes: ${solution.volumes.join(", ")}
  - Prices: ${solution.prices.join(", ")}
  - Token ids for prices: ${solution.tokenIdsForPrice.join(", ")}`);
        const objectiveValue = yield exchange.submitSolution(batchId, 1, solution.owners, solution.touchedorderIds, solution.volumes, solution.prices, solution.tokenIdsForPrice, { from: solverAddress });
        log.info(`Transaction for ${name}: ${objectiveValue.tx}`);
    });
}
exports.submitSolution = submitSolution;
function getBatchId(exchange) {
    return __awaiter(this, void 0, void 0, function* () {
        const batchId = yield exchange.getCurrentBatchId();
        return batchId.toNumber();
    });
}
exports.getBatchId = getBatchId;
function createMintableToken(artifacts) {
    return __awaiter(this, void 0, void 0, function* () {
        const ERC20Mintable = artifacts.require("ERC20Mintable");
        return ERC20Mintable.new();
    });
}
exports.createMintableToken = createMintableToken;
function mintToken(token, account, amount, minter) {
    return __awaiter(this, void 0, void 0, function* () {
        log.info(`Mint ${amount} of token ${token.address} for user ${account}. Using ${minter} as the minter`);
        yield token.mint(account, amount, { from: minter });
    });
}
function mintTokens(tokens, users, amount, minter) {
    return __awaiter(this, void 0, void 0, function* () {
        for (let i = 0; i < tokens.length; i++) {
            for (let j = 0; j < users.length; j++) {
                yield mintToken(tokens[i], users[j], amount, minter);
            }
        }
    });
}
exports.mintTokens = mintTokens;
function mintOwlForUser(owl, user, amount, minter) {
    return __awaiter(this, void 0, void 0, function* () {
        log.info(`Mint ${amount} OWL to ${user}`);
        return owl.mintOWL(user, amount, { from: minter });
    });
}
function mintOwl(owl, users, amount, minter) {
    return __awaiter(this, void 0, void 0, function* () {
        for (let i = 0; i < users.length; i++) {
            yield mintOwlForUser(owl, users[i], amount, minter);
        }
    });
}
exports.mintOwl = mintOwl;
