code-423n4 / 2023-09-maia-findings

25 stars 17 forks source link

CallOutAndBridge could result in stuck funds on CoreRootRouter #456

Open c4-submissions opened 1 year ago

c4-submissions commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/MulticallRootRouter.sol#L203-L205 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/interfaces/BridgeAgentConstants.sol#L15 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/CoreRootRouter.sol#L350-L357 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgentExecutor.sol#L82-L106 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L490-L512

Vulnerability details

Impact

The callOutAndBridge function on Branches allow users to bridge tokens to other chains, which first need to go through the root implementation on Arbitrum. However, there is a chance after bridging to the root, funds get stuck on the RootCoreRouter contract with the deposit nonce being spent when no extra data is passed into the payload.

Proof of Concept

//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "./ulysses-omnichain/helpers/ImportHelper.sol";

contract TestPocTest is DSTestPlus, BridgeAgentConstants {
    // Consts

    uint16 constant rootChainId = uint16(42161);

    uint16 constant avaxChainId = uint16(43114);

    uint16 constant ftmChainId = uint16(2040);

    // Root

    RootPort rootPort;

    ERC20hTokenRootFactory hTokenFactory;

    RootBridgeAgentFactory bridgeAgentFactory;

    RootBridgeAgent coreRootBridgeAgent;

    RootBridgeAgent multicallBridgeAgent;

    CoreRootRouter rootCoreRouter;

    MulticallRootRouter rootMulticallRouter;

    address lzEndpointAddress = address(new MockEndpoint());

    address owner = address(this);

    address dao = address(this);

    // Arbitrum Branch

    ArbitrumBranchPort arbitrumPort;

    ERC20hTokenBranchFactory localHTokenFactory;

    ArbitrumBranchBridgeAgentFactory arbitrumBranchBridgeAgentFactory;

    ArbitrumBranchBridgeAgent arbitrumCoreBridgeAgent;

    ArbitrumBranchBridgeAgent arbitrumMulticallBridgeAgent;

    ArbitrumCoreBranchRouter arbitrumCoreRouter;

    BaseBranchRouter arbitrumMulticallRouter;

    // Avax Branch

    BranchPort avaxPort;

    ERC20hTokenBranchFactory avaxHTokenFactory;

    BranchBridgeAgentFactory avaxBranchBridgeAgentFactory;

    BranchBridgeAgent avaxCoreBridgeAgent;

    BranchBridgeAgent avaxMulticallBridgeAgent;

    CoreBranchRouter avaxCoreRouter;

    BaseBranchRouter avaxMulticallRouter;

    // Ftm Branch

    BranchPort ftmPort;

    ERC20hTokenBranchFactory ftmHTokenFactory;

    BranchBridgeAgentFactory ftmBranchBridgeAgentFactory;

    BranchBridgeAgent ftmCoreBridgeAgent;

    BranchBridgeAgent ftmMulticallBridgeAgent;

    CoreBranchRouter ftmCoreRouter;

    BaseBranchRouter ftmMulticallRouter;

    // Mocks

    address arbitrumGlobalToken;
    address avaxGlobalToken;
    address ftmGlobalToken;

    address arbitrumWrappedNativeToken;
    address avaxWrappedNativeToken;
    address ftmWrappedNativeToken;

    address arbitrumLocalWrappedNativeToken;
    address avaxLocalWrappedNativeToken;
    address ftmLocalWrappedNativeToken;

    address multicallAddress;

    address testGasPoolAddress = address(0xFFFF);

    address nonFungiblePositionManagerAddress = address(0xABAD);

    address avaxLocalarbitrumWrappedNativeTokenAddress = address(0xBFFF);
    address avaxUnderlyingarbitrumWrappedNativeTokenAddress = address(0xFFFB);

    address ftmLocalarbitrumWrappedNativeTokenAddress = address(0xABBB);
    address ftmUnderlyingarbitrumWrappedNativeTokenAddress = address(0xAAAB);

    address avaxCoreBridgeAgentAddress = address(0xBEEF);

    address avaxMulticallBridgeAgentAddress = address(0xEBFE);

    address avaxPortAddress = address(0xFEEB);

    address ftmCoreBridgeAgentAddress = address(0xCACA);

    address ftmMulticallBridgeAgentAddress = address(0xACAC);

    address ftmPortAddressM = address(0xABAC);

    address public newAvaxAssetLocalTokenAddress;

    address public newFTMAvaxAssetLocalToken;

    address public newAvaxAssetGlobalAddress;
    // ERC20s from different chains.

    address avaxMockAssethToken;

    MockERC20 avaxMockAssetToken;

    address ftmMockAssethToken;

    MockERC20 ftmMockAssetToken;

    ERC20hTokenRoot arbitrumMockAssethToken;

    MockERC20 arbitrumMockToken;

    function setUp() public {
        /////////////////////////////////
        //      Deploy Root Utils      //
        /////////////////////////////////

        arbitrumWrappedNativeToken = address(new WETH());
        avaxWrappedNativeToken = address(new WETH());
        ftmWrappedNativeToken = address(new WETH());

        multicallAddress = address(new Multicall2());

        /////////////////////////////////
        //    Deploy Root Contracts    //
        /////////////////////////////////

        rootPort = new RootPort(rootChainId);

        bridgeAgentFactory = new RootBridgeAgentFactory(
            rootChainId,
            lzEndpointAddress,
            address(rootPort)
        );

        rootCoreRouter = new CoreRootRouter(rootChainId, address(rootPort));

        rootMulticallRouter = new MulticallRootRouter(
            rootChainId,
            address(rootPort),
            multicallAddress
        );

        hTokenFactory = new ERC20hTokenRootFactory(
            rootChainId,
            address(rootPort)
        );

        /////////////////////////////////
        //  Initialize Root Contracts  //
        /////////////////////////////////

        rootPort.initialize(
            address(bridgeAgentFactory),
            address(rootCoreRouter)
        );

        hevm.deal(address(rootPort), 1 ether);
        hevm.prank(address(rootPort));
        WETH(arbitrumWrappedNativeToken).deposit{value: 1 ether}();

        hTokenFactory.initialize(address(rootCoreRouter));

        coreRootBridgeAgent = RootBridgeAgent(
            payable(
                RootBridgeAgentFactory(bridgeAgentFactory).createBridgeAgent(
                    address(rootCoreRouter)
                )
            )
        );

        multicallBridgeAgent = RootBridgeAgent(
            payable(
                RootBridgeAgentFactory(bridgeAgentFactory).createBridgeAgent(
                    address(rootMulticallRouter)
                )
            )
        );

        rootCoreRouter.initialize(
            address(coreRootBridgeAgent),
            address(hTokenFactory)
        );

        rootMulticallRouter.initialize(address(multicallBridgeAgent));

        /////////////////////////////////
        // Deploy Local Branch Contracts//
        /////////////////////////////////

        arbitrumPort = new ArbitrumBranchPort(
            rootChainId,
            address(rootPort),
            owner
        );

        arbitrumMulticallRouter = new BaseBranchRouter();

        arbitrumCoreRouter = new ArbitrumCoreBranchRouter();

        arbitrumBranchBridgeAgentFactory = new ArbitrumBranchBridgeAgentFactory(
            rootChainId,
            address(bridgeAgentFactory),
            address(arbitrumCoreRouter),
            address(arbitrumPort),
            owner
        );

        arbitrumPort.initialize(
            address(arbitrumCoreRouter),
            address(arbitrumBranchBridgeAgentFactory)
        );

        arbitrumBranchBridgeAgentFactory.initialize(
            address(coreRootBridgeAgent)
        );
        arbitrumCoreBridgeAgent = ArbitrumBranchBridgeAgent(
            payable(arbitrumPort.bridgeAgents(0))
        );

        arbitrumCoreRouter.initialize(address(arbitrumCoreBridgeAgent));
        // ArbitrumMulticallRouter.initialize(address(arbitrumMulticallBridgeAgent));

        //////////////////////////////////
        // Deploy Avax Branch Contracts //
        //////////////////////////////////

        avaxPort = new BranchPort(owner);

        avaxHTokenFactory = new ERC20hTokenBranchFactory(
            rootChainId,
            address(avaxPort),
            "Avalanche Ulysses ",
            "avax-u"
        );

        avaxMulticallRouter = new BaseBranchRouter();

        avaxCoreRouter = new CoreBranchRouter(address(avaxHTokenFactory));

        avaxBranchBridgeAgentFactory = new BranchBridgeAgentFactory(
            avaxChainId,
            rootChainId,
            address(bridgeAgentFactory),
            lzEndpointAddress,
            address(avaxCoreRouter),
            address(avaxPort),
            owner
        );

        avaxPort.initialize(
            address(avaxCoreRouter),
            address(avaxBranchBridgeAgentFactory)
        );

        avaxBranchBridgeAgentFactory.initialize(address(coreRootBridgeAgent));
        avaxCoreBridgeAgent = BranchBridgeAgent(
            payable(avaxPort.bridgeAgents(0))
        );

        avaxHTokenFactory.initialize(
            avaxWrappedNativeToken,
            address(avaxCoreRouter)
        );
        avaxLocalWrappedNativeToken = 0x386Cc0A3450d41747C05C62381320C039C65ee0d;

        avaxCoreRouter.initialize(address(avaxCoreBridgeAgent));

        //////////////////////////////////
        // Deploy Ftm Branch Contracts //
        //////////////////////////////////

        ftmPort = new BranchPort(owner);

        ftmHTokenFactory = new ERC20hTokenBranchFactory(
            rootChainId,
            address(ftmPort),
            "Fantom Ulysses ",
            "ftm-u"
        );

        ftmMulticallRouter = new BaseBranchRouter();

        ftmCoreRouter = new CoreBranchRouter(address(ftmHTokenFactory));

        ftmBranchBridgeAgentFactory = new BranchBridgeAgentFactory(
            ftmChainId,
            rootChainId,
            address(bridgeAgentFactory),
            lzEndpointAddress,
            address(ftmCoreRouter),
            address(ftmPort),
            owner
        );

        ftmPort.initialize(
            address(ftmCoreRouter),
            address(ftmBranchBridgeAgentFactory)
        );

        ftmBranchBridgeAgentFactory.initialize(address(coreRootBridgeAgent));
        ftmCoreBridgeAgent = BranchBridgeAgent(
            payable(ftmPort.bridgeAgents(0))
        );

        ftmHTokenFactory.initialize(
            ftmWrappedNativeToken,
            address(ftmCoreRouter)
        );
        ftmLocalWrappedNativeToken = 0x0315E8648695243BCE3Da6a0Ce973867B75Db847;

        ftmCoreRouter.initialize(address(ftmCoreBridgeAgent));

        /////////////////////////////
        //  Add new branch chains  //
        /////////////////////////////

        RootPort(rootPort).addNewChain(
            address(avaxCoreBridgeAgent),
            avaxChainId,
            "Avalanche",
            "AVAX",
            18,
            avaxLocalWrappedNativeToken,
            avaxWrappedNativeToken
        );

        RootPort(rootPort).addNewChain(
            address(ftmCoreBridgeAgent),
            ftmChainId,
            "Fantom Opera",
            "FTM",
            18,
            ftmLocalWrappedNativeToken,
            ftmWrappedNativeToken
        );

        avaxGlobalToken = RootPort(rootPort).getGlobalTokenFromLocal(
            avaxLocalWrappedNativeToken,
            avaxChainId
        );

        ftmGlobalToken = RootPort(rootPort).getGlobalTokenFromLocal(
            ftmLocalWrappedNativeToken,
            ftmChainId
        );

        //////////////////////
        // Verify Addition  //
        //////////////////////

        require(
            RootPort(rootPort).isGlobalAddress(avaxGlobalToken),
            "Token should be added"
        );

        require(
            RootPort(rootPort).getGlobalTokenFromLocal(
                address(avaxLocalWrappedNativeToken),
                avaxChainId
            ) == avaxGlobalToken,
            "Token should be added"
        );

        require(
            RootPort(rootPort).getLocalTokenFromGlobal(
                avaxGlobalToken,
                avaxChainId
            ) == address(avaxLocalWrappedNativeToken),
            "Token should be added"
        );
        require(
            RootPort(rootPort).getUnderlyingTokenFromLocal(
                address(avaxLocalWrappedNativeToken),
                avaxChainId
            ) == address(avaxWrappedNativeToken),
            "Token should be added"
        );

        require(
            RootPort(rootPort).getGlobalTokenFromLocal(
                address(ftmLocalWrappedNativeToken),
                ftmChainId
            ) == ftmGlobalToken,
            "Token should be added"
        );

        require(
            RootPort(rootPort).getLocalTokenFromGlobal(
                ftmGlobalToken,
                ftmChainId
            ) == address(ftmLocalWrappedNativeToken),
            "Token should be added"
        );
        require(
            RootPort(rootPort).getUnderlyingTokenFromLocal(
                address(ftmLocalWrappedNativeToken),
                ftmChainId
            ) == address(ftmWrappedNativeToken),
            "Token should be added"
        );

        ///////////////////////////////////
        //  Approve new Branchs in Root  //
        ///////////////////////////////////

        rootPort.initializeCore(
            address(coreRootBridgeAgent),
            address(arbitrumCoreBridgeAgent),
            address(arbitrumPort)
        );

        multicallBridgeAgent.approveBranchBridgeAgent(rootChainId);

        multicallBridgeAgent.approveBranchBridgeAgent(avaxChainId);

        multicallBridgeAgent.approveBranchBridgeAgent(ftmChainId);

        ///////////////////////////////////////
        //  Add new branches to  Root Agents //
        ///////////////////////////////////////

        hevm.deal(address(this), 3 ether);

        rootCoreRouter.addBranchToBridgeAgent{value: 1 ether}(
            address(multicallBridgeAgent),
            address(avaxBranchBridgeAgentFactory),
            address(avaxCoreRouter),
            address(this),
            avaxChainId,
            [GasParams(0.05 ether, 0.05 ether), GasParams(0.02 ether, 0)]
        );

        rootCoreRouter.addBranchToBridgeAgent{value: 1 ether}(
            address(multicallBridgeAgent),
            address(ftmBranchBridgeAgentFactory),
            address(ftmCoreRouter),
            address(this),
            ftmChainId,
            [GasParams(0.05 ether, 0.05 ether), GasParams(0.02 ether, 0)]
        );

        rootCoreRouter.addBranchToBridgeAgent(
            address(multicallBridgeAgent),
            address(arbitrumBranchBridgeAgentFactory),
            address(arbitrumCoreRouter),
            address(this),
            rootChainId,
            [GasParams(0, 0), GasParams(0, 0)]
        );

        /////////////////////////////////////
        //  Initialize new Branch Routers  //
        /////////////////////////////////////

        arbitrumMulticallBridgeAgent = ArbitrumBranchBridgeAgent(
            payable(arbitrumPort.bridgeAgents(1))
        );
        avaxMulticallBridgeAgent = BranchBridgeAgent(
            payable(avaxPort.bridgeAgents(1))
        );
        ftmMulticallBridgeAgent = BranchBridgeAgent(
            payable(ftmPort.bridgeAgents(1))
        );

        arbitrumMulticallRouter.initialize(
            address(arbitrumMulticallBridgeAgent)
        );
        avaxMulticallRouter.initialize(address(avaxMulticallBridgeAgent));
        ftmMulticallRouter.initialize(address(ftmMulticallBridgeAgent));

        //////////////////////////////////////
        // Deploy Underlying Tokens and Mocks//
        //////////////////////////////////////

        avaxMockAssetToken = new MockERC20("underlying token", "UNDER", 18);

        ftmMockAssetToken = new MockERC20("underlying token", "UNDER", 18);

        ///on Avax, add local token.
        hevm.deal(address(this), 1 ether);

        avaxCoreRouter.addLocalToken{value: 0.1 ether}(
            address(avaxMockAssetToken),
            GasParams(0.5 ether, 0.5 ether)
        );

        newAvaxAssetLocalTokenAddress = RootPort(rootPort)
            .getLocalTokenFromUnderlying(
                0x541dC483Eb43cf8F9969baF71BF783193e5C5B1A,
                avaxChainId
            );

        console2.log("New: ", newAvaxAssetLocalTokenAddress);

        newAvaxAssetGlobalAddress = RootPort(rootPort).getGlobalTokenFromLocal(
            address(newAvaxAssetLocalTokenAddress),
            avaxChainId
        );

        GasParams[3] memory gasParams = [
            GasParams(0.05 ether, 0.05 ether),
            GasParams(0.05 ether, 0.0025 ether),
            GasParams(0.002 ether, 0)
        ];

        avaxCoreRouter.addGlobalToken{value: 0.15 ether}(
            newAvaxAssetGlobalAddress,
            ftmChainId,
            gasParams
        );

        newFTMAvaxAssetLocalToken = RootPort(rootPort).getLocalTokenFromGlobal(
            newAvaxAssetGlobalAddress,
            ftmChainId
        );

        console2.log("New Local: ", newFTMAvaxAssetLocalToken);

        require(
            RootPort(rootPort).getLocalTokenFromGlobal(
                newAvaxAssetGlobalAddress,
                ftmChainId
            ) == newFTMAvaxAssetLocalToken,
            "Token should be added"
        );

        require(
            RootPort(rootPort).getUnderlyingTokenFromLocal(
                newFTMAvaxAssetLocalToken,
                ftmChainId
            ) == address(0),
            "Underlying should not be added"
        );
    }

    function testPOC_callOutAndBridgeStuckFunds() public {
        ///@dev mocking call from the perspective of lzReceive on root chain.
        address hToken = newAvaxAssetLocalTokenAddress;
        address underlyingToken = address(avaxMockAssetToken);
        address alice = hevm.addr(0xA11cE);
       ///@notice extra data not passed into payload
        bytes memory payload = abi.encodePacked(
            bytes1(0x02),
            uint32(99),
            hToken,
            underlyingToken,
            uint256(100e18),
            uint256(100e18),
            ""
        );
        console2.log(
            "lz endpoint in Agent: ",
            coreRootBridgeAgent.lzEndpointAddress()
        );
        console2.log("lz endpoint in test: ", lzEndpointAddress);

        uint256 coreRouterBalancePrior = MockERC20(newAvaxAssetGlobalAddress)
            .balanceOf(address(rootCoreRouter));

        uint256 userBalancePrior = MockERC20(newAvaxAssetGlobalAddress)
            .balanceOf(alice);

        console2.log("Alice's balance prior: ", userBalancePrior);
        console2.log("coreRootRouterBalance prior: ", coreRouterBalancePrior);

        hevm.startPrank(lzEndpointAddress);
        coreRootBridgeAgent.lzReceive{gas: 0.5 ether}(
            43114,
            abi.encodePacked(
                payable(coreRootBridgeAgent),
                payable(avaxCoreBridgeAgent)
            ),
            1,
            payload
        );
        hevm.stopPrank();

        ///@dev uint8 internal constant STATUS_DONE = 1;
        ///@notice reference in: https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/interfaces/BridgeAgentConstants.sol#L15

        assertEq(
            coreRootBridgeAgent.executionState(43114, 99),
            1,
            "Nonce not spent?"
        );

        uint256 coreRouterBalanceAfter = MockERC20(newAvaxAssetGlobalAddress)
            .balanceOf(address(rootCoreRouter));

        uint256 userBalanceAfter = MockERC20(newAvaxAssetGlobalAddress)
            .balanceOf(alice);

        console2.log("Alice's balance prior: ", userBalanceAfter);
        console2.log("coreRootRouterBalance prior: ", coreRouterBalanceAfter);

        assertEq(
            userBalanceAfter,
            userBalancePrior,
            "Alice's balance did not change on the root chain"
        );
        assertEq(
            coreRouterBalanceAfter,
            coreRouterBalancePrior + 100e18,
            "The router balance changes by 100e18 tokens"
        );
    }
}

contract MockEndpoint is DSTestPlus {
    uint256 constant rootChain = 42161;

    address public sourceBridgeAgent;
    address public destinationBridgeAgent;
    bytes public data;
    uint32 public nonce;
    bool forceFallback;
    uint256 fallbackCountdown;
    uint256 gasLimit;
    uint256 remoteBranchExecutionGas;
    address receiver;

    constructor() {}

    function toggleFallback(uint256 _fallbackCountdown) external {
        forceFallback = !forceFallback;
        fallbackCountdown = _fallbackCountdown;
    }

    function sendFallback() public {
        console2.log("Mocking fallback...");
        console2.log("sourceBridgeAgent:", sourceBridgeAgent);
        console2.log("destinationBridgeAgent:", destinationBridgeAgent);
        console2.log(
            "srcChainId:",
            BranchBridgeAgent(payable(sourceBridgeAgent)).localChainId()
        );

        hevm.deal(
            address(this),
            (gasLimit + remoteBranchExecutionGas) * tx.gasprice
        );

        bytes memory fallbackData = abi.encodePacked(
            BranchBridgeAgent(payable(sourceBridgeAgent)).localChainId() ==
                rootChain
                ? 0x09
                : 0x04,
            nonce
        );

        // Perform Call
        sourceBridgeAgent.call{value: remoteBranchExecutionGas}("");
        RootBridgeAgent(payable(sourceBridgeAgent)).lzReceive{gas: gasLimit}(
            BranchBridgeAgent(payable(destinationBridgeAgent)).localChainId(),
            abi.encodePacked(sourceBridgeAgent, destinationBridgeAgent),
            1,
            fallbackData
        );
    }

    // @notice send a LayerZero message to the specified address at a LayerZero endpoint.
    // @param _dstChainId - the destination chain identifier
    // @param _destination - the address on destination chain (in bytes). address length/format may vary by chains
    // @param _payload - a custom bytes payload to send to the destination contract
    // @param _refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address
    // @param _zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction
    // @param _adapterParams - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination

    function send(
        uint16 _dstChainId,
        bytes calldata _destination,
        bytes calldata _payload,
        address payable,
        address,
        bytes calldata _adapterParams
    ) external payable {
        sourceBridgeAgent = msg.sender;
        destinationBridgeAgent = address(bytes20(_destination[:20]));
        data = _payload;

        nonce = _dstChainId == uint16(42161)
            ? BranchBridgeAgent(payable(msg.sender)).depositNonce() - 1
            : RootBridgeAgent(payable(msg.sender)).settlementNonce() - 1;

        console2.log("Mocking lzSends...");
        console2.log("sourceBridgeAgent:", msg.sender);
        console2.log("destinationBridgeAgent:", destinationBridgeAgent);
        console2.log(
            "srcChainId:",
            BranchBridgeAgent(payable(msg.sender)).localChainId()
        );

        // Decode adapter params
        if (_adapterParams.length > 0) {
            gasLimit = uint256(bytes32(_adapterParams[0:32]));
            remoteBranchExecutionGas = uint256(bytes32(_adapterParams[32:64]));
            receiver = address(bytes20(_adapterParams[64:84]));
        } else {
            gasLimit = 200_000;
            remoteBranchExecutionGas = 0;
            receiver = address(0);
        }

        if (!forceFallback) {
            // Perform Call
            destinationBridgeAgent.call{value: remoteBranchExecutionGas}("");
            RootBridgeAgent(payable(destinationBridgeAgent)).lzReceive{
                gas: gasLimit
            }(
                BranchBridgeAgent(payable(msg.sender)).localChainId(),
                _destination,
                1,
                data
            );
        } else if (fallbackCountdown > 0) {
            console2.log("Execute LayerZero request...", fallbackCountdown--);
            // Perform Call
            destinationBridgeAgent.call{value: remoteBranchExecutionGas}("");
            RootBridgeAgent(payable(destinationBridgeAgent)).lzReceive{
                gas: gasLimit
            }(
                BranchBridgeAgent(payable(msg.sender)).localChainId(),
                _destination,
                1,
                data
            );
        }
    }
}

Tools Used

Foundry

Recommended Mitigation Steps

In the case where no extra data is passed into the payload to be used on the root chain to forward the tokens to the intended destination, the transaction should be reverted, allowing the user to retrieve their deposit on the branch chain they're bridging from.

Assessed type

Token-Transfer

c4-pre-sort commented 1 year ago

0xA5DF marked the issue as duplicate of #898

c4-pre-sort commented 1 year ago

0xA5DF marked the issue as sufficient quality report

c4-judge commented 1 year ago

alcueca marked the issue as duplicate of #685

c4-judge commented 1 year ago

alcueca changed the severity to QA (Quality Assurance)

alcueca commented 1 year ago

The Router is not expected to hold funds, and callers of unsigned functions should know that. They are minted in the Router to be immediately used. If they make an error and leave their tokens in the Router, then it not expected that they will be protected.

c4-judge commented 1 year ago

alcueca marked the issue as grade-b