"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.placeFeeTokenLiquidityOrders = exports.fetchTokenInfoFromExchange = void 0;
const bn_js_1 = __importDefault(require("bn.js"));
const MAXU32 = new bn_js_1.default(2).pow(new bn_js_1.default(32)).sub(new bn_js_1.default(1));
/**
 * Used to recover relevant {@link TokenInfo | TokenInfo} by ID from the exchange such as number of decimals or name.
 * @param exchange - An instance of deployed Batch Exchange Contract
 * @param tokenIds - An array of token ids as listed on the Batch Exchange.
 * @param artifacts - A context-like object providing a gateway to Truffle contract ABIs.
 * @returns A mapping of TokenInfo objects fetched from the exchange.
 *   Note that nullish TokenInfo objects are returned in places where the requested token ID failed to fetch.
 */
function fetchTokenInfoFromExchange(exchange, tokenIds, artifacts) {
    return __awaiter(this, void 0, void 0, function* () {
        const ERC20 = artifacts.require("ERC20Detailed");
        // Fetching token data from EVM
        const tokenObjects = new Map();
        yield Promise.all(tokenIds.map((id) => __awaiter(this, void 0, void 0, function* () {
            const tokenAddress = yield exchange.tokenIdToAddressMap(id);
            let tokenInfo;
            try {
                const tokenInstance = yield ERC20.at(tokenAddress);
                const [symbol, decimals] = yield Promise.all([
                    tokenInstance.symbol(),
                    tokenInstance.decimals(),
                ]);
                tokenInfo = {
                    id: id,
                    symbol: symbol,
                    decimals: decimals.toNumber(),
                    address: tokenAddress,
                };
            }
            catch (err) {
                // This generic try-catch is essentially a TokenNotFoundError
                // Could occur when the given ID slot is not occupied by a registered token on the exhchange
                // or if the code registered at address occupied by a token slot is not that of and ERC20 token
                tokenInfo = {
                    id: id,
                    address: tokenAddress,
                };
            }
            tokenObjects.set(id, tokenInfo);
        })));
        return tokenObjects;
    });
}
exports.fetchTokenInfoFromExchange = fetchTokenInfoFromExchange;
/**
 * A handy function providing fee token liquidity by placing orders selling the fee token
 * at the specified provision price for each specified token.
 * @param exchange - BatchExchange Smart Contract
 * @param tokenIds - An array of token indices as represented on the exchange
 * @param provisionPrice - Price at which liquidity is to be provided
 * @param sellAmountOwl - Amount of feeToken to be sold
 * @param artifacts - A context-like object providing a gateway to Truffle contract ABIs.
 * @returns Void Promise
 */
function placeFeeTokenLiquidityOrders(exchange, tokenIds, provisionPrice, sellAmountOwl, artifacts) {
    var _a, _b;
    return __awaiter(this, void 0, void 0, function* () {
        const minBuyAmounts = [];
        const validTokenIds = [];
        const feeToken = yield fetchTokenInfoFromExchange(exchange, [0], artifacts);
        // This is expected to always be OWL which has 18 digits.
        const feeDigits = ((_a = feeToken.get(0)) === null || _a === void 0 ? void 0 : _a.decimals) || 18;
        const tokenInfo = yield fetchTokenInfoFromExchange(exchange, tokenIds, artifacts);
        for (const tokenId of tokenIds) {
            const numDigits = (_b = tokenInfo.get(tokenId)) === null || _b === void 0 ? void 0 : _b.decimals;
            if (numDigits) {
                validTokenIds.push(tokenId);
                if (numDigits < feeDigits) {
                    minBuyAmounts.push(sellAmountOwl
                        .mul(provisionPrice)
                        .div(new bn_js_1.default(10).pow(new bn_js_1.default(feeDigits - numDigits))));
                }
                else {
                    minBuyAmounts.push(sellAmountOwl
                        .mul(provisionPrice)
                        .mul(new bn_js_1.default(10).pow(new bn_js_1.default(numDigits - feeDigits))));
                }
            }
        }
        const numOrders = validTokenIds.length;
        const batchId = (yield exchange.getCurrentBatchId()).toNumber();
        if (numOrders == 0) {
            return [];
        }
        yield exchange.placeValidFromOrders(validTokenIds, // buyTokens
        Array(numOrders).fill(0), // sellTokens
        Array(numOrders).fill(batchId + 2), // validFroms (all to begin 2 batches from now)
        Array(numOrders).fill(MAXU32), // validTos
        minBuyAmounts, // buyAmounts
        Array(numOrders).fill(sellAmountOwl));
        return validTokenIds;
    });
}
exports.placeFeeTokenLiquidityOrders = placeFeeTokenLiquidityOrders;
