synapsecns/sanguine

View on GitHub
packages/contracts-rfq/test/FastBridgeV2.Dst.Zap.t.sol

Summary

Maintainability
Test Coverage
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol";
import {FastBridgeV2DstExclusivityTest} from "./FastBridgeV2.Dst.Exclusivity.t.sol";
import {RecipientMock} from "./mocks/RecipientMock.sol";

import {Address} from "@openzeppelin/contracts/utils/Address.sol";

// solhint-disable func-name-mixedcase, ordering
contract FastBridgeV2DstZapTest is FastBridgeV2DstExclusivityTest {
    bytes public constant ZAP_DATA = abi.encode("Hello, world!");
    bytes public constant REVERT_MSG = "GM, this is a revert";

    function createFixtures() public virtual override {
        // In the inherited tests userB is always used as the recipient of the tokens.
        userB = address(new RecipientMock());
        vm.label(userB, "ContractRecipient");
        super.createFixtures();
    }

    function createFixturesV2() public virtual override {
        super.createFixturesV2();
        setTokenTestZapData(ZAP_DATA);
        setEthTestZapData(ZAP_DATA);
    }

    /// @notice We override the "expect event" function to also check for the Zap
    /// made to the token recipient.
    function expectBridgeRelayed(
        IFastBridgeV2.BridgeTransactionV2 memory bridgeTx,
        bytes32 txId,
        address relayer
    )
        public
        virtual
        override
    {
        vm.expectCall({callee: userB, data: getExpectedCalldata(bridgeTx), count: 1});
        super.expectBridgeRelayed(bridgeTx, txId, relayer);
    }

    function mockRecipientRevert(IFastBridgeV2.BridgeTransactionV2 memory bridgeTx) public {
        vm.mockCallRevert({callee: userB, data: getExpectedCalldata(bridgeTx), revertData: bytes(REVERT_MSG)});
    }

    function getExpectedCalldata(IFastBridgeV2.BridgeTransactionV2 memory bridgeTx)
        public
        pure
        returns (bytes memory)
    {
        // zap(token, amount, zapData)
        return abi.encodeCall(RecipientMock.zap, (bridgeTx.destToken, bridgeTx.destAmount, ZAP_DATA));
    }

    // ═══════════════════════════════════════════════ RECIPIENT EOA ═══════════════════════════════════════════════════

    function test_relay_token_revert_recipientNotContract() public {
        setTokenTestRecipient(userA);
        vm.expectRevert(abi.encodeWithSelector(Address.AddressEmptyCode.selector, userA));
        relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddress_revert_recipientNotContract() public {
        setTokenTestRecipient(userA);
        vm.expectRevert(abi.encodeWithSelector(Address.AddressEmptyCode.selector, userA));
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_eth_revert_recipientNotContract() public {
        setEthTestRecipient(userA);
        vm.expectRevert(abi.encodeWithSelector(Address.AddressEmptyCode.selector, userA));
        relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    function test_relay_eth_withRelayerAddress_revert_recipientNotContract() public {
        setEthTestRecipient(userA);
        vm.expectRevert(abi.encodeWithSelector(Address.AddressEmptyCode.selector, userA));
        relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    // ═════════════════════════════════════ EXCESSIVE RETURN VALUE RECIPIENT ══════════════════════════════════════════

    function test_relay_token_excessiveReturnValueRecipient_revertWhenZapDataPresent() public virtual override {
        setTokenTestRecipient(excessiveReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddress_excessiveReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestRecipient(excessiveReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withZapNative_excessiveReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestZapNative(ZAP_NATIVE);
        setTokenTestRecipient(excessiveReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relay({caller: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddressZapNative_excessiveReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestZapNative(ZAP_NATIVE);
        setTokenTestRecipient(excessiveReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_eth_excessiveReturnValueRecipient_revertWhenZapDataPresent() public virtual override {
        setEthTestRecipient(excessiveReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    function test_relay_eth_withRelayerAddress_excessiveReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setEthTestRecipient(excessiveReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    // ═════════════════════════════════════ INCORRECT RETURN VALUE RECIPIENT ══════════════════════════════════════════

    function test_relay_token_incorrectReturnValueRecipient_revertWhenZapDataPresent() public virtual override {
        setTokenTestRecipient(incorrectReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddress_incorrectReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestRecipient(incorrectReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withZapNative_incorrectReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestZapNative(ZAP_NATIVE);
        setTokenTestRecipient(incorrectReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relay({caller: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddressZapNative_incorrectReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestZapNative(ZAP_NATIVE);
        setTokenTestRecipient(incorrectReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_eth_incorrectReturnValueRecipient_revertWhenZapDataPresent() public virtual override {
        setEthTestRecipient(incorrectReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    function test_relay_eth_withRelayerAddress_incorrectReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setEthTestRecipient(incorrectReturnValueRecipient);
        vm.expectRevert(RecipientIncorrectReturnValue.selector);
        relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    // ══════════════════════════════════════════════ NO-OP RECIPIENT ══════════════════════════════════════════════════

    // Note: in these tests NoOpRecipient doesn't implement hook function, so we expect a generic OZ library revert.

    function test_relay_token_noOpRecipient_revertWhenZapDataPresent() public virtual override {
        setTokenTestRecipient(noOpRecipient);
        vm.expectRevert(Address.FailedInnerCall.selector);
        relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddress_noOpRecipient_revertWhenZapDataPresent() public virtual override {
        setTokenTestRecipient(noOpRecipient);
        vm.expectRevert(Address.FailedInnerCall.selector);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withZapNative_noOpRecipient_revertWhenZapDataPresent() public virtual override {
        setTokenTestZapNative(ZAP_NATIVE);
        setTokenTestRecipient(noOpRecipient);
        vm.expectRevert(Address.FailedInnerCall.selector);
        relay({caller: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddressZapNative_noOpRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestZapNative(ZAP_NATIVE);
        setTokenTestRecipient(noOpRecipient);
        vm.expectRevert(Address.FailedInnerCall.selector);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_eth_noOpRecipient_revertWhenZapDataPresent() public virtual override {
        setEthTestRecipient(noOpRecipient);
        vm.expectRevert(Address.FailedInnerCall.selector);
        relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    function test_relay_eth_withRelayerAddress_noOpRecipient_revertWhenZapDataPresent() public virtual override {
        setEthTestRecipient(noOpRecipient);
        vm.expectRevert(Address.FailedInnerCall.selector);
        relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    // ═════════════════════════════════════════ NO RETURN VALUE RECIPIENT ═════════════════════════════════════════════

    function test_relay_token_noReturnValueRecipient_revertWhenZapDataPresent() public virtual override {
        setTokenTestRecipient(noReturnValueRecipient);
        vm.expectRevert(RecipientNoReturnValue.selector);
        relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddress_noReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestRecipient(noReturnValueRecipient);
        vm.expectRevert(RecipientNoReturnValue.selector);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withZapNative_noReturnValueRecipient_revertWhenZapDataPresent() public virtual override {
        setTokenTestZapNative(ZAP_NATIVE);
        setTokenTestRecipient(noReturnValueRecipient);
        vm.expectRevert(RecipientNoReturnValue.selector);
        relay({caller: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddressZapNative_noReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setTokenTestZapNative(ZAP_NATIVE);
        setTokenTestRecipient(noReturnValueRecipient);
        vm.expectRevert(RecipientNoReturnValue.selector);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_eth_noReturnValueRecipient_revertWhenZapDataPresent() public virtual override {
        setEthTestRecipient(noReturnValueRecipient);
        vm.expectRevert(RecipientNoReturnValue.selector);
        relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    function test_relay_eth_withRelayerAddress_noReturnValueRecipient_revertWhenZapDataPresent()
        public
        virtual
        override
    {
        setEthTestRecipient(noReturnValueRecipient);
        vm.expectRevert(RecipientNoReturnValue.selector);
        relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    // ═════════════════════════════════════════════ RECIPIENT REVERTS ═════════════════════════════════════════════════

    function test_relay_token_revert_recipientReverts() public {
        mockRecipientRevert(tokenTx);
        vm.expectRevert(REVERT_MSG);
        relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddress_revert_recipientReverts() public {
        mockRecipientRevert(tokenTx);
        vm.expectRevert(REVERT_MSG);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
    }

    function test_relay_token_withZapNative_revert_recipientReverts() public {
        setTokenTestZapNative(ZAP_NATIVE);
        mockRecipientRevert(tokenTx);
        vm.expectRevert(REVERT_MSG);
        relay({caller: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_token_withRelayerAddressZapNative_revert_recipientReverts() public {
        setTokenTestZapNative(ZAP_NATIVE);
        mockRecipientRevert(tokenTx);
        vm.expectRevert(REVERT_MSG);
        relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ZAP_NATIVE, bridgeTx: tokenTx});
    }

    function test_relay_eth_revert_recipientReverts() public {
        mockRecipientRevert(ethTx);
        vm.expectRevert(REVERT_MSG);
        relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    function test_relay_eth_withRelayerAddress_revert_recipientReverts() public {
        mockRecipientRevert(ethTx);
        vm.expectRevert(REVERT_MSG);
        relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    function test_relay_eth_noZapData_revert_recipientReverts() public {
        setEthTestZapData("");
        vm.mockCallRevert({callee: userB, data: "", revertData: bytes(REVERT_MSG)});
        // Note: OZ library doesn't bubble the revert message for just sending ETH
        // (as opposed to doing an external hook call). Therefore we expect a generic library revert.
        vm.expectRevert(Address.FailedInnerCall.selector);
        relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }

    function test_relay_eth_withRelayerAddress_noZapData_revert_recipientReverts() public {
        setEthTestZapData("");
        vm.mockCallRevert({callee: userB, data: "", revertData: bytes(REVERT_MSG)});
        // Note: OZ library doesn't bubble the revert message for just sending ETH
        // (as opposed to doing an external hook call). Therefore we expect a generic library revert.
        vm.expectRevert(Address.FailedInnerCall.selector);
        relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
    }
}