import { __assign, __awaiter, __generator, __spreadArray } from "tslib";
import assert from "assert";
import chai, { expect } from "chai";
import chaiExclude from "chai-exclude";
import { ethers, Contract, Signer } from "ethers";
import { ethers as hreEthers } from "hardhat";
import { smock } from "@defi-wonderland/smock";
import _ from "lodash";
import sinon from "sinon";
import winston from "winston";
import { GLOBAL_CONFIG_STORE_KEYS } from "../../src/clients";
import { EMPTY_MESSAGE, PROTOCOL_DEFAULT_CHAIN_ID_INDICES, ZERO_ADDRESS } from "../../src/constants";
import { BigNumber, bnOne, bnUint32Max, getCurrentTime, getMessageHash, isDefined, resolveContractFromSymbol, toAddressType, toBN, toBNWei, toBytes32, toEvmAddress, toWei, utf8ToHex, } from "../../src/utils";
import { amountToSeedWallets, MAX_L1_TOKENS_PER_POOL_REBALANCE_LEAF, MAX_REFUNDS_PER_RELAYER_REFUND_LEAF, originChainId, randomAddress, sampleRateModel, TokenRolesEnum, zeroAddress, } from "../constants";
import { SpyTransport } from "./SpyTransport";
chai.use(chaiExclude);
var chaiAssert = chai.assert;
export { ethers };
// Re-export from fixtures
export { getDepositParams, getUpdatedV3DepositSignature, modifyRelayHelper, getRelayHash, getV3RelayHash, getLegacyV3RelayHash, getFillRelayParams, getFillRelayUpdatedFeeParams, getExecuteSlowRelayParams, deployMockSpokePoolCaller, } from "../fixtures/SpokePool.Fixture";
// Re-export constants
export { randomAddress, zeroAddress };
// Import local Merkle utilities that use our local getContractFactory
export { buildPoolRebalanceLeafTree, buildPoolRebalanceLeaves, buildRelayerRefundTree, buildRelayerRefundLeaves, buildSlowRelayTree, buildV3SlowRelayTree, getParamType, constructSingleRelayerRefundTree, constructSingleChainTree, } from "./MerkleLib.utils";
// Import and export the local getContractFactory
import { getContractFactory } from "./getContractFactory";
export { getContractFactory };
// Import local fixtures that use our local getContractFactory
import { hubPoolFixture, deployHubPool } from "../fixtures/HubPool.Fixture";
import { spokePoolFixture, deploySpokePool } from "../fixtures/SpokePool.Fixture";
export { hubPoolFixture, deployHubPool, spokePoolFixture, deploySpokePool };
// Import and export the merkle lib fixture
export { merkleLibFixture } from "../fixtures/MerkleLib.Fixture";
export { BigNumber, Contract, chai, chaiAssert, expect, sinon, toBN, toBNWei, toWei, utf8ToHex, winston };
// Re-export ethers utilities
var _a = ethers.utils, defaultAbiCoder = _a.defaultAbiCoder, keccak256 = _a.keccak256;
export { defaultAbiCoder, keccak256, Signer };
// ---------------------------------------------------------------------------
// Conversion / encoding helpers (mirroring contracts test-utils)
// ---------------------------------------------------------------------------
export var toWeiWithDecimals = function (num, decimals) {
    return ethers.utils.parseUnits(num.toString(), decimals);
};
export var toBNWeiWithDecimals = function (num, decimals) {
    return BigNumber.from(toWeiWithDecimals(num, decimals));
};
export var fromWei = function (num) { return ethers.utils.formatUnits(num.toString()); };
export var hexToUtf8 = function (input) { return ethers.utils.toUtf8String(input); };
export var hexZeroPad = function (input, length) { return ethers.utils.hexZeroPad(input, length); };
export var addressToBytes = function (input) { return hexZeroPad(input.toLowerCase(), 32); };
export var bytes32ToAddress = function (input) {
    if (!/^0x[a-fA-F0-9]{64}$/.test(input)) {
        throw new Error("Invalid bytes32 input");
    }
    return ethers.utils.getAddress("0x" + input.slice(26));
};
export var isBytes32 = function (input) { return /^0x[0-9a-fA-F]{64}$/.test(input); };
// ---------------------------------------------------------------------------
// Random data generators
// ---------------------------------------------------------------------------
export function randomBigNumber(bytes, signed) {
    if (bytes === void 0) { bytes = 32; }
    if (signed === void 0) { signed = false; }
    var sign = signed && Math.random() < 0.5 ? "-" : "";
    var byteString = "0x" + Buffer.from(ethers.utils.randomBytes(signed ? bytes - 1 : bytes)).toString("hex");
    return ethers.BigNumber.from(sign + byteString);
}
export function randomBytes32() {
    return ethers.utils.hexlify(ethers.utils.randomBytes(32));
}
// ---------------------------------------------------------------------------
// Wallet / contract seeding
// ---------------------------------------------------------------------------
export function seedWallet(walletToFund, tokens, weth, amountToSeedWith) {
    return __awaiter(this, void 0, void 0, function () {
        var _i, tokens_1, token, _a, _b;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    _i = 0, tokens_1 = tokens;
                    _c.label = 1;
                case 1:
                    if (!(_i < tokens_1.length)) return [3 /*break*/, 5];
                    token = tokens_1[_i];
                    _b = (_a = token).mint;
                    return [4 /*yield*/, walletToFund.getAddress()];
                case 2: return [4 /*yield*/, _b.apply(_a, [_c.sent(), amountToSeedWith])];
                case 3:
                    _c.sent();
                    _c.label = 4;
                case 4:
                    _i++;
                    return [3 /*break*/, 1];
                case 5:
                    if (!weth) return [3 /*break*/, 7];
                    return [4 /*yield*/, weth.connect(walletToFund).deposit({ value: amountToSeedWith })];
                case 6:
                    _c.sent();
                    _c.label = 7;
                case 7: return [2 /*return*/];
            }
        });
    });
}
export function seedContract(contract, walletToFund, tokens, weth, amountToSeedWith) {
    return __awaiter(this, void 0, void 0, function () {
        var _i, tokens_2, token;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, seedWallet(walletToFund, tokens, weth, amountToSeedWith)];
                case 1:
                    _a.sent();
                    _i = 0, tokens_2 = tokens;
                    _a.label = 2;
                case 2:
                    if (!(_i < tokens_2.length)) return [3 /*break*/, 5];
                    token = tokens_2[_i];
                    return [4 /*yield*/, token.connect(walletToFund).transfer(contract.address, amountToSeedWith)];
                case 3:
                    _a.sent();
                    _a.label = 4;
                case 4:
                    _i++;
                    return [3 /*break*/, 2];
                case 5:
                    if (!weth) return [3 /*break*/, 7];
                    return [4 /*yield*/, weth.connect(walletToFund).transfer(contract.address, amountToSeedWith)];
                case 6:
                    _a.sent();
                    _a.label = 7;
                case 7: return [2 /*return*/];
            }
        });
    });
}
// ---------------------------------------------------------------------------
// Smock fakes
// ---------------------------------------------------------------------------
export function createFake(contractName_1) {
    return __awaiter(this, arguments, void 0, function (contractName, targetAddress) {
        var contractFactory;
        if (targetAddress === void 0) { targetAddress = ""; }
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, getContractFactory(contractName, new ethers.VoidSigner(ethers.constants.AddressZero))];
                case 1:
                    contractFactory = _a.sent();
                    return [2 /*return*/, smock.fake(contractFactory.interface.fragments, {
                            address: targetAddress === "" ? undefined : targetAddress,
                            provider: contractFactory.signer.provider,
                        })];
            }
        });
    });
}
export function createFakeFromABI(abi_1) {
    return __awaiter(this, arguments, void 0, function (abi, targetAddress) {
        if (targetAddress === void 0) { targetAddress = ""; }
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, createTypedFakeFromABI(abi, targetAddress)];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
export function createTypedFakeFromABI(abi_1) {
    return __awaiter(this, arguments, void 0, function (abi, targetAddress) {
        var signer;
        if (targetAddress === void 0) { targetAddress = ""; }
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    signer = new ethers.VoidSigner(ethers.constants.AddressZero);
                    return [4 /*yield*/, smock.fake(abi, {
                            address: !targetAddress ? undefined : targetAddress,
                            provider: signer.provider,
                        })];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
// ---------------------------------------------------------------------------
// Solana / message helpers
// ---------------------------------------------------------------------------
export function trimSolanaAddress(bytes32Address) {
    if (!ethers.utils.isHexString(bytes32Address, 32)) {
        throw new Error("Invalid bytes32 address");
    }
    var uint160Address = ethers.BigNumber.from(bytes32Address).mask(160);
    return ethers.utils.hexZeroPad(ethers.utils.hexlify(uint160Address), 20);
}
export function hashNonEmptyMessage(message) {
    if (!ethers.utils.isHexString(message) || message.length % 2 !== 0)
        throw new Error("Invalid hex message bytes");
    // account for 0x prefix when checking length
    if (message.length > 2) {
        return ethers.utils.keccak256(message);
    }
    return ethers.utils.hexlify(new Uint8Array(32));
}
// ---------------------------------------------------------------------------
// createRandomBytes32 (kept for backward compatibility)
// ---------------------------------------------------------------------------
export var createRandomBytes32 = function () { return ethers.utils.hexlify(ethers.utils.randomBytes(32)); };
// ---------------------------------------------------------------------------
// Test helper functions
// ---------------------------------------------------------------------------
export function deepEqualsWithBigNumber(x, y, omitKeys) {
    if (omitKeys === void 0) { omitKeys = []; }
    if (x === undefined || y === undefined || x === null || y === null) {
        return false;
    }
    var xObj = x;
    var yObj = y;
    var sortedKeysX = Object.fromEntries(Object.keys(xObj)
        .sort()
        .map(function (key) { return [key, xObj[key]]; }));
    var sortedKeysY = Object.fromEntries(Object.keys(yObj)
        .sort()
        .map(function (key) { return [key, yObj[key]]; }));
    chaiAssert.deepStrictEqual(_.omit(sortedKeysX, omitKeys), _.omit(sortedKeysY, omitKeys));
    return true;
}
export function assertPromiseError(promise, errMessage) {
    return __awaiter(this, void 0, void 0, function () {
        var SPECIAL_ERROR_MESSAGE, e_1, err;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    SPECIAL_ERROR_MESSAGE = "Promise didn't fail";
                    _a.label = 1;
                case 1:
                    _a.trys.push([1, 3, , 4]);
                    return [4 /*yield*/, promise];
                case 2:
                    _a.sent();
                    throw new Error(SPECIAL_ERROR_MESSAGE);
                case 3:
                    e_1 = _a.sent();
                    err = e_1;
                    if (err.message.includes(SPECIAL_ERROR_MESSAGE)) {
                        throw err;
                    }
                    if (errMessage) {
                        chaiAssert.isTrue(err.message.includes(errMessage));
                    }
                    return [3 /*break*/, 4];
                case 4: return [2 /*return*/];
            }
        });
    });
}
export function assertPromisePasses(promise) {
    return __awaiter(this, void 0, void 0, function () {
        var e_2, err;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    _a.trys.push([0, 2, , 3]);
                    return [4 /*yield*/, promise];
                case 1:
                    _a.sent();
                    return [3 /*break*/, 3];
                case 2:
                    e_2 = _a.sent();
                    err = e_2;
                    throw new Error("Promise failed: " + err.message);
                case 3: return [2 /*return*/];
            }
        });
    });
}
export function setupTokensForWallet(contractToApprove_1, wallet_1, tokens_3, weth_1) {
    return __awaiter(this, arguments, void 0, function (contractToApprove, wallet, tokens, weth, seedMultiplier) {
        var approveToken;
        var _this = this;
        if (seedMultiplier === void 0) { seedMultiplier = 1; }
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    approveToken = function (token) { return __awaiter(_this, void 0, void 0, function () {
                        var balance;
                        return __generator(this, function (_a) {
                            switch (_a.label) {
                                case 0: return [4 /*yield*/, token.balanceOf(wallet.address)];
                                case 1:
                                    balance = _a.sent();
                                    return [4 /*yield*/, token.connect(wallet).approve(contractToApprove.address, balance)];
                                case 2:
                                    _a.sent();
                                    return [2 /*return*/];
                            }
                        });
                    }); };
                    return [4 /*yield*/, seedWallet(wallet, tokens, weth, amountToSeedWallets.mul(seedMultiplier))];
                case 1:
                    _a.sent();
                    return [4 /*yield*/, Promise.all(tokens.map(approveToken))];
                case 2:
                    _a.sent();
                    if (!weth) return [3 /*break*/, 4];
                    return [4 /*yield*/, approveToken(weth)];
                case 3:
                    _a.sent();
                    _a.label = 4;
                case 4: return [2 /*return*/];
            }
        });
    });
}
export function createSpyLogger() {
    var spy = sinon.spy();
    var spyLogger = winston.createLogger({
        level: "debug",
        format: winston.format.combine(winston.format(bigNumberFormatter)(), winston.format.json()),
        transports: [
            new SpyTransport({ level: "debug" }, { spy: spy }),
            process.env.LOG_IN_TEST ? new winston.transports.Console() : null,
        ].filter(function (n) { return n; }),
    });
    return { spy: spy, spyLogger: spyLogger };
}
export function deploySpokePoolWithToken() {
    return __awaiter(this, arguments, void 0, function (fromChainId) {
        var _a, weth, erc20, spokePool, unwhitelistedErc20, destErc20, receipt;
        if (fromChainId === void 0) { fromChainId = 0; }
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0: return [4 /*yield*/, deploySpokePool(hreEthers)];
                case 1:
                    _a = _b.sent(), weth = _a.weth, erc20 = _a.erc20, spokePool = _a.spokePool, unwhitelistedErc20 = _a.unwhitelistedErc20, destErc20 = _a.destErc20;
                    return [4 /*yield*/, spokePool.deployTransaction.wait()];
                case 2:
                    receipt = _b.sent();
                    return [4 /*yield*/, spokePool.setChainId(fromChainId == 0 ? originChainId : fromChainId)];
                case 3:
                    _b.sent();
                    return [2 /*return*/, { weth: weth, erc20: erc20, spokePool: spokePool, unwhitelistedErc20: unwhitelistedErc20, destErc20: destErc20, deploymentBlock: receipt.blockNumber }];
            }
        });
    });
}
export function deployConfigStore(signer_1, tokensToAdd_1) {
    return __awaiter(this, arguments, void 0, function (signer, tokensToAdd, maxL1TokensPerPoolRebalanceLeaf, maxRefundPerRelayerRefundLeaf, rateModel, additionalChainIdIndices) {
        var configStore, deploymentBlock, _i, tokensToAdd_2, token;
        if (maxL1TokensPerPoolRebalanceLeaf === void 0) { maxL1TokensPerPoolRebalanceLeaf = MAX_L1_TOKENS_PER_POOL_REBALANCE_LEAF; }
        if (maxRefundPerRelayerRefundLeaf === void 0) { maxRefundPerRelayerRefundLeaf = MAX_REFUNDS_PER_RELAYER_REFUND_LEAF; }
        if (rateModel === void 0) { rateModel = sampleRateModel; }
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, getContractFactory("AcrossConfigStore", signer)];
                case 1: return [4 /*yield*/, (_a.sent()).deploy()];
                case 2:
                    configStore = (_a.sent());
                    return [4 /*yield*/, configStore.deployTransaction.wait()];
                case 3:
                    deploymentBlock = (_a.sent()).blockNumber;
                    _i = 0, tokensToAdd_2 = tokensToAdd;
                    _a.label = 4;
                case 4:
                    if (!(_i < tokensToAdd_2.length)) return [3 /*break*/, 7];
                    token = tokensToAdd_2[_i];
                    return [4 /*yield*/, configStore.updateTokenConfig(token.address, JSON.stringify({
                            rateModel: rateModel,
                        }))];
                case 5:
                    _a.sent();
                    _a.label = 6;
                case 6:
                    _i++;
                    return [3 /*break*/, 4];
                case 7: return [4 /*yield*/, configStore.updateGlobalConfig(utf8ToHex(GLOBAL_CONFIG_STORE_KEYS.MAX_POOL_REBALANCE_LEAF_SIZE), maxL1TokensPerPoolRebalanceLeaf.toString())];
                case 8:
                    _a.sent();
                    return [4 /*yield*/, configStore.updateGlobalConfig(utf8ToHex(GLOBAL_CONFIG_STORE_KEYS.MAX_RELAYER_REPAYMENT_LEAF_SIZE), maxRefundPerRelayerRefundLeaf.toString())];
                case 9:
                    _a.sent();
                    if (!additionalChainIdIndices) return [3 /*break*/, 11];
                    return [4 /*yield*/, configStore.updateGlobalConfig(utf8ToHex(GLOBAL_CONFIG_STORE_KEYS.CHAIN_ID_INDICES), JSON.stringify(__spreadArray(__spreadArray([], PROTOCOL_DEFAULT_CHAIN_ID_INDICES, true), additionalChainIdIndices, true)))];
                case 10:
                    _a.sent();
                    _a.label = 11;
                case 11: return [2 /*return*/, { configStore: configStore, deploymentBlock: deploymentBlock }];
            }
        });
    });
}
export function deployAndConfigureHubPool(signer_1, spokePools_1) {
    return __awaiter(this, arguments, void 0, function (signer, spokePools, finderAddress, timerAddress) {
        var lpTokenFactory, hubPool, receipt, mockAdapter, _i, spokePools_2, spokePool, l1Token_1, l1Token_2;
        if (finderAddress === void 0) { finderAddress = zeroAddress; }
        if (timerAddress === void 0) { timerAddress = zeroAddress; }
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, getContractFactory("LpTokenFactory", signer)];
                case 1: return [4 /*yield*/, (_a.sent()).deploy()];
                case 2:
                    lpTokenFactory = _a.sent();
                    return [4 /*yield*/, getContractFactory("HubPool", signer)];
                case 3: return [4 /*yield*/, (_a.sent()).deploy(lpTokenFactory.address, finderAddress, zeroAddress, timerAddress)];
                case 4:
                    hubPool = _a.sent();
                    return [4 /*yield*/, hubPool.deployTransaction.wait()];
                case 5:
                    receipt = _a.sent();
                    return [4 /*yield*/, getContractFactory("Mock_Adapter", signer)];
                case 6: return [4 /*yield*/, (_a.sent()).deploy()];
                case 7:
                    mockAdapter = _a.sent();
                    _i = 0, spokePools_2 = spokePools;
                    _a.label = 8;
                case 8:
                    if (!(_i < spokePools_2.length)) return [3 /*break*/, 11];
                    spokePool = spokePools_2[_i];
                    return [4 /*yield*/, hubPool.setCrossChainContracts(spokePool.l2ChainId, mockAdapter.address, spokePool.spokePool.address)];
                case 9:
                    _a.sent();
                    _a.label = 10;
                case 10:
                    _i++;
                    return [3 /*break*/, 8];
                case 11: return [4 /*yield*/, getContractFactory("ExpandedERC20", signer)];
                case 12: return [4 /*yield*/, (_a.sent()).deploy("L1Token1", "L1Token1", 18)];
                case 13:
                    l1Token_1 = _a.sent();
                    return [4 /*yield*/, l1Token_1.addMember(TokenRolesEnum.MINTER, signer.address)];
                case 14:
                    _a.sent();
                    return [4 /*yield*/, getContractFactory("ExpandedERC20", signer)];
                case 15: return [4 /*yield*/, (_a.sent()).deploy("L1Token2", "L1Token2", 18)];
                case 16:
                    l1Token_2 = _a.sent();
                    return [4 /*yield*/, l1Token_2.addMember(TokenRolesEnum.MINTER, signer.address)];
                case 17:
                    _a.sent();
                    return [2 /*return*/, { hubPool: hubPool, mockAdapter: mockAdapter, l1Token_1: l1Token_1, l1Token_2: l1Token_2, hubPoolDeploymentBlock: receipt.blockNumber }];
            }
        });
    });
}
export function enableRoutesOnHubPool(hubPool, rebalanceRouteTokens) {
    return __awaiter(this, void 0, void 0, function () {
        var _i, rebalanceRouteTokens_1, tkn;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    _i = 0, rebalanceRouteTokens_1 = rebalanceRouteTokens;
                    _a.label = 1;
                case 1:
                    if (!(_i < rebalanceRouteTokens_1.length)) return [3 /*break*/, 5];
                    tkn = rebalanceRouteTokens_1[_i];
                    return [4 /*yield*/, hubPool.setPoolRebalanceRoute(tkn.destinationChainId, tkn.l1Token.address, tkn.destinationToken.address)];
                case 2:
                    _a.sent();
                    return [4 /*yield*/, hubPool.enableL1TokenForLiquidityProvision(tkn.l1Token.address)];
                case 3:
                    _a.sent();
                    _a.label = 4;
                case 4:
                    _i++;
                    return [3 /*break*/, 1];
                case 5: return [2 /*return*/];
            }
        });
    });
}
/**
 * Takes as input a body and returns a new object with the body and a message property. Used to appease the typescript
 * compiler when we want to return a type that doesn't have a message property.
 * @param body Typically a partial structure of a Deposit or Fill.
 * @returns A new object with the body and a message property.
 */
export function appendMessageToResult(body) {
    return __assign(__assign({}, body), { message: "" });
}
export function getLastBlockTime(provider) {
    return __awaiter(this, void 0, void 0, function () {
        var _a, _b;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    _b = (_a = provider).getBlock;
                    return [4 /*yield*/, provider.getBlockNumber()];
                case 1: return [4 /*yield*/, _b.apply(_a, [_c.sent()])];
                case 2: return [2 /*return*/, (_c.sent()).timestamp];
            }
        });
    });
}
export function addLiquidity(signer, hubPool, l1Token, amount) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, seedWallet(signer, [l1Token], undefined, amount)];
                case 1:
                    _a.sent();
                    return [4 /*yield*/, l1Token.connect(signer).approve(hubPool.address, amount)];
                case 2:
                    _a.sent();
                    return [4 /*yield*/, hubPool.enableL1TokenForLiquidityProvision(l1Token.address)];
                case 3:
                    _a.sent();
                    return [4 /*yield*/, hubPool.connect(signer).addLiquidity(l1Token.address, amount)];
                case 4:
                    _a.sent();
                    return [2 /*return*/];
            }
        });
    });
}
export function deposit(spokePool, destinationChainId, signer, inputToken, inputAmount, outputToken, outputAmount, opts) {
    if (opts === void 0) { opts = {}; }
    return _deposit(spokePool, destinationChainId, signer, inputToken, inputAmount, outputToken, outputAmount, __assign(__assign({}, opts), { addressModifier: toBytes32 }));
}
export function depositV3(spokePool, destinationChainId, signer, inputToken, inputAmount, outputToken, outputAmount, opts) {
    if (opts === void 0) { opts = {}; }
    return _deposit(spokePool, destinationChainId, signer, inputToken, inputAmount, outputToken, outputAmount, __assign(__assign({}, opts), { addressModifier: toEvmAddress }));
}
function _deposit(spokePool_1, destinationChainId_1, signer_1, inputToken_1, inputAmount_1, outputToken_1, outputAmount_1) {
    return __awaiter(this, arguments, void 0, function (spokePool, destinationChainId, signer, inputToken, inputAmount, outputToken, outputAmount, opts) {
        var depositor, _a, _b, recipient, _c, spokePoolTime, fillDeadlineBuffer, quoteTimestamp, message, fillDeadline, exclusivityDeadline, exclusiveRelayer, getChainId, _d, events, fetchedOriginChainId, lastEvent, args, _e, blockNumber, transactionHash, transactionIndex, logIndex;
        var _this = this;
        var _f, _g, _h, _j, _k, _l;
        if (opts === void 0) { opts = {}; }
        return __generator(this, function (_m) {
            switch (_m.label) {
                case 0:
                    _a = toAddressType;
                    _b = [signer.address];
                    return [4 /*yield*/, spokePool.chainId()];
                case 1:
                    depositor = _a.apply(void 0, _b.concat([_m.sent()]));
                    recipient = (_f = opts.recipient) !== null && _f !== void 0 ? _f : depositor;
                    return [4 /*yield*/, Promise.all([spokePool.getCurrentTime(), spokePool.fillDeadlineBuffer()])];
                case 2:
                    _c = (_m.sent()).map(function (n) { return Number(n); }), spokePoolTime = _c[0], fillDeadlineBuffer = _c[1];
                    quoteTimestamp = (_g = opts.quoteTimestamp) !== null && _g !== void 0 ? _g : spokePoolTime;
                    message = (_h = opts.message) !== null && _h !== void 0 ? _h : EMPTY_MESSAGE;
                    fillDeadline = (_j = opts.fillDeadline) !== null && _j !== void 0 ? _j : spokePoolTime + fillDeadlineBuffer;
                    exclusivityDeadline = (_k = opts.exclusivityDeadline) !== null && _k !== void 0 ? _k : 0;
                    exclusiveRelayer = (_l = opts.exclusiveRelayer) !== null && _l !== void 0 ? _l : toAddressType(zeroAddress, destinationChainId);
                    return [4 /*yield*/, spokePool
                            .connect(signer)
                            .deposit(depositor.toBytes32(), recipient.toBytes32(), inputToken.toBytes32(), outputToken.toBytes32(), inputAmount, outputAmount, destinationChainId, exclusiveRelayer.toBytes32(), quoteTimestamp, fillDeadline, exclusivityDeadline, message)];
                case 3:
                    _m.sent();
                    getChainId = function () { return __awaiter(_this, void 0, void 0, function () { var _a, _b, _c; return __generator(this, function (_d) {
                        switch (_d.label) {
                            case 0:
                                _b = (_a = Promise).resolve;
                                _c = Number;
                                return [4 /*yield*/, spokePool.chainId()];
                            case 1: return [2 /*return*/, _b.apply(_a, [_c.apply(void 0, [_d.sent()])])];
                        }
                    }); }); };
                    return [4 /*yield*/, Promise.all([
                            spokePool.queryFilter(spokePool.filters.FundsDeposited()),
                            getChainId(),
                        ])];
                case 4:
                    _d = _m.sent(), events = _d[0], fetchedOriginChainId = _d[1];
                    lastEvent = events.at(-1);
                    args = lastEvent === null || lastEvent === void 0 ? void 0 : lastEvent.args;
                    chaiAssert.exists(args);
                    args = args;
                    _e = lastEvent, blockNumber = _e.blockNumber, transactionHash = _e.transactionHash, transactionIndex = _e.transactionIndex, logIndex = _e.logIndex;
                    assert(args.destinationChainId.toNumber() === destinationChainId);
                    return [2 /*return*/, {
                            depositId: toBN(args.depositId),
                            originChainId: fetchedOriginChainId,
                            destinationChainId: destinationChainId,
                            depositor: toAddressType(args.depositor, fetchedOriginChainId),
                            recipient: toAddressType(args.recipient, destinationChainId),
                            inputToken: toAddressType(args.inputToken, fetchedOriginChainId),
                            inputAmount: args.inputAmount,
                            outputToken: toAddressType(args.outputToken, destinationChainId),
                            outputAmount: args.outputAmount,
                            quoteTimestamp: args.quoteTimestamp,
                            message: args.message,
                            messageHash: getMessageHash(args.message),
                            fillDeadline: args.fillDeadline,
                            exclusivityDeadline: args.exclusivityDeadline,
                            exclusiveRelayer: toAddressType(args.exclusiveRelayer, destinationChainId),
                            fromLiteChain: false,
                            toLiteChain: false,
                            quoteBlockNumber: 0, // @todo
                            blockNumber: blockNumber,
                            txnRef: transactionHash,
                            txnIndex: transactionIndex,
                            logIndex: logIndex,
                        }];
            }
        });
    });
}
export function requestV3SlowFill(spokePool, relayData, signer) {
    return __awaiter(this, void 0, void 0, function () {
        var destinationChainId, _a, events, lastEvent, _b, blockNumber, transactionHash, transactionIndex, logIndex, args, fetchedOriginChainId;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    _a = Number;
                    return [4 /*yield*/, spokePool.chainId()];
                case 1:
                    destinationChainId = _a.apply(void 0, [_c.sent()]);
                    chaiAssert.notEqual(relayData.originChainId, destinationChainId);
                    return [4 /*yield*/, spokePool.connect(signer).requestSlowFill(__assign(__assign({}, relayData), { depositor: relayData.depositor.toBytes32(), recipient: relayData.recipient.toBytes32(), inputToken: relayData.inputToken.toBytes32(), outputToken: relayData.outputToken.toBytes32(), exclusiveRelayer: relayData.exclusiveRelayer.toBytes32() }))];
                case 2:
                    _c.sent();
                    return [4 /*yield*/, spokePool.queryFilter(spokePool.filters.RequestedSlowFill())];
                case 3:
                    events = _c.sent();
                    lastEvent = events.at(-1);
                    expect(lastEvent).to.exist;
                    _b = lastEvent, blockNumber = _b.blockNumber, transactionHash = _b.transactionHash, transactionIndex = _b.transactionIndex, logIndex = _b.logIndex;
                    expect(lastEvent.args).to.exist;
                    args = lastEvent.args;
                    fetchedOriginChainId = Number(args.originChainId);
                    return [2 /*return*/, {
                            depositId: toBN(args.depositId),
                            originChainId: fetchedOriginChainId,
                            destinationChainId: destinationChainId,
                            depositor: toAddressType(args.depositor, fetchedOriginChainId),
                            recipient: toAddressType(args.recipient, destinationChainId),
                            inputToken: toAddressType(args.inputToken, fetchedOriginChainId),
                            inputAmount: args.inputAmount,
                            outputToken: toAddressType(args.outputToken, destinationChainId),
                            outputAmount: args.outputAmount,
                            messageHash: getMessageHash(args.message),
                            fillDeadline: args.fillDeadline,
                            exclusivityDeadline: args.exclusivityDeadline,
                            exclusiveRelayer: toAddressType(args.exclusiveRelayer, destinationChainId),
                            blockNumber: blockNumber,
                            txnRef: transactionHash,
                            txnIndex: transactionIndex,
                            logIndex: logIndex,
                        }];
            }
        });
    });
}
export function fillRelay(spokePool, _deposit, signer, repayment) {
    return __awaiter(this, void 0, void 0, function () {
        var destinationChainId, _a, depositData, repaymentAddress, events, lastEvent, args, _b, blockNumber, transactionHash, transactionIndex, logIndex;
        var _c, _d;
        return __generator(this, function (_e) {
            switch (_e.label) {
                case 0:
                    _a = Number;
                    return [4 /*yield*/, spokePool.chainId()];
                case 1:
                    destinationChainId = _a.apply(void 0, [_e.sent()]);
                    chaiAssert.notEqual(_deposit.originChainId, destinationChainId);
                    depositData = __assign(__assign({}, _deposit), { depositor: _deposit.depositor.toBytes32(), recipient: _deposit.recipient.toBytes32(), exclusiveRelayer: _deposit.exclusiveRelayer.toBytes32(), inputToken: _deposit.inputToken.toBytes32(), outputToken: _deposit.outputToken.toBytes32() });
                    repaymentAddress = (_c = repayment === null || repayment === void 0 ? void 0 : repayment.repaymentAddress.toBytes32()) !== null && _c !== void 0 ? _c : toBytes32(signer.address);
                    return [4 /*yield*/, spokePool
                            .connect(signer)
                            .fillRelay(depositData, (_d = repayment === null || repayment === void 0 ? void 0 : repayment.repaymentChainId) !== null && _d !== void 0 ? _d : destinationChainId, repaymentAddress)];
                case 2:
                    _e.sent();
                    return [4 /*yield*/, spokePool.queryFilter(spokePool.filters.FilledRelay())];
                case 3:
                    events = _e.sent();
                    lastEvent = events.at(-1);
                    args = lastEvent.args;
                    chaiAssert.exists(args);
                    args = args;
                    _b = lastEvent, blockNumber = _b.blockNumber, transactionHash = _b.transactionHash, transactionIndex = _b.transactionIndex, logIndex = _b.logIndex;
                    return [2 /*return*/, {
                            depositId: toBN(args.depositId),
                            originChainId: Number(args.originChainId),
                            destinationChainId: destinationChainId,
                            depositor: toAddressType(args.depositor, args.originChainId),
                            recipient: toAddressType(args.recipient, destinationChainId),
                            inputToken: toAddressType(args.inputToken, args.originChainId),
                            inputAmount: args.inputAmount,
                            outputToken: toAddressType(args.outputToken, destinationChainId),
                            outputAmount: args.outputAmount,
                            messageHash: getMessageHash(args.message),
                            fillDeadline: args.fillDeadline,
                            exclusivityDeadline: args.exclusivityDeadline,
                            exclusiveRelayer: toAddressType(args.exclusiveRelayer, destinationChainId),
                            relayer: args.relayer,
                            repaymentChainId: Number(args.repaymentChainId),
                            relayExecutionInfo: {
                                updatedRecipient: toAddressType(args.relayExecutionInfo.updatedRecipient, destinationChainId),
                                updatedMessageHash: args.relayExecutionInfo.updatedMessageHash,
                                updatedOutputAmount: args.relayExecutionInfo.updatedOutputAmount,
                                fillType: args.relayExecutionInfo.fillType,
                            },
                            blockNumber: blockNumber,
                            txnRef: transactionHash,
                            txnIndex: transactionIndex,
                            logIndex: logIndex,
                        }];
            }
        });
    });
}
/**
 * Grabs the latest block number from the hardhat provider.
 * @returns The latest block number.
 */
export function getLastBlockNumber() {
    return hreEthers.provider.getBlockNumber();
}
export function convertMockedConfigClient(_client) {
    return true;
}
// Iterate over each element in the log and see if it is a big number. if it is, then try casting it to a string to
// make it more readable. If something goes wrong in parsing the object (it's too large or something else) then simply
// return the original log entry without modifying it.
export function bigNumberFormatter(logEntry) {
    try {
        // Out is the original object if and only if one or more BigNumbers were replaced.
        var out_1 = iterativelyReplaceBigNumbers(logEntry);
        // Because winston depends on some non-enumerable symbol properties, we explicitly copy those over, as they are not
        // handled in iterativelyReplaceBigNumbers. This only needs to happen if logEntry is being replaced.
        if (out_1 !== logEntry)
            Object.getOwnPropertySymbols(logEntry).map(function (symbol) { return (out_1[symbol] = logEntry[symbol]); });
        return out_1;
    }
    catch (_) {
        return logEntry;
    }
}
// Traverse a potentially nested object and replace any element that is either a Ethers BigNumber or web3 BigNumber
// with the string version of it for easy logging.
var iterativelyReplaceBigNumbers = function (obj) {
    // This does a DFS, recursively calling this function to find the desired value for each key.
    // It doesn't modify the original object. Instead, it creates an array of keys and updated values.
    var replacements = Object.entries(obj).map(function (_a) {
        var key = _a[0], value = _a[1];
        if (BigNumber.isBigNumber(value))
            return [key, value.toString()];
        else if (typeof value === "object" && value !== null)
            return [key, iterativelyReplaceBigNumbers(value)];
        else
            return [key, value];
    });
    // This will catch any values that were changed by value _or_ by reference.
    // If no changes were detected, no copy is needed and it is fine to discard the copy and return the original object.
    var copyNeeded = replacements.some(function (_a) {
        var key = _a[0], value = _a[1];
        return obj[key] !== value;
    });
    // Only copy if something changed. Otherwise, return the original object.
    return copyNeeded ? Object.fromEntries(replacements) : obj;
};
export function buildDepositForRelayerFeeTest(amount, tokenSymbol, originChainId, toChainId) {
    var _inputToken = resolveContractFromSymbol(tokenSymbol, originChainId);
    assert(isDefined(_inputToken), "".concat(tokenSymbol, " not found on ").concat(originChainId));
    var inputToken = toAddressType(_inputToken, originChainId);
    var _outputToken = resolveContractFromSymbol(tokenSymbol, toChainId);
    assert(isDefined(_outputToken), "".concat(tokenSymbol, " not found on ").concat(toChainId));
    var outputToken = toAddressType(_outputToken, toChainId);
    var currentTime = getCurrentTime();
    var message = EMPTY_MESSAGE;
    return {
        depositId: bnUint32Max,
        originChainId: originChainId,
        destinationChainId: toChainId,
        depositor: toAddressType(randomAddress(), originChainId),
        recipient: toAddressType(randomAddress(), toChainId),
        inputToken: inputToken,
        inputAmount: toBN(amount),
        outputToken: outputToken,
        outputAmount: toBN(amount).sub(bnOne),
        message: message,
        messageHash: getMessageHash(message),
        quoteTimestamp: currentTime,
        fillDeadline: currentTime + 7200,
        exclusivityDeadline: 0,
        exclusiveRelayer: toAddressType(ZERO_ADDRESS, toChainId),
        fromLiteChain: false,
        toLiteChain: false,
    };
}
//# sourceMappingURL=utils.js.map