diff --git a/conventions.md b/conventions.md index f73ea70a2..0e7c88ae2 100644 --- a/conventions.md +++ b/conventions.md @@ -201,10 +201,10 @@ Follow the folder structure to locate resources and generate or modify code in a ); } ``` - - When a non-EVM address is used, the `bridgeData.receiver` must contain `LibAsset.NON_EVM_ADDRESS`, ensuring proper handling: + - When a non-EVM address is used, the `bridgeData.receiver` must contain `NON_EVM_ADDRESS` (found in src/Helpers/LiFiData.sol), ensuring proper handling: ``` if ( - bridgeData.receiver != LibAsset.NON_EVM_ADDRESS + bridgeData.receiver != NON_EVM_ADDRESS ) { revert InvalidCallData(); } diff --git a/docs/ChainflipFacet.md b/docs/ChainflipFacet.md index b76a60dbe..f2e17bfd5 100644 --- a/docs/ChainflipFacet.md +++ b/docs/ChainflipFacet.md @@ -32,7 +32,7 @@ struct ChainflipData { } ``` -For non-EVM destinations (i.e. Solana, Bitcoin), set the `receiver` in `BridgeData` to `LibAsset.NON_EVM_ADDRESS` and provide the destination address in `nonEVMReceiver`. +For non-EVM destinations (i.e. Solana, Bitcoin), set the `receiver` in `BridgeData` to `NON_EVM_ADDRESS` (inherit from src/Helpers/LiFiData.sol) and provide the destination address in `nonEVMReceiver`. ## Supported Chains diff --git a/src/Errors/GenericErrors.sol b/src/Errors/GenericErrors.sol index 5d373dec3..6a5d5ebce 100644 --- a/src/Errors/GenericErrors.sol +++ b/src/Errors/GenericErrors.sol @@ -19,6 +19,7 @@ error InvalidConfig(); error InvalidContract(); error InvalidDestinationChain(); error InvalidFallbackAddress(); +error InvalidNonEVMReceiver(); error InvalidReceiver(); error InvalidSendingToken(); error NativeAssetNotSupported(); diff --git a/src/Facets/AllBridgeFacet.sol b/src/Facets/AllBridgeFacet.sol index f7006e451..d17fcb32e 100644 --- a/src/Facets/AllBridgeFacet.sol +++ b/src/Facets/AllBridgeFacet.sol @@ -8,27 +8,55 @@ import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { LibSwap } from "../Libraries/LibSwap.sol"; +import { InvalidConfig, InvalidCallData, InvalidNonEVMReceiver, InvalidReceiver } from "../Errors/GenericErrors.sol"; +import { LiFiData } from "../Helpers/LiFiData.sol"; /// @title Allbridge Facet -/// @author Li.Finance (https://li.finance) +/// @author LI.FI (https://li.fi) /// @notice Provides functionality for bridging through AllBridge -/// @custom:version 2.0.0 -contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { +/// @custom:version 2.1.0 +contract AllBridgeFacet is + ILiFi, + ReentrancyGuard, + SwapperV2, + Validatable, + LiFiData +{ + uint32 private constant ALLBRIDGE_ID_ETHEREUM = 1; + uint32 private constant ALLBRIDGE_ID_BSC = 2; + uint32 private constant ALLBRIDGE_ID_TRON = 3; + uint32 private constant ALLBRIDGE_ID_SOLANA = 4; + uint32 private constant ALLBRIDGE_ID_POLYGON = 5; + uint32 private constant ALLBRIDGE_ID_ARBITRUM = 6; + uint32 private constant ALLBRIDGE_ID_AVALANCHE = 8; + uint32 private constant ALLBRIDGE_ID_BASE = 9; + uint32 private constant ALLBRIDGE_ID_OPTIMISM = 10; + uint32 private constant ALLBRIDGE_ID_CELO = 11; + uint32 private constant ALLBRIDGE_ID_SUI = 13; + uint256 internal constant LIFI_CHAIN_ID_ARBITRUM = 42161; + uint256 internal constant LIFI_CHAIN_ID_AVALANCHE = 43114; + uint256 internal constant LIFI_CHAIN_ID_BASE = 8453; + uint256 internal constant LIFI_CHAIN_ID_BSC = 56; + uint256 internal constant LIFI_CHAIN_ID_CELO = 42220; + uint256 internal constant LIFI_CHAIN_ID_POLYGON = 137; + + error UnsupportedAllBridgeChainId(); + /// @notice The contract address of the AllBridge router on the source chain. // solhint-disable-next-line immutable-vars-naming - IAllBridge private immutable allBridge; + IAllBridge private immutable ALLBRIDGE; /// @notice The struct for the AllBridge data. - /// @param fees The amount of token to pay the messenger and the bridge /// @param recipient The address of the token receiver after bridging. + /// @param fees The amount of token to pay the messenger and the bridge /// @param destinationChainId The destination chain id. /// @param receiveToken The token to receive on the destination chain. /// @param nonce A random nonce to associate with the tx. /// @param messenger The messenger protocol enum /// @param payFeeWithSendingAsset Whether to pay the relayer fee with the sending asset or not struct AllBridgeData { - uint256 fees; bytes32 recipient; + uint256 fees; uint256 destinationChainId; bytes32 receiveToken; uint256 nonce; @@ -39,7 +67,9 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @notice Initializes the AllBridge contract /// @param _allBridge The address of the AllBridge contract constructor(IAllBridge _allBridge) { - allBridge = _allBridge; + if (address(_allBridge) == address(0)) revert InvalidConfig(); + + ALLBRIDGE = _allBridge; } /// @notice Bridge tokens to another chain via AllBridge @@ -96,14 +126,38 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { ILiFi.BridgeData memory _bridgeData, AllBridgeData calldata _allBridgeData ) internal { + // make sure destinationChainId matches in bridgeData and allBridgeData + if ( + _allBridgeData.destinationChainId != + _getAllBridgeChainId(_bridgeData.destinationChainId) + ) revert InvalidCallData(); + + // validate receiver address + if (_bridgeData.receiver == NON_EVM_ADDRESS) { + // destination chain is non-EVM + // make sure it's non-zero (we cannot validate further) + if (_allBridgeData.recipient == bytes32(0)) + revert InvalidNonEVMReceiver(); + } else { + // destination chain is EVM + // make sure that bridgeData and allBridgeData receiver addresses match + if ( + _bridgeData.receiver != + address(uint160(uint256(_allBridgeData.recipient))) + ) revert InvalidReceiver(); + } + + // set max approval to allBridge, if current allowance is insufficient LibAsset.maxApproveERC20( IERC20(_bridgeData.sendingAssetId), - address(allBridge), + address(ALLBRIDGE), _bridgeData.minAmount ); + // check if bridge fee should be paid with sending or native asset if (_allBridgeData.payFeeWithSendingAsset) { - allBridge.swapAndBridge( + // pay fee with sending asset + ALLBRIDGE.swapAndBridge( bytes32(uint256(uint160(_bridgeData.sendingAssetId))), _bridgeData.minAmount, _allBridgeData.recipient, @@ -114,7 +168,8 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { _allBridgeData.fees ); } else { - allBridge.swapAndBridge{ value: _allBridgeData.fees }( + // pay fee with native asset + ALLBRIDGE.swapAndBridge{ value: _allBridgeData.fees }( bytes32(uint256(uint160(_bridgeData.sendingAssetId))), _bridgeData.minAmount, _allBridgeData.recipient, @@ -128,4 +183,41 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { emit LiFiTransferStarted(_bridgeData); } + + /// @notice Converts LiFi internal chain IDs to AllBridge chain IDs + /// @param _destinationChainId The LiFi chain ID to convert + /// @return The corresponding Chainflip chain ID + /// @dev Reverts if the destination chain is not supported + function _getAllBridgeChainId( + uint256 _destinationChainId + ) internal pure returns (uint256) { + // split possible values in half for more efficient search/matching + + // first try to match cases where chainId is the same and does not need to be mapped + if ( + _destinationChainId == ALLBRIDGE_ID_ETHEREUM || + _destinationChainId == ALLBRIDGE_ID_OPTIMISM + ) return _destinationChainId; + // all others have custom chainIds + else if (_destinationChainId == LIFI_CHAIN_ID_BSC) + return ALLBRIDGE_ID_BSC; + else if (_destinationChainId == LIFI_CHAIN_ID_TRON) + return ALLBRIDGE_ID_TRON; + else if (_destinationChainId == LIFI_CHAIN_ID_SOLANA) + return ALLBRIDGE_ID_SOLANA; + else if (_destinationChainId == LIFI_CHAIN_ID_POLYGON) + return ALLBRIDGE_ID_POLYGON; + else if (_destinationChainId == LIFI_CHAIN_ID_ARBITRUM) + return ALLBRIDGE_ID_ARBITRUM; + else if (_destinationChainId == LIFI_CHAIN_ID_AVALANCHE) + return ALLBRIDGE_ID_AVALANCHE; + else if (_destinationChainId == LIFI_CHAIN_ID_BASE) + return ALLBRIDGE_ID_BASE; + else if (_destinationChainId == LIFI_CHAIN_ID_CELO) + return ALLBRIDGE_ID_CELO; + else if (_destinationChainId == LIFI_CHAIN_ID_SUI) + return ALLBRIDGE_ID_SUI; + // revert if no match found + else revert UnsupportedAllBridgeChainId(); + } } diff --git a/src/Facets/ChainflipFacet.sol b/src/Facets/ChainflipFacet.sol index 6e52feda8..6e7f62438 100644 --- a/src/Facets/ChainflipFacet.sol +++ b/src/Facets/ChainflipFacet.sol @@ -10,12 +10,19 @@ import { Validatable } from "../Helpers/Validatable.sol"; import { IChainflipVault } from "../Interfaces/IChainflip.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { InformationMismatch, InvalidConfig, InvalidReceiver } from "../Errors/GenericErrors.sol"; +import { LiFiData } from "../Helpers/LiFiData.sol"; -/// @title Chainflip Facet +/// @title ChainflipFacet /// @author LI.FI (https://li.fi) /// @notice Allows bridging assets via Chainflip -/// @custom:version 1.0.0 -contract ChainflipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { +/// @custom:version 1.0.1 +contract ChainflipFacet is + ILiFi, + ReentrancyGuard, + SwapperV2, + Validatable, + LiFiData +{ /// Events /// event BridgeToNonEVMChain( bytes32 indexed transactionId, @@ -134,7 +141,7 @@ contract ChainflipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { // Handle address encoding based on destination chain type bytes memory encodedDstAddress; - if (_bridgeData.receiver == LibAsset.NON_EVM_ADDRESS) { + if (_bridgeData.receiver == NON_EVM_ADDRESS) { if (_chainflipData.nonEVMReceiver.length == 0) { revert EmptyNonEvmAddress(); } diff --git a/src/Facets/RelayFacet.sol b/src/Facets/RelayFacet.sol index 8e08ec3b0..c9c99c9b2 100644 --- a/src/Facets/RelayFacet.sol +++ b/src/Facets/RelayFacet.sol @@ -9,12 +9,20 @@ import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { ECDSA } from "solady/utils/ECDSA.sol"; +import { LiFiData } from "../Helpers/LiFiData.sol"; +import { InvalidConfig } from "../Errors/GenericErrors.sol"; -/// @title Relay Facet +/// @title RelayFacet /// @author LI.FI (https://li.fi) /// @notice Provides functionality for bridging through Relay Protocol -/// @custom:version 1.0.0 -contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { +/// @custom:version 1.0.1 +contract RelayFacet is + ILiFi, + ReentrancyGuard, + SwapperV2, + Validatable, + LiFiData +{ // Receiver for native transfers // solhint-disable-next-line immutable-vars-naming address public immutable relayReceiver; @@ -67,7 +75,7 @@ contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { // Ensure nonEVMAddress is not empty if ( - _bridgeData.receiver == LibAsset.NON_EVM_ADDRESS && + _bridgeData.receiver == NON_EVM_ADDRESS && _relayData.nonEVMReceiver == bytes32(0) ) { revert InvalidQuote(); @@ -84,7 +92,7 @@ contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { bytes32(uint256(uint160(address(this)))), bytes32(uint256(uint160(_bridgeData.sendingAssetId))), _getMappedChainId(_bridgeData.destinationChainId), - _bridgeData.receiver == LibAsset.NON_EVM_ADDRESS + _bridgeData.receiver == NON_EVM_ADDRESS ? _relayData.nonEVMReceiver : bytes32(uint256(uint160(_bridgeData.receiver))), _relayData.receivingAssetId @@ -103,6 +111,10 @@ contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @param _relayReceiver The receiver for native transfers /// @param _relaySolver The relayer wallet for ERC20 transfers constructor(address _relayReceiver, address _relaySolver) { + if (_relayReceiver == address(0) || _relaySolver == address(0)) { + revert InvalidConfig(); + } + relayReceiver = _relayReceiver; relaySolver = _relaySolver; } @@ -203,7 +215,7 @@ contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { consumedIds[_relayData.requestId] = true; // Emit special event if bridging to non-EVM chain - if (_bridgeData.receiver == LibAsset.NON_EVM_ADDRESS) { + if (_bridgeData.receiver == NON_EVM_ADDRESS) { emit BridgeToNonEVMChain( _bridgeData.transactionId, _getMappedChainId(_bridgeData.destinationChainId), diff --git a/src/Helpers/LiFiData.sol b/src/Helpers/LiFiData.sol new file mode 100644 index 000000000..59e025d89 --- /dev/null +++ b/src/Helpers/LiFiData.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +/// @title LiFiData +/// @author LI.FI (https://li.fi) +/// @notice A storage for LI.FI-internal config data (addresses, chainIDs, etc.) +/// @custom:version 1.0.0 +contract LiFiData { + address internal constant NON_EVM_ADDRESS = + 0x11f111f111f111F111f111f111F111f111f111F1; + + // LI.FI non-EVM Custom Chain IDs (IDs are made up by the LI.FI team) + uint256 internal constant LIFI_CHAIN_ID_APTOS = 9271000000000010; + uint256 internal constant LIFI_CHAIN_ID_BCH = 20000000000002; + uint256 internal constant LIFI_CHAIN_ID_BTC = 20000000000001; + uint256 internal constant LIFI_CHAIN_ID_DGE = 20000000000004; + uint256 internal constant LIFI_CHAIN_ID_LTC = 20000000000003; + uint256 internal constant LIFI_CHAIN_ID_SOLANA = 1151111081099710; + uint256 internal constant LIFI_CHAIN_ID_SUI = 9270000000000000; + uint256 internal constant LIFI_CHAIN_ID_TRON = 1885080386571452; +} diff --git a/src/Helpers/SwapperV2.sol b/src/Helpers/SwapperV2.sol index 66e60aa3d..70e12fff2 100644 --- a/src/Helpers/SwapperV2.sol +++ b/src/Helpers/SwapperV2.sol @@ -1,5 +1,4 @@ // SPDX-License-Identifier: LGPL-3.0-only -/// @custom:version 1.0.0 pragma solidity ^0.8.17; import { ILiFi } from "../Interfaces/ILiFi.sol"; @@ -8,9 +7,10 @@ import { LibAsset } from "../Libraries/LibAsset.sol"; import { LibAllowList } from "../Libraries/LibAllowList.sol"; import { ContractCallNotAllowed, NoSwapDataProvided, CumulativeSlippageTooHigh } from "../Errors/GenericErrors.sol"; -/// @title Swapper +/// @title SwapperV2 /// @author LI.FI (https://li.fi) /// @notice Abstract contract to provide swap functionality +/// @custom:version 1.0.1 contract SwapperV2 is ILiFi { /// Types /// @@ -119,7 +119,7 @@ contract SwapperV2 is ILiFi { if (finalBalance > initialBalance) { LibAsset.transferAsset( - LibAsset.NATIVE_ASSETID, + LibAsset.NULL_ADDRESS, _refundReceiver, finalBalance - initialBalance ); diff --git a/src/Libraries/LibAsset.sol b/src/Libraries/LibAsset.sol index 59fd20570..b3f2c6608 100644 --- a/src/Libraries/LibAsset.sol +++ b/src/Libraries/LibAsset.sol @@ -7,7 +7,7 @@ import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { InvalidReceiver, NullAddrIsNotAValidSpender, InvalidAmount, NullAddrIsNotAnERC20Token } from "../Errors/GenericErrors.sol"; /// @title LibAsset -/// @custom:version 2.1.0 +/// @custom:version 2.1.1 /// @notice This library contains helpers for dealing with onchain transfers /// of assets, including accounting for the native asset `assetId` /// conventions and any noncompliant ERC20 transfers @@ -15,15 +15,9 @@ library LibAsset { using SafeTransferLib for address; using SafeTransferLib for address payable; - address internal constant NULL_ADDRESS = address(0); - - address internal constant NON_EVM_ADDRESS = - 0x11f111f111f111F111f111f111F111f111f111F1; - /// @dev All native assets use the empty address for their asset id /// by convention - - address internal constant NATIVE_ASSETID = NULL_ADDRESS; + address internal constant NULL_ADDRESS = address(0); /// @dev EIP-7702 delegation designator prefix for Account Abstraction bytes3 internal constant DELEGATION_DESIGNATOR = 0xef0100; @@ -191,7 +185,7 @@ library LibAsset { /// @param assetId The asset identifier to evaluate /// @return Boolean indicating if the asset is the native asset function isNativeAsset(address assetId) internal pure returns (bool) { - return assetId == NATIVE_ASSETID; + return assetId == NULL_ADDRESS; } /// @notice Checks if the given address is a contract diff --git a/src/Periphery/ReceiverChainflip.sol b/src/Periphery/ReceiverChainflip.sol index 428ac0561..fa47fca9a 100644 --- a/src/Periphery/ReceiverChainflip.sol +++ b/src/Periphery/ReceiverChainflip.sol @@ -12,7 +12,7 @@ import { InvalidConfig } from "../Errors/GenericErrors.sol"; /// @title ReceiverChainflip /// @author LI.FI (https://li.fi) /// @notice Receiver contract for Chainflip cross-chain swaps and message passing -/// @custom:version 1.0.0 +/// @custom:version 1.0.1 contract ReceiverChainflip is ILiFi, WithdrawablePeriphery { using SafeTransferLib for address; using SafeTransferLib for address payable; @@ -114,7 +114,7 @@ contract ReceiverChainflip is ILiFi, WithdrawablePeriphery { ) private { // Group address conversion and store in memory to avoid multiple storage reads address actualAssetId = assetId == CHAINFLIP_NATIVE_ADDRESS - ? LibAsset.NATIVE_ASSETID + ? LibAsset.NULL_ADDRESS : assetId; bool isNative = LibAsset.isNativeAsset(actualAssetId); diff --git a/test/solidity/Facets/AllBridgeFacet.t.sol b/test/solidity/Facets/AllBridgeFacet.t.sol index 01aec8130..fcc9c29bf 100644 --- a/test/solidity/Facets/AllBridgeFacet.t.sol +++ b/test/solidity/Facets/AllBridgeFacet.t.sol @@ -5,6 +5,8 @@ import { TestBaseFacet } from "../utils/TestBaseFacet.sol"; import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; import { AllBridgeFacet } from "lifi/Facets/AllBridgeFacet.sol"; import { IAllBridge } from "lifi/Interfaces/IAllBridge.sol"; +import { InvalidConfig, InvalidCallData, InvalidNonEVMReceiver, InvalidReceiver } from "lifi/Errors/GenericErrors.sol"; +import { LiFiData } from "lifi/Helpers/LiFiData.sol"; // Stub AllBridgeFacet Contract contract TestAllBridgeFacet is AllBridgeFacet { @@ -17,13 +19,41 @@ contract TestAllBridgeFacet is AllBridgeFacet { function setFunctionApprovalBySignature(bytes4 _signature) external { LibAllowList.addAllowedSelector(_signature); } + + function getAllBridgeChainId( + uint256 _chainId + ) public pure returns (uint256) { + return _getAllBridgeChainId(_chainId); + } } -contract AllBridgeFacetTest is TestBaseFacet { +contract AllBridgeFacetTest is TestBaseFacet, LiFiData { IAllBridge internal constant ALLBRIDGE_ROUTER = IAllBridge(0x609c690e8F7D68a59885c9132e812eEbDaAf0c9e); address internal constant ALLBRIDGE_POOL = 0xa7062bbA94c91d565Ae33B893Ab5dFAF1Fc57C4d; + uint32 private constant ALLBRIDGE_ID_ETHEREUM = 1; + uint32 private constant ALLBRIDGE_ID_BSC = 2; + uint32 private constant ALLBRIDGE_ID_TRON = 3; + uint32 private constant ALLBRIDGE_ID_SOLANA = 4; + uint32 private constant ALLBRIDGE_ID_POLYGON = 5; + uint32 private constant ALLBRIDGE_ID_ARBITRUM = 6; + uint32 private constant ALLBRIDGE_ID_AVALANCHE = 8; + uint32 private constant ALLBRIDGE_ID_BASE = 9; + uint32 private constant ALLBRIDGE_ID_OPTIMISM = 10; + uint32 private constant ALLBRIDGE_ID_CELO = 11; + uint32 private constant ALLBRIDGE_ID_SUI = 13; + uint256 internal constant LIFI_CHAIN_ID_ETHEREUM = 1; + uint256 internal constant LIFI_CHAIN_ID_ARBITRUM = 42161; + uint256 internal constant LIFI_CHAIN_ID_AVALANCHE = 43114; + uint256 internal constant LIFI_CHAIN_ID_BASE = 8453; + uint256 internal constant LIFI_CHAIN_ID_BSC = 56; + uint256 internal constant LIFI_CHAIN_ID_CELO = 42220; + uint256 internal constant LIFI_CHAIN_ID_OPTIMISM = 10; + uint256 internal constant LIFI_CHAIN_ID_POLYGON = 137; + + error UnsupportedAllBridgeChainId(); + // ----- AllBridgeFacet.AllBridgeData internal validAllBridgeData; TestAllBridgeFacet internal allBridgeFacet; @@ -33,7 +63,7 @@ contract AllBridgeFacetTest is TestBaseFacet { initTestBase(); allBridgeFacet = new TestAllBridgeFacet(ALLBRIDGE_ROUTER); - bytes4[] memory functionSelectors = new bytes4[](4); + bytes4[] memory functionSelectors = new bytes4[](5); functionSelectors[0] = allBridgeFacet .startBridgeTokensViaAllBridge .selector; @@ -44,6 +74,7 @@ contract AllBridgeFacetTest is TestBaseFacet { functionSelectors[3] = allBridgeFacet .setFunctionApprovalBySignature .selector; + functionSelectors[4] = allBridgeFacet.getAllBridgeChainId.selector; addFacet(diamond, address(allBridgeFacet), functionSelectors); allBridgeFacet = TestAllBridgeFacet(address(diamond)); @@ -72,7 +103,7 @@ contract AllBridgeFacetTest is TestBaseFacet { // produce valid AllBridgeData validAllBridgeData = AllBridgeFacet.AllBridgeData({ fees: fees, - recipient: 0x00000000000000000000000012561cc3ea2a60aa158b0421010859a983bf3c96, + recipient: bytes32(uint256(uint160(USER_RECEIVER))), destinationChainId: 5, receiveToken: 0x0000000000000000000000002791Bca1f2de4661ED88A30C99A7a9449Aa84174, nonce: 40953790744158426077674476975877556494233328003707004662889959804198145032447, @@ -88,6 +119,12 @@ contract AllBridgeFacetTest is TestBaseFacet { }(bridgeData, validAllBridgeData); } + function initiateSwapAndBridgeTxWithFacet(bool) internal override { + allBridgeFacet.swapAndStartBridgeTokensViaAllBridge{ + value: addToMessageValue + }(bridgeData, swapData, validAllBridgeData); + } + function testBase_CanBridgeNativeTokens() public override { // facet does not support bridging of native assets } @@ -100,10 +137,10 @@ contract AllBridgeFacetTest is TestBaseFacet { // we decided to deactivate this test since it often fails on Github } - function initiateSwapAndBridgeTxWithFacet(bool) internal override { - allBridgeFacet.swapAndStartBridgeTokensViaAllBridge{ - value: addToMessageValue - }(bridgeData, swapData, validAllBridgeData); + function testRevert_WhenConstructedWithZeroAddress() public { + vm.expectRevert(InvalidConfig.selector); + + new TestAllBridgeFacet(IAllBridge(address(0))); } function test_CanBridgeAndPayFeeWithBridgedToken() public { @@ -168,4 +205,114 @@ contract AllBridgeFacetTest is TestBaseFacet { // execute call in child contract initiateSwapAndBridgeTxWithFacet(false); } + + function testRevert_WhenDestinationChainIdDoesNotMatch() public { + vm.startPrank(USER_SENDER); + + // update bridgeData + validAllBridgeData.destinationChainId = 10; // optimism + + usdc.approve(address(allBridgeFacet), bridgeData.minAmount); + + vm.expectRevert(InvalidCallData.selector); + + allBridgeFacet.startBridgeTokensViaAllBridge( + bridgeData, + validAllBridgeData + ); + } + + function testRevert_WhenReceiverDoesNotMatch() public { + vm.startPrank(USER_SENDER); + + // update bridgeData + validAllBridgeData.recipient = bytes32(uint256(uint160(USER_SENDER))); + + usdc.approve(address(allBridgeFacet), bridgeData.minAmount); + + vm.expectRevert(InvalidReceiver.selector); + + allBridgeFacet.startBridgeTokensViaAllBridge( + bridgeData, + validAllBridgeData + ); + } + + function testRevert_InvalidNonEVMReceiver() public { + vm.startPrank(USER_SENDER); + + // update bridgeData + bridgeData.receiver = NON_EVM_ADDRESS; + validAllBridgeData.recipient = bytes32(0); + + usdc.approve(address(allBridgeFacet), bridgeData.minAmount); + + vm.expectRevert(InvalidNonEVMReceiver.selector); + + allBridgeFacet.startBridgeTokensViaAllBridge( + bridgeData, + validAllBridgeData + ); + + vm.stopPrank(); + } + + function test_ChainIdMapping() public { + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_ETHEREUM), + ALLBRIDGE_ID_ETHEREUM + ); + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_OPTIMISM), + ALLBRIDGE_ID_OPTIMISM + ); + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_BSC), + ALLBRIDGE_ID_BSC + ); + // tron + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_TRON), + ALLBRIDGE_ID_TRON + ); + // solana + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_SOLANA), + ALLBRIDGE_ID_SOLANA + ); + // polygon + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_POLYGON), + ALLBRIDGE_ID_POLYGON + ); + // arbitrum + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_ARBITRUM), + ALLBRIDGE_ID_ARBITRUM + ); + // avalanche + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_AVALANCHE), + ALLBRIDGE_ID_AVALANCHE + ); + // base + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_BASE), + ALLBRIDGE_ID_BASE + ); + // celo + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_CELO), + ALLBRIDGE_ID_CELO + ); + // sui + assertEq( + allBridgeFacet.getAllBridgeChainId(LIFI_CHAIN_ID_SUI), + ALLBRIDGE_ID_SUI + ); + // unknown + vm.expectRevert(UnsupportedAllBridgeChainId.selector); + + allBridgeFacet.getAllBridgeChainId(1290); + } } diff --git a/test/solidity/Facets/ChainflipFacet.t.sol b/test/solidity/Facets/ChainflipFacet.t.sol index d64331fa2..115b73697 100644 --- a/test/solidity/Facets/ChainflipFacet.t.sol +++ b/test/solidity/Facets/ChainflipFacet.t.sol @@ -5,9 +5,9 @@ import { TestBaseFacet } from "../utils/TestBaseFacet.sol"; import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; import { ChainflipFacet } from "lifi/Facets/ChainflipFacet.sol"; import { IChainflipVault } from "lifi/Interfaces/IChainflip.sol"; -import { LibAsset } from "lifi/Libraries/LibAsset.sol"; import { LibSwap } from "lifi/Libraries/LibSwap.sol"; import { InformationMismatch, CannotBridgeToSameNetwork, InvalidConfig, InvalidReceiver } from "lifi/Errors/GenericErrors.sol"; +import { LiFiData } from "lifi/Helpers/LiFiData.sol"; // Stub ChainflipFacet Contract contract TestChainflipFacet is ChainflipFacet { @@ -24,7 +24,7 @@ contract TestChainflipFacet is ChainflipFacet { } } -contract ChainflipFacetTest is TestBaseFacet { +contract ChainflipFacetTest is TestBaseFacet, LiFiData { ChainflipFacet.ChainflipData internal validChainflipData; TestChainflipFacet internal chainflipFacet; address internal chainflipVault; @@ -124,7 +124,7 @@ contract ChainflipFacetTest is TestBaseFacet { -int256(defaultUSDCAmount) ) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = CHAIN_ID_SOLANA; validChainflipData.dstToken = 6; validChainflipData.nonEVMReceiver = bytes( @@ -153,7 +153,7 @@ contract ChainflipFacetTest is TestBaseFacet { ) assertBalanceChange(ADDRESS_DAI, USER_SENDER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = CHAIN_ID_BITCOIN; validChainflipData.dstToken = 6; validChainflipData.nonEVMReceiver = bytes( @@ -406,7 +406,7 @@ contract ChainflipFacetTest is TestBaseFacet { // Test Solana mapping usdc.approve(_facetTestContractAddress, bridgeData.minAmount); bridgeData.destinationChainId = CHAIN_ID_SOLANA; - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; validChainflipData.nonEVMReceiver = bytes( "EoW7FWTdPdZKpd3WAhH98c2HMGHsdh5yhzzEtk1u68Bb" ); @@ -430,7 +430,7 @@ contract ChainflipFacetTest is TestBaseFacet { } function testRevert_WhenUsingEmptyNonEVMAddress() public { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = CHAIN_ID_SOLANA; validChainflipData.dstToken = 6; validChainflipData.nonEVMReceiver = bytes(""); // Empty address should fail diff --git a/test/solidity/Facets/RelayFacet.t.sol b/test/solidity/Facets/RelayFacet.t.sol index 773763478..50415e792 100644 --- a/test/solidity/Facets/RelayFacet.t.sol +++ b/test/solidity/Facets/RelayFacet.t.sol @@ -3,9 +3,10 @@ pragma solidity ^0.8.17; import { TestBaseFacet, LibSwap } from "../utils/TestBaseFacet.sol"; import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; -import { LibAsset } from "lifi/Libraries/LibAsset.sol"; import { RelayFacet } from "lifi/Facets/RelayFacet.sol"; import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; +import { LiFiData } from "lifi/Helpers/LiFiData.sol"; +import { InvalidConfig } from "lifi/Errors/GenericErrors.sol"; contract Reverter { error AlwaysReverts(); @@ -41,7 +42,7 @@ contract TestRelayFacet is RelayFacet { } } -contract RelayFacetTest is TestBaseFacet { +contract RelayFacetTest is TestBaseFacet, LiFiData { RelayFacet.RelayData internal validRelayData; TestRelayFacet internal relayFacet; address internal constant RELAY_RECEIVER = @@ -124,6 +125,17 @@ contract RelayFacetTest is TestBaseFacet { } } + function testRevert_WhenUsingInvalidConfig() public { + // invalid relay solver + vm.expectRevert(InvalidConfig.selector); + new RelayFacet(RELAY_RECEIVER, address(0)); + + // invalid relay receiver + vm.expectRevert(InvalidConfig.selector); + + new RelayFacet(RELAY_RECEIVER, address(0)); + } + function test_CanDeployFacet() public virtual { new RelayFacet(RELAY_RECEIVER, relaySolver); } @@ -153,7 +165,7 @@ contract RelayFacetTest is TestBaseFacet { assertBalanceChange(ADDRESS_DAI, USER_SENDER, 0) assertBalanceChange(ADDRESS_DAI, USER_RECEIVER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 1151111081099710; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -184,7 +196,7 @@ contract RelayFacetTest is TestBaseFacet { } function testRevert_WhenUsingEmptyNonEVMAddress() public virtual { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 1151111081099710; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -209,7 +221,7 @@ contract RelayFacetTest is TestBaseFacet { function testRevert_WhenReplayingTransactionIds() public virtual { relayFacet.setConsumedId(validRelayData.requestId); - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 1151111081099710; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -248,7 +260,7 @@ contract RelayFacetTest is TestBaseFacet { assertBalanceChange(ADDRESS_USDC, USER_SENDER, 0) assertBalanceChange(ADDRESS_DAI, USER_SENDER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 1151111081099710; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -291,7 +303,7 @@ contract RelayFacetTest is TestBaseFacet { assertBalanceChange(ADDRESS_USDC, USER_SENDER, 0) assertBalanceChange(ADDRESS_USDC, USER_RECEIVER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 1151111081099710; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -344,7 +356,7 @@ contract RelayFacetTest is TestBaseFacet { assertBalanceChange(ADDRESS_DAI, USER_RECEIVER, 0) assertBalanceChange(ADDRESS_USDC, USER_RECEIVER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 1151111081099710; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -445,7 +457,7 @@ contract RelayFacetTest is TestBaseFacet { assertBalanceChange(ADDRESS_DAI, USER_SENDER, 0) assertBalanceChange(ADDRESS_DAI, USER_RECEIVER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 20000000000001; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -483,7 +495,7 @@ contract RelayFacetTest is TestBaseFacet { assertBalanceChange(ADDRESS_USDC, USER_SENDER, 0) assertBalanceChange(ADDRESS_DAI, USER_SENDER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 20000000000001; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -522,7 +534,7 @@ contract RelayFacetTest is TestBaseFacet { assertBalanceChange(ADDRESS_USDC, USER_SENDER, 0) assertBalanceChange(ADDRESS_USDC, USER_RECEIVER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 20000000000001; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -572,7 +584,7 @@ contract RelayFacetTest is TestBaseFacet { assertBalanceChange(ADDRESS_DAI, USER_RECEIVER, 0) assertBalanceChange(ADDRESS_USDC, USER_RECEIVER, 0) { - bridgeData.receiver = LibAsset.NON_EVM_ADDRESS; + bridgeData.receiver = NON_EVM_ADDRESS; bridgeData.destinationChainId = 20000000000001; validRelayData = RelayFacet.RelayData({ requestId: bytes32("1234"), @@ -740,7 +752,7 @@ contract RelayFacetTest is TestBaseFacet { bytes32(uint256(uint160(address(relayFacet)))), bytes32(uint256(uint160(_bridgeData.sendingAssetId))), _getMappedChainId(_bridgeData.destinationChainId), - _bridgeData.receiver == LibAsset.NON_EVM_ADDRESS + _bridgeData.receiver == NON_EVM_ADDRESS ? _relayData.nonEVMReceiver : bytes32(uint256(uint160(_bridgeData.receiver))), _relayData.receivingAssetId diff --git a/test/solidity/Periphery/LidoWrapper/LidoWrapper.t.sol b/test/solidity/Periphery/LidoWrapper/LidoWrapper.t.sol index fd533cdec..56cb2e768 100644 --- a/test/solidity/Periphery/LidoWrapper/LidoWrapper.t.sol +++ b/test/solidity/Periphery/LidoWrapper/LidoWrapper.t.sol @@ -12,6 +12,7 @@ import { LibAsset } from "lifi/Libraries/LibAsset.sol"; import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; import { GenericSwapFacetV3 } from "lifi/Facets/GenericSwapFacetV3.sol"; import { InvalidConfig, InvalidAmount } from "lifi/Errors/GenericErrors.sol"; +import { LiFiData } from "lifi/Helpers/LiFiData.sol"; // Custom errors from stETH contract error ErrorZeroSharesWrap(); @@ -20,7 +21,7 @@ error ErrorZeroSharesUnwrap(); error ErrorNotEnoughBalance(); error ErrorNotEnoughAllowance(); -contract LidoWrapperTest is TestBase { +contract LidoWrapperTest is TestBase, LiFiData { LidoWrapper private lidoWrapper; address private constant ST_ETH_ADDRESS_OPTIMISM = 0x76A50b8c7349cCDDb7578c6627e79b5d99D24138; @@ -479,7 +480,7 @@ contract LidoWrapperTest is TestBase { bytes32(uint256(uint160(address(relayFacet)))), bytes32(uint256(uint160(_bridgeData.sendingAssetId))), _bridgeData.destinationChainId, - _bridgeData.receiver == LibAsset.NON_EVM_ADDRESS + _bridgeData.receiver == NON_EVM_ADDRESS ? _relayData.nonEVMReceiver : bytes32(uint256(uint160(_bridgeData.receiver))), _relayData.receivingAssetId diff --git a/test/solidity/Periphery/ReceiverChainflip.t.sol b/test/solidity/Periphery/ReceiverChainflip.t.sol index f721f93e4..72825fc30 100644 --- a/test/solidity/Periphery/ReceiverChainflip.t.sol +++ b/test/solidity/Periphery/ReceiverChainflip.t.sol @@ -208,7 +208,7 @@ contract ReceiverChainflipTest is TestBase { swapData[0] = LibSwap.SwapData({ callTo: address(mockDEX), approveTo: address(mockDEX), - sendingAssetId: LibAsset.NATIVE_ASSETID, + sendingAssetId: LibAsset.NULL_ADDRESS, receivingAssetId: ADDRESS_DAI, fromAmount: 1 ether, callData: abi.encodeWithSelector( @@ -227,7 +227,7 @@ contract ReceiverChainflipTest is TestBase { vm.expectEmit(true, true, true, true, address(receiver)); emit LiFiTransferRecovered( guid, - LibAsset.NATIVE_ASSETID, + LibAsset.NULL_ADDRESS, receiverAddress, 1 ether, block.timestamp @@ -262,8 +262,8 @@ contract ReceiverChainflipTest is TestBase { swapData[0] = LibSwap.SwapData({ callTo: address(mockDEX), approveTo: address(mockDEX), - sendingAssetId: LibAsset.NATIVE_ASSETID, - receivingAssetId: LibAsset.NATIVE_ASSETID, // Try to get ETH back + sendingAssetId: LibAsset.NULL_ADDRESS, + receivingAssetId: LibAsset.NULL_ADDRESS, // Try to get ETH back fromAmount: 1 ether, callData: abi.encodeWithSelector( mockDEX.mockSwapWillRevertWithReason.selector, @@ -311,7 +311,7 @@ contract ReceiverChainflipTest is TestBase { vm.expectEmit(); emit LiFiTransferCompleted( guid, - LibAsset.NATIVE_ASSETID, + LibAsset.NULL_ADDRESS, receiverAddress, amountOutMin, block.timestamp @@ -391,7 +391,7 @@ contract ReceiverChainflipTest is TestBase { swapData[0] = LibSwap.SwapData({ callTo: address(uniswap), approveTo: address(uniswap), - sendingAssetId: LibAsset.NATIVE_ASSETID, + sendingAssetId: LibAsset.NULL_ADDRESS, receivingAssetId: ADDRESS_DAI, fromAmount: amountIn, callData: abi.encodeWithSelector(