"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 });
const BatchExchange = artifacts.require("BatchExchange");
const logging_1 = require("../src/logging");
const log = logging_1.factory.getLogger("scripts.spread_orders");
const bn_js_1 = __importDefault(require("bn.js"));
const readline_1 = __importDefault(require("readline"));
const utilities_1 = require("../test/utilities");
const fee_token_liquidity_1 = require("../src/fee_token_liquidity");
const rl = readline_1.default.createInterface({
    input: process.stdin,
    output: process.stdout,
});
const promptUser = function (message) {
    return new Promise((resolve) => rl.question(message, (answer) => resolve(answer)));
};
const formatAmount = function (amount, token) {
    const exponent = token.decimals;
    if (exponent) {
        return new bn_js_1.default(10).pow(new bn_js_1.default(exponent)).muln(amount);
    }
    throw new Error("Insufficient token data for amount formatting");
};
const argv = require("yargs")
    .option("tokens", {
    alias: "t",
    type: "string",
    describe: "Collection of trusted tokenIds",
    coerce: (str) => {
        return str.split(",").map((t) => parseInt(t));
    },
})
    .option("accountId", {
    describe: "Account index of the order placer",
    type: "int",
    default: 0,
})
    .option("spread", {
    type: "float",
    describe: "Percentage increase required for trade (fees not accounted)",
    default: 0.25,
})
    .option("sellAmount", {
    type: "int",
    describe: "Maximum sell amount (in full token units)",
    default: 1000,
})
    .option("validFrom", {
    type: "int",
    describe: "Number of batches (from current) until order become valid",
    default: 3,
})
    .option("expiry", {
    type: "int",
    describe: "Maximum auction batch for which these orders are valid",
    default: Math.pow(2, 32) - 1,
})
    .demand(["tokens"])
    .help("Make sure that you have an RPC connection to the network in consideration. For network configurations, please see truffle-config.js Example usage \n   npx truffle exec scripts/place_spread_orders.js --tokens=2,3,4 --accountId 0 --spread 0.3 --validFrom 5")
    .version(false).argv;
module.exports = (callback) => __awaiter(void 0, void 0, void 0, function* () {
    try {
        const instance = yield BatchExchange.deployed();
        const accounts = yield web3.eth.getAccounts();
        const account = accounts[argv.accountId];
        const batch_index = (yield instance.getCurrentBatchId()).toNumber();
        log.info("Communicating with exchange for requested token info...");
        const token_data = yield fee_token_liquidity_1.fetchTokenInfoFromExchange(instance, argv.tokens, artifacts);
        argv.tokens.map((id) => {
            const token = token_data.get(id);
            log.info(`Found ${token === null || token === void 0 ? void 0 : token.symbol} with ID ${id} at ${token === null || token === void 0 ? void 0 : token.address} having ${token === null || token === void 0 ? void 0 : token.decimals} decimals`);
        });
        const expectedReturnFactor = 1 + argv.spread / 100;
        const sellAmount = argv.sellAmount;
        const buyAmount = sellAmount * expectedReturnFactor;
        let buyTokens = [];
        let sellTokens = [];
        let buyAmounts = [];
        let sellAmounts = [];
        for (let i = 0; i < argv.tokens.length - 1; i++) {
            const tokenA = token_data.get(argv.tokens[i]);
            for (let j = i + 1; j < argv.tokens.length; j++) {
                const tokenB = token_data.get(argv.tokens[j]);
                if (tokenA && tokenB) {
                    buyTokens = buyTokens.concat(tokenA.id, tokenB.id);
                    sellTokens = sellTokens.concat(tokenB.id, tokenA.id);
                    buyAmounts = buyAmounts.concat(formatAmount(buyAmount, tokenA), formatAmount(buyAmount, tokenB));
                    sellAmounts = sellAmounts.concat(formatAmount(sellAmount, tokenB), formatAmount(sellAmount, tokenA));
                    log.info(`Sell ${sellAmounts.slice(-2)[0]} ${tokenB.symbol} for ${buyAmounts.slice(-2)[0]} ${tokenA.symbol}`);
                    log.info(`Sell ${sellAmounts.slice(-2)[1]} ${tokenA.symbol} for ${buyAmounts.slice(-2)[1]} ${tokenB.symbol}`);
                }
            }
        }
        const validFroms = Array(buyTokens.length).fill(batch_index + argv.validFrom);
        const validTos = Array(buyTokens.length).fill(argv.expiry);
        const answer = yield promptUser("Are you sure you want to send this transaction to the EVM? [yN] ");
        if (answer.toLowerCase() == "y" || answer.toLowerCase() == "yes") {
            const ids = yield utilities_1.sendTxAndGetReturnValue(instance.placeValidFromOrders, buyTokens, sellTokens, validFroms, validTos, buyAmounts, sellAmounts, {
                from: account,
            });
            log.info(`Successfully placed spread orders with IDs ${ids}.\n`);
        }
        callback();
    }
    catch (error) {
        callback(error);
    }
});
