"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../../../utils/utils");
const SpokePool_Fixture_1 = require("./fixtures/SpokePool.Fixture");
const constants_1 = require("./constants");
let spokePool, weth, erc20, destErc20, erc1271;
let depositor, recipient, relayer;
// _fillRelay takes a V3RelayExecutionParams object as a param. This function returns the correct object
// as a convenience.
async function getRelayExecutionParams(_relayData, destinationChainId) {
    return {
        relay: _relayData,
        relayHash: (0, SpokePool_Fixture_1.getV3RelayHash)(_relayData, destinationChainId),
        updatedOutputAmount: _relayData.outputAmount,
        updatedRecipient: _relayData.recipient,
        updatedMessage: _relayData.message,
        repaymentChainId: constants_1.repaymentChainId,
    };
}
describe("SpokePool Relayer Logic", async function () {
    beforeEach(async function () {
        [depositor, recipient, relayer] = await utils_1.ethers.getSigners();
        ({ weth, erc20, spokePool, destErc20, erc1271 } = await (0, SpokePool_Fixture_1.spokePoolFixture)());
        // mint some fresh tokens and deposit ETH for weth for depositor and relayer.
        await (0, utils_1.seedWallet)(depositor, [erc20], weth, constants_1.amountToSeedWallets);
        await (0, utils_1.seedWallet)(relayer, [destErc20], weth, constants_1.amountToSeedWallets);
        // Approve spokepool to spend tokens
        await erc20.connect(depositor).approve(spokePool.address, constants_1.amountToDeposit);
        await weth.connect(depositor).approve(spokePool.address, constants_1.amountToDeposit);
        await destErc20.connect(relayer).approve(spokePool.address, constants_1.amountToDeposit);
        await weth.connect(relayer).approve(spokePool.address, constants_1.amountToDeposit);
    });
    describe("fill V3", function () {
        let relayData;
        beforeEach(async function () {
            const fillDeadline = (await spokePool.getCurrentTime()).toNumber() + 1000;
            relayData = {
                depositor: (0, utils_1.addressToBytes)(depositor.address),
                recipient: (0, utils_1.addressToBytes)(recipient.address),
                exclusiveRelayer: (0, utils_1.addressToBytes)(relayer.address),
                inputToken: (0, utils_1.addressToBytes)(erc20.address),
                outputToken: (0, utils_1.addressToBytes)(destErc20.address),
                inputAmount: constants_1.amountToDeposit,
                outputAmount: constants_1.amountToDeposit,
                originChainId: constants_1.originChainId,
                depositId: constants_1.firstDepositId,
                fillDeadline: fillDeadline,
                exclusivityDeadline: fillDeadline - 500,
                message: "0x",
            };
        });
        describe("_fillRelay internal logic", function () {
            it("default status is unfilled", async function () {
                const relayExecution = await getRelayExecutionParams(relayData, constants_1.destinationChainId);
                (0, utils_1.expect)(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(0 /* FillStatus.Unfilled */);
            });
            // @todo we can remove this after the new spoke pool is upgraded
            it("relay hash is same pre and post address -> bytes32 upgrade", async function () {
                const newBytes32Keys = ["depositor", "recipient", "exclusiveRelayer", "inputToken", "outputToken"];
                const relayDataCopy = { ...relayData, message: (0, utils_1.randomBytes32)() };
                const legacyRelayData = {
                    ...relayDataCopy,
                    depositor: (0, utils_1.bytes32ToAddress)(relayData.depositor),
                    recipient: (0, utils_1.bytes32ToAddress)(relayData.recipient),
                    exclusiveRelayer: (0, utils_1.bytes32ToAddress)(relayData.exclusiveRelayer),
                    inputToken: (0, utils_1.bytes32ToAddress)(relayData.inputToken),
                    outputToken: (0, utils_1.bytes32ToAddress)(relayData.outputToken),
                };
                (0, utils_1.expect)(newBytes32Keys.every((key) => utils_1.ethers.utils.hexDataLength(legacyRelayData[key]) === 20)).to.be.true;
                (0, utils_1.expect)(newBytes32Keys.every((key) => utils_1.ethers.utils.hexDataLength(relayDataCopy[key]) === 32)).to.be.true;
                const newRelayHash = (0, SpokePool_Fixture_1.getV3RelayHash)(relayDataCopy, constants_1.destinationChainId);
                const oldRelayHash = (0, SpokePool_Fixture_1.getLegacyV3RelayHash)(legacyRelayData, constants_1.destinationChainId);
                (0, utils_1.expect)(newRelayHash).to.equal(oldRelayHash);
            });
            it("expired fill deadline reverts", async function () {
                const _relay = {
                    ...relayData,
                    fillDeadline: 0, // Will always be less than SpokePool.currentTime so should expire.
                };
                const relayExecution = await getRelayExecutionParams(_relay, constants_1.destinationChainId);
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), false // isSlowFill
                )).to.be.revertedWith("ExpiredFillDeadline");
            });
            it("relay hash already marked filled", async function () {
                const relayExecution = await getRelayExecutionParams(relayData, constants_1.destinationChainId);
                await spokePool.setFillStatus(relayExecution.relayHash, 2 /* FillStatus.Filled */);
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), false // isSlowFill
                )).to.be.revertedWith("RelayFilled");
            });
            it("fast fill replacing speed up request emits correct FillType", async function () {
                const relayExecution = await getRelayExecutionParams(relayData, constants_1.destinationChainId);
                await spokePool.setFillStatus(relayExecution.relayHash, 1 /* FillStatus.RequestedSlowFill */);
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), false // isSlowFill
                ))
                    .to.emit(spokePool, "FilledRelay")
                    .withArgs((0, utils_1.addressToBytes)(relayData.inputToken), (0, utils_1.addressToBytes)(relayData.outputToken), relayData.inputAmount, relayData.outputAmount, relayExecution.repaymentChainId, relayData.originChainId, relayData.depositId, relayData.fillDeadline, relayData.exclusivityDeadline, (0, utils_1.addressToBytes)(relayData.exclusiveRelayer), (0, utils_1.addressToBytes)(relayer.address), (0, utils_1.addressToBytes)(relayData.depositor), (0, utils_1.addressToBytes)(relayData.recipient), (0, utils_1.hashNonEmptyMessage)(relayData.message), [
                    (0, utils_1.addressToBytes)(relayData.recipient),
                    (0, utils_1.hashNonEmptyMessage)(relayExecution.updatedMessage),
                    relayExecution.updatedOutputAmount,
                    1 /* FillType.ReplacedSlowFill */,
                ]);
                (0, utils_1.expect)(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(2 /* FillStatus.Filled */);
            });
            it("slow fill emits correct FillType", async function () {
                const relayExecution = await getRelayExecutionParams(relayData, constants_1.destinationChainId);
                await destErc20.connect(relayer).transfer(spokePool.address, relayExecution.updatedOutputAmount);
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), true // isSlowFill
                ))
                    .to.emit(spokePool, "FilledRelay")
                    .withArgs((0, utils_1.addressToBytes)(relayData.inputToken), (0, utils_1.addressToBytes)(relayData.outputToken), relayData.inputAmount, relayData.outputAmount, relayExecution.repaymentChainId, relayData.originChainId, relayData.depositId, relayData.fillDeadline, relayData.exclusivityDeadline, (0, utils_1.addressToBytes)(relayData.exclusiveRelayer), (0, utils_1.addressToBytes)(relayer.address), (0, utils_1.addressToBytes)(relayData.depositor), (0, utils_1.addressToBytes)(relayData.recipient), (0, utils_1.hashNonEmptyMessage)(relayData.message), [
                    (0, utils_1.addressToBytes)(relayData.recipient),
                    (0, utils_1.hashNonEmptyMessage)(relayExecution.updatedMessage),
                    relayExecution.updatedOutputAmount,
                    2 /* FillType.SlowFill */,
                ]);
                (0, utils_1.expect)(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(2 /* FillStatus.Filled */);
            });
            it("fast fill emits correct FillType", async function () {
                const relayExecution = await getRelayExecutionParams(relayData, constants_1.destinationChainId);
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), false // isSlowFill
                ))
                    .to.emit(spokePool, "FilledRelay")
                    .withArgs((0, utils_1.addressToBytes)(relayData.inputToken), (0, utils_1.addressToBytes)(relayData.outputToken), relayData.inputAmount, relayData.outputAmount, relayExecution.repaymentChainId, relayData.originChainId, relayData.depositId, relayData.fillDeadline, relayData.exclusivityDeadline, (0, utils_1.addressToBytes)(relayData.exclusiveRelayer), (0, utils_1.addressToBytes)(relayer.address), (0, utils_1.addressToBytes)(relayData.depositor), (0, utils_1.addressToBytes)(relayData.recipient), (0, utils_1.hashNonEmptyMessage)(relayData.message), [
                    (0, utils_1.addressToBytes)(relayData.recipient),
                    (0, utils_1.hashNonEmptyMessage)(relayExecution.updatedMessage),
                    relayExecution.updatedOutputAmount,
                    0 /* FillType.FastFill */,
                ]);
                (0, utils_1.expect)(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(2 /* FillStatus.Filled */);
            });
            it("transfers funds even when msg.sender is recipient", async function () {
                const _relayData = {
                    ...relayData,
                    // Set recipient == relayer
                    recipient: (0, utils_1.addressToBytes)(relayer.address),
                };
                const relayExecution = await getRelayExecutionParams(_relayData, constants_1.destinationChainId);
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), false // isSlowFill
                )).to.emit(destErc20, "Transfer");
            });
            it("sends updatedOutputAmount to updatedRecipient", async function () {
                const relayExecution = await getRelayExecutionParams(relayData, constants_1.destinationChainId);
                const _relayExecution = {
                    ...relayExecution,
                    // Overwrite amount to send to be double the original amount
                    updatedOutputAmount: constants_1.amountToDeposit.mul(2),
                    // Overwrite recipient to depositor which is not the same as the original recipient
                    updatedRecipient: (0, utils_1.addressToBytes)(depositor.address),
                };
                (0, utils_1.expect)(_relayExecution.updatedRecipient).to.not.equal((0, utils_1.addressToBytes)(relayExecution.updatedRecipient));
                (0, utils_1.expect)(_relayExecution.updatedOutputAmount).to.not.equal(relayExecution.updatedOutputAmount);
                await destErc20.connect(relayer).approve(spokePool.address, _relayExecution.updatedOutputAmount);
                await (0, utils_1.expect)(() => spokePool.connect(relayer).fillRelayV3Internal(_relayExecution, (0, utils_1.addressToBytes)(relayer.address), false // isSlowFill
                )).to.changeTokenBalance(destErc20, depositor, constants_1.amountToDeposit.mul(2));
            });
            it("unwraps native token if sending to EOA", async function () {
                const _relayData = {
                    ...relayData,
                    outputToken: (0, utils_1.addressToBytes)(weth.address),
                };
                const relayExecution = await getRelayExecutionParams(_relayData, constants_1.destinationChainId);
                await (0, utils_1.expect)(() => spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), false // isSlowFill
                )).to.changeEtherBalance(recipient, relayExecution.updatedOutputAmount);
            });
            it("slow fills send native token out of spoke pool balance", async function () {
                const _relayData = {
                    ...relayData,
                    outputToken: (0, utils_1.addressToBytes)(weth.address),
                };
                const relayExecution = await getRelayExecutionParams(_relayData, constants_1.destinationChainId);
                await weth.connect(relayer).transfer(spokePool.address, relayExecution.updatedOutputAmount);
                const initialSpokeBalance = await weth.balanceOf(spokePool.address);
                await (0, utils_1.expect)(() => spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), true // isSlowFill
                )).to.changeEtherBalance(recipient, relayExecution.updatedOutputAmount);
                const spokeBalance = await weth.balanceOf(spokePool.address);
                (0, utils_1.expect)(spokeBalance).to.equal(initialSpokeBalance.sub(relayExecution.updatedOutputAmount));
            });
            it("slow fills send non-native token out of spoke pool balance", async function () {
                const relayExecution = await getRelayExecutionParams(relayData, constants_1.destinationChainId);
                await destErc20.connect(relayer).transfer(spokePool.address, relayExecution.updatedOutputAmount);
                await (0, utils_1.expect)(() => spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), true // isSlowFill
                )).to.changeTokenBalance(destErc20, spokePool, relayExecution.updatedOutputAmount.mul(-1));
            });
            it("if recipient is contract that implements message handler, calls message handler", async function () {
                // Does nothing if message length is 0
                const acrossMessageHandler = await (0, utils_1.createFake)("AcrossMessageHandlerMock");
                const _relayData = {
                    ...relayData,
                    recipient: (0, utils_1.addressToBytes)(acrossMessageHandler.address),
                    message: "0x1234",
                };
                const relayExecution = await getRelayExecutionParams(_relayData, constants_1.destinationChainId);
                // Handler is called with expected params.
                await spokePool.connect(relayer).fillRelayV3Internal(relayExecution, (0, utils_1.addressToBytes)(relayer.address), false // isSlowFill
                );
                (0, utils_1.expect)(acrossMessageHandler.handleV3AcrossMessage).to.have.been.calledOnceWith((0, utils_1.bytes32ToAddress)(_relayData.outputToken), relayExecution.updatedOutputAmount, relayer.address, // Custom relayer
                _relayData.message);
            });
        });
        describe("fillV3Relay", function () {
            it("fills are not paused", async function () {
                await spokePool.pauseFills(true);
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelay(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address))).to.be.revertedWith("FillsArePaused");
            });
            it("reentrancy protected", async function () {
                const functionCalldata = spokePool.interface.encodeFunctionData("fillRelay", [
                    relayData,
                    constants_1.repaymentChainId,
                    (0, utils_1.addressToBytes)(relayer.address),
                ]);
                await (0, utils_1.expect)(spokePool.connect(relayer).callback(functionCalldata)).to.be.revertedWith("ReentrancyGuard: reentrant call");
            });
            it("must be exclusive relayer before exclusivity deadline", async function () {
                const _relayData = {
                    ...relayData,
                    // Overwrite exclusive relayer and exclusivity deadline
                    exclusiveRelayer: (0, utils_1.addressToBytes)(recipient.address),
                    exclusivityDeadline: relayData.fillDeadline,
                };
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelay(_relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address))).to.be.revertedWith("NotExclusiveRelayer");
                // Can send it after exclusivity deadline
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelay({ ..._relayData, exclusivityDeadline: 0 }, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address))).to.not.be.reverted;
            });
            it("calls _fillRelayV3 with expected params", async function () {
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelay(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address)))
                    .to.emit(spokePool, "FilledRelay")
                    .withArgs((0, utils_1.addressToBytes)(relayData.inputToken), (0, utils_1.addressToBytes)(relayData.outputToken), relayData.inputAmount, relayData.outputAmount, constants_1.repaymentChainId, // Should be passed-in repayment chain ID
                relayData.originChainId, relayData.depositId, relayData.fillDeadline, relayData.exclusivityDeadline, (0, utils_1.addressToBytes)(relayData.exclusiveRelayer), (0, utils_1.addressToBytes)(relayer.address), // Should be equal to msg.sender of fillRelayV3
                (0, utils_1.addressToBytes)(relayData.depositor), (0, utils_1.addressToBytes)(relayData.recipient), (0, utils_1.hashNonEmptyMessage)(relayData.message), [
                    (0, utils_1.addressToBytes)(relayData.recipient), // updatedRecipient should be equal to recipient
                    (0, utils_1.hashNonEmptyMessage)(relayData.message), // updatedMessageHash should be equal to message hash
                    relayData.outputAmount, // updatedOutputAmount should be equal to outputAmount
                    0 /* FillType.FastFill */,
                ]);
            });
            it("calls legacy (address) fillRelayV3 with expected params", async function () {
                const legacyRelayData = {
                    ...relayData,
                    depositor: (0, utils_1.bytes32ToAddress)(relayData.depositor),
                    recipient: (0, utils_1.bytes32ToAddress)(relayData.recipient),
                    exclusiveRelayer: (0, utils_1.bytes32ToAddress)(relayData.exclusiveRelayer),
                    inputToken: (0, utils_1.bytes32ToAddress)(relayData.inputToken),
                    outputToken: (0, utils_1.bytes32ToAddress)(relayData.outputToken),
                };
                await (0, utils_1.expect)(spokePool.connect(relayer).fillV3Relay(legacyRelayData, constants_1.repaymentChainId))
                    .to.emit(spokePool, "FilledRelay")
                    .withArgs((0, utils_1.addressToBytes)(relayData.inputToken), (0, utils_1.addressToBytes)(relayData.outputToken), relayData.inputAmount, relayData.outputAmount, constants_1.repaymentChainId, // Should be passed-in repayment chain ID
                relayData.originChainId, relayData.depositId, relayData.fillDeadline, relayData.exclusivityDeadline, (0, utils_1.addressToBytes)(relayData.exclusiveRelayer), (0, utils_1.addressToBytes)(relayer.address), // Should be equal to msg.sender of fillRelayV3
                (0, utils_1.addressToBytes)(relayData.depositor), (0, utils_1.addressToBytes)(relayData.recipient), (0, utils_1.hashNonEmptyMessage)(relayData.message), [
                    (0, utils_1.addressToBytes)(relayData.recipient), // updatedRecipient should be equal to recipient
                    (0, utils_1.hashNonEmptyMessage)(relayData.message), // updatedMessageHash should be equal to message hash
                    relayData.outputAmount, // updatedOutputAmount should be equal to outputAmount
                    0 /* FillType.FastFill */,
                ]);
            });
        });
        describe("fillRelayWithUpdatedDeposit", function () {
            let updatedOutputAmount, updatedRecipient, updatedMessage, signature;
            beforeEach(async function () {
                updatedOutputAmount = relayData.outputAmount.add(1);
                updatedRecipient = (0, utils_1.randomAddress)();
                updatedMessage = (0, utils_1.createRandomBytes32)();
                await destErc20.connect(relayer).approve(spokePool.address, updatedOutputAmount);
                signature = await (0, SpokePool_Fixture_1.getUpdatedV3DepositSignature)(depositor, relayData.depositId, relayData.originChainId, updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage);
            });
            it("in absence of exclusivity", async function () {
                // Clock drift between spokes can mean exclusivityDeadline is in future even when no exclusivity was applied.
                await spokePool.setCurrentTime(relayData.exclusivityDeadline - 1);
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit({ ...relayData, exclusivityDeadline: 0 }, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.emit(spokePool, "FilledRelay");
            });
            it("must be exclusive relayer before exclusivity deadline", async function () {
                const _relayData = {
                    ...relayData,
                    // Overwrite exclusive relayer and exclusivity deadline
                    exclusiveRelayer: (0, utils_1.addressToBytes)(recipient.address),
                    exclusivityDeadline: relayData.fillDeadline,
                };
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit(_relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.be.revertedWith("NotExclusiveRelayer");
                // Even if not exclusive relayer, can send it after exclusivity deadline
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelayWithUpdatedDeposit({
                    ..._relayData,
                    exclusivityDeadline: 0,
                }, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.not.be.reverted;
                // @dev: However note that if the relayer modifies the relay data in production such that it doesn't match a
                // deposit with the origin chain ID and deposit ID, then the relay will not be refunded. Therefore, this
                // function is not a backdoor to send an invalid fill.
            });
            it("Happy case: updates fill status for relay hash associated with original relay data", async function () {
                // Check event is emitted with updated params
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature))
                    .to.emit(spokePool, "FilledRelay")
                    .withArgs((0, utils_1.addressToBytes)(relayData.inputToken), (0, utils_1.addressToBytes)(relayData.outputToken), relayData.inputAmount, relayData.outputAmount, constants_1.repaymentChainId, // Should be passed-in repayment chain ID
                relayData.originChainId, relayData.depositId, relayData.fillDeadline, relayData.exclusivityDeadline, (0, utils_1.addressToBytes)(relayData.exclusiveRelayer), (0, utils_1.addressToBytes)(relayer.address), // Should be equal to msg.sender
                (0, utils_1.addressToBytes)(relayData.depositor), (0, utils_1.addressToBytes)(relayData.recipient), (0, utils_1.hashNonEmptyMessage)(relayData.message), [
                    // Should use passed-in updated params:
                    (0, utils_1.addressToBytes)(updatedRecipient),
                    (0, utils_1.hashNonEmptyMessage)(updatedMessage),
                    updatedOutputAmount,
                    0 /* FillType.FastFill */,
                ]);
                // Check fill status mapping is updated
                const relayExecution = await getRelayExecutionParams(relayData, constants_1.destinationChainId);
                (0, utils_1.expect)(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(2 /* FillStatus.Filled */);
            });
            it("validates depositor signature", async function () {
                // Incorrect depositor
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit({ ...relayData, depositor: (0, utils_1.addressToBytes)(relayer.address) }, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.be.revertedWith("InvalidDepositorSignature");
                // Incorrect signature for new deposit ID
                const otherSignature = await (0, SpokePool_Fixture_1.getUpdatedV3DepositSignature)(depositor, relayData.depositId.add((0, utils_1.toBN)(1)), relayData.originChainId, updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage);
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, otherSignature)).to.be.revertedWith("InvalidDepositorSignature");
                // Incorrect origin chain ID
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit({ ...relayData, originChainId: relayData.originChainId + 1 }, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.be.revertedWith("InvalidDepositorSignature");
                // Incorrect deposit ID
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit({ ...relayData, depositId: relayData.depositId.add((0, utils_1.toBN)(1)) }, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.be.revertedWith("InvalidDepositorSignature");
                // Incorrect updated output amount
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount.sub(1), (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.be.revertedWith("InvalidDepositorSignature");
                // Incorrect updated recipient
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)((0, utils_1.randomAddress)()), updatedMessage, signature)).to.be.revertedWith("InvalidDepositorSignature");
                // Incorrect updated message
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, (0, utils_1.createRandomBytes32)())).to.be.revertedWith("InvalidDepositorSignature");
            });
            it("validates ERC-1271 depositor contract signature", async function () {
                // The MockERC1271 contract returns true for isValidSignature if the signature was signed by the contract's
                // owner, so using the depositor's signature should succeed and using someone else's signature should fail.
                const incorrectSignature = await (0, SpokePool_Fixture_1.getUpdatedV3DepositSignature)(relayer, // not depositor
                relayData.depositId, relayData.originChainId, updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage);
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit({ ...relayData, depositor: (0, utils_1.addressToBytes)(erc1271.address) }, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, incorrectSignature)).to.be.revertedWith("InvalidDepositorSignature");
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit({ ...relayData, depositor: (0, utils_1.addressToBytes)(erc1271.address) }, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.not.be.reverted;
            });
            it("cannot send updated fill after original fill", async function () {
                await spokePool.connect(relayer).fillRelay(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address));
                await (0, utils_1.expect)(spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature)).to.be.revertedWith("RelayFilled");
            });
            it("cannot send updated fill after slow fill", async function () {
                await spokePool
                    .connect(relayer)
                    .fillRelayWithUpdatedDeposit(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address), updatedOutputAmount, (0, utils_1.addressToBytes)(updatedRecipient), updatedMessage, signature);
                await (0, utils_1.expect)(spokePool.connect(relayer).fillRelay(relayData, constants_1.repaymentChainId, (0, utils_1.addressToBytes)(relayer.address))).to.be.revertedWith("RelayFilled");
            });
        });
    });
});
