"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MockSpokePoolClient = void 0;
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
const lodash_1 = require("lodash");
const constants_1 = require("../../constants");
const interfaces_1 = require("../../interfaces");
const utils_1 = require("../../utils");
const SpokePoolClient_1 = require("../SpokePoolClient");
const MockEvents_1 = require("./MockEvents");
class MockSpokePoolClient extends SpokePoolClient_1.EVMSpokePoolClient {
    eventManager;
    destinationTokenForChainOverride = {};
    depositIdAtBlock = [];
    numberOfDeposits = utils_1.bnZero;
    constructor(logger, spokePool, chainId, deploymentBlock, opts = {}) {
        super(logger, spokePool, opts.hubPoolClient ?? null, chainId, deploymentBlock);
        this.latestHeightSearched = deploymentBlock;
        this.eventManager = opts.eventManager ?? new MockEvents_1.EventManager(deploymentBlock);
        this.eventManager.addEventSignatures(this.eventSignatures);
    }
    setConfigStoreClient(configStore) {
        this.configStoreClient = configStore;
    }
    setDestinationTokenForChain(chainId, token) {
        this.destinationTokenForChainOverride[chainId] = token;
    }
    getDestinationTokenForDeposit(deposit) {
        const override = this.destinationTokenForChainOverride[deposit.originChainId];
        return (0, utils_1.isDefined)(override)
            ? (0, utils_1.toAddressType)(override, deposit.destinationChainId)
            : super.getDestinationTokenForDeposit(deposit);
    }
    setLatestBlockNumber(blockNumber) {
        this.latestHeightSearched = blockNumber;
    }
    setDepositIds(_depositIds) {
        this.depositIdAtBlock = [];
        if (_depositIds.length === 0) {
            return;
        }
        let lastDepositId = _depositIds[0];
        for (let i = 0; i < _depositIds.length; i++) {
            if (_depositIds[i].lt(lastDepositId)) {
                throw new Error("deposit ID must be equal to or greater than previous");
            }
            this.depositIdAtBlock[i] = _depositIds[i];
            lastDepositId = _depositIds[i];
        }
    }
    _getDepositIdAtBlock(blockTag) {
        return Promise.resolve(this.depositIdAtBlock[blockTag]);
    }
    _update(eventsToQuery) {
        const latestBlockSearched = this.eventManager.blockNumber;
        const currentTime = (0, utils_1.getCurrentTime)();
        const events = eventsToQuery.map(() => []);
        this.eventManager
            .getEvents()
            .flat()
            .forEach((event) => {
            const idx = eventsToQuery.indexOf(event.event);
            if (idx !== -1) {
                events[idx].push(event);
            }
        });
        const eventsWithBlockNumber = events.map((eventList) => eventList.map((event) => (0, utils_1.spreadEventWithBlockNumber)(event)));
        return Promise.resolve({
            success: true,
            firstDepositId: utils_1.bnZero,
            currentTime,
            events: eventsWithBlockNumber,
            searchEndBlock: this.eventSearchConfig.to || latestBlockSearched,
        });
    }
    eventSignatures = {
        EnabledDepositRoute: "address,uint256,bool",
    };
    deposit(deposit) {
        return this._deposit("FundsDeposited", deposit);
    }
    _deposit(event, deposit) {
        const { blockNumber, txnIndex } = deposit;
        let { depositId, destinationChainId, inputAmount, outputAmount } = deposit;
        depositId ??= this.numberOfDeposits;
        this.numberOfDeposits = depositId.add(utils_1.bnOne);
        destinationChainId ??= (0, lodash_1.random)(1, 42161, false);
        const depositor = deposit.depositor?.toBytes32() ?? (0, utils_1.toBytes32)((0, utils_1.randomAddress)());
        const recipient = deposit.recipient?.toBytes32() ?? (0, utils_1.toBytes32)(depositor);
        const inputToken = deposit.inputToken?.toBytes32() ?? (0, utils_1.toBytes32)((0, utils_1.randomAddress)());
        const outputToken = deposit.outputToken?.toBytes32() ?? inputToken;
        const exclusiveRelayer = deposit.exclusiveRelayer?.toBytes32() ?? (0, utils_1.toBytes32)(constants_1.ZERO_ADDRESS);
        inputAmount ??= (0, utils_1.toBNWei)((0, lodash_1.random)(1, 1000, false));
        outputAmount ??= inputAmount.mul((0, utils_1.toBN)("0.95"));
        const message = deposit.message ?? "0x";
        const topics = [destinationChainId, depositId, depositor];
        const quoteTimestamp = deposit.quoteTimestamp ?? (0, utils_1.getCurrentTime)();
        const args = {
            depositId,
            originChainId: deposit.originChainId ?? this.chainId,
            destinationChainId,
            depositor,
            recipient,
            inputToken,
            inputAmount,
            outputToken,
            outputAmount,
            quoteTimestamp,
            fillDeadline: deposit.fillDeadline ?? quoteTimestamp + 3600,
            exclusiveRelayer,
            exclusivityDeadline: deposit.exclusivityDeadline ?? quoteTimestamp + 600,
            message,
        };
        return this.eventManager.generateEvent({
            event,
            address: this.spokePool.address,
            topics: topics.map((topic) => topic.toString()),
            args,
            blockNumber,
            transactionIndex: txnIndex,
        });
    }
    fillRelay(fill) {
        return this._fillRelay("FilledRelay", fill);
    }
    _fillRelay(event, fill) {
        const { blockNumber, txnIndex } = fill;
        let { originChainId, depositId, inputAmount, outputAmount, fillDeadline } = fill;
        originChainId ??= (0, lodash_1.random)(1, 42161, false);
        depositId ??= utils_1.BigNumber.from((0, lodash_1.random)(1, 100_000, false));
        inputAmount ??= (0, utils_1.toBNWei)((0, lodash_1.random)(1, 1000, false));
        outputAmount ??= inputAmount;
        fillDeadline ??= (0, utils_1.getCurrentTime)() + 60;
        const depositor = fill.depositor?.toBytes32() ?? (0, utils_1.toBytes32)((0, utils_1.randomAddress)());
        const recipient = fill.recipient?.toBytes32() ?? (0, utils_1.toBytes32)(depositor);
        const inputToken = fill.inputToken?.toBytes32() ?? (0, utils_1.toBytes32)((0, utils_1.randomAddress)());
        const outputToken = fill.outputToken?.toBytes32() ?? (0, utils_1.toBytes32)(constants_1.ZERO_ADDRESS);
        const exclusiveRelayer = fill.exclusiveRelayer?.toBytes32() ?? (0, utils_1.toBytes32)(constants_1.ZERO_ADDRESS);
        const relayer = fill.relayer?.toBytes32() ?? (0, utils_1.toBytes32)((0, utils_1.randomAddress)());
        const topics = [originChainId, depositId, relayer];
        const message = fill.message ?? constants_1.EMPTY_MESSAGE;
        const updatedMessage = fill.relayExecutionInfo?.updatedMessage ?? message;
        const updatedRecipient = fill.relayExecutionInfo?.updatedRecipient.toBytes32() ?? recipient;
        const relayExecutionInfo = {
            updatedRecipient,
            updatedOutputAmount: fill.relayExecutionInfo?.updatedOutputAmount ?? outputAmount,
            fillType: fill.relayExecutionInfo?.fillType ?? interfaces_1.FillType.FastFill,
        };
        const _args = {
            inputToken,
            outputToken,
            inputAmount: fill.inputAmount,
            outputAmount: fill.outputAmount,
            repaymentChainId: fill.repaymentChainId ?? this.chainId,
            originChainId,
            depositId,
            fillDeadline,
            exclusivityDeadline: fill.exclusivityDeadline ?? fillDeadline,
            exclusiveRelayer,
            relayer,
            depositor,
            recipient,
            relayExecutionInfo: {
                updatedRecipient,
                updatedOutputAmount: fill.relayExecutionInfo?.updatedOutputAmount ?? outputAmount,
                fillType: fill.relayExecutionInfo?.fillType ?? interfaces_1.FillType.FastFill,
            },
        };
        const args = {
            ..._args,
            messageHash: (0, utils_1.getMessageHash)(message),
            relayExecutionInfo: {
                ...relayExecutionInfo,
                updatedMessageHash: (0, utils_1.getMessageHash)(updatedMessage),
            },
        };
        return this.eventManager.generateEvent({
            event,
            address: this.spokePool.address,
            topics: topics.map((topic) => topic.toString()),
            args,
            blockNumber,
            transactionIndex: txnIndex,
        });
    }
    speedUpDeposit(speedUp) {
        return this._speedUpDeposit("RequestedSpeedUpDeposit", speedUp);
    }
    _speedUpDeposit(event, speedUp) {
        const depositor = speedUp.depositor.toBytes32();
        const topics = [speedUp.depositId, depositor];
        const args = { ...speedUp };
        return this.eventManager.generateEvent({
            event,
            address: this.spokePool.address,
            topics: topics.map((topic) => topic.toString()),
            args: {
                ...args,
                depositor,
                updatedRecipient: speedUp.updatedRecipient.toBytes32(),
            },
        });
    }
    setTokensBridged(tokensBridged) {
        const event = "TokensBridged";
        const topics = [tokensBridged.chainId, tokensBridged.leafId, tokensBridged.l2TokenAddress];
        const args = { ...tokensBridged };
        return this.eventManager.generateEvent({
            event,
            address: this.spokePool.address,
            topics: topics.map((topic) => topic.toString()),
            args,
        });
    }
    requestSlowFill(request) {
        return this._requestSlowFill("RequestedSlowFill", request);
    }
    _requestSlowFill(event, request) {
        const { originChainId, depositId } = request;
        const topics = [originChainId, depositId];
        const args = { ...request };
        const depositor = args.depositor.toBytes32() ?? (0, utils_1.toBytes32)((0, utils_1.randomAddress)());
        return this.eventManager.generateEvent({
            event,
            address: this.spokePool.address,
            topics: topics.map((topic) => topic.toString()),
            args: {
                ...args,
                destinationChainId: this.chainId,
                depositor,
                recipient: args.recipient?.toBytes32() ?? depositor,
                inputToken: args.inputToken?.toBytes32() ?? (0, utils_1.toBytes32)((0, utils_1.randomAddress)()),
                outputToken: args.outputToken?.toBytes32() ?? (0, utils_1.toBytes32)(constants_1.ZERO_ADDRESS),
                exclusiveRelayer: args.exclusiveRelayer?.toBytes32() ?? (0, utils_1.toBytes32)(constants_1.ZERO_ADDRESS),
                messageHash: args.messageHash ?? constants_1.ZERO_BYTES,
            },
            blockNumber: request.blockNumber,
            transactionIndex: request.txnIndex,
        });
    }
    executeSlowRelayLeaf(leaf) {
        const destinationChainId = this.chainId;
        const fill = {
            ...leaf.relayData,
            destinationChainId,
            relayer: (0, utils_1.toAddressType)(constants_1.ZERO_ADDRESS, destinationChainId),
            repaymentChainId: 0,
            relayExecutionInfo: {
                updatedRecipient: leaf.relayData.recipient,
                updatedOutputAmount: leaf.updatedOutputAmount,
                updatedMessage: leaf.relayData.message,
                updatedMessageHash: (0, utils_1.getMessageHash)(leaf.relayData.message),
                fillType: interfaces_1.FillType.SlowFill,
            },
        };
        return this.fillRelay(fill);
    }
    executeRelayerRefundLeaf(refund) {
        const event = "ExecutedRelayerRefundRoot";
        const chainId = refund.chainId ?? this.chainId;
        (0, assert_1.default)(chainId === this.chainId);
        const { rootBundleId, leafId } = refund;
        const topics = [chainId, rootBundleId, leafId];
        const args = {
            chainId,
            rootBundleId,
            leafId,
            amountToReturn: refund.amountToReturn,
            l2TokenAddress: refund.l2TokenAddress,
            refundAddresses: refund.refundAddresses,
            refundAmounts: refund.refundAmounts,
        };
        return this.eventManager.generateEvent({
            event,
            address: this.spokePool.address,
            topics: topics.map((topic) => topic.toString()),
            args,
            blockNumber: refund.blockNumber,
        });
    }
    setEnableRoute(originToken, destinationChainId, enabled, overrides = {}) {
        const event = "EnabledDepositRoute";
        const topics = [originToken, destinationChainId];
        const args = { originToken, destinationChainId, enabled };
        return this.eventManager.generateEvent({
            event,
            address: this.spokePool.address,
            topics: topics.map((topic) => topic.toString()),
            args,
            blockNumber: overrides.blockNumber,
        });
    }
}
exports.MockSpokePoolClient = MockSpokePoolClient;
//# sourceMappingURL=MockSpokePoolClient.js.map