Skip to content
Merged
1 change: 1 addition & 0 deletions src/Errors/GenericErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ error InvalidConfig();
error InvalidContract();
error InvalidDestinationChain();
error InvalidFallbackAddress();
error InvalidNonEVMReceiver();
error InvalidReceiver();
error InvalidSendingToken();
error NativeAssetNotSupported();
Expand Down
103 changes: 99 additions & 4 deletions src/Facets/AllBridgeFacet.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity ^0.8.17;

import { ILiFi } from "../Interfaces/ILiFi.sol";
Expand All @@ -8,27 +9,57 @@ 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)
/// @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_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();

/// @notice The contract address of the AllBridge router on the source chain.
// solhint-disable-next-line immutable-vars-naming
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;
Expand All @@ -39,6 +70,8 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
/// @notice Initializes the AllBridge contract
/// @param _allBridge The address of the AllBridge contract
constructor(IAllBridge _allBridge) {
if (address(_allBridge) == address(0)) revert InvalidConfig();

allBridge = _allBridge;
}

Expand Down Expand Up @@ -96,13 +129,37 @@ 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),
_bridgeData.minAmount
);

// check if bridge fee should be paid with sending or native asset
if (_allBridgeData.payFeeWithSendingAsset) {
// pay fee with sending asset
allBridge.swapAndBridge(
bytes32(uint256(uint160(_bridgeData.sendingAssetId))),
_bridgeData.minAmount,
Expand All @@ -114,6 +171,7 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
_allBridgeData.fees
);
} else {
// pay fee with native asset
allBridge.swapAndBridge{ value: _allBridgeData.fees }(
bytes32(uint256(uint160(_bridgeData.sendingAssetId))),
_bridgeData.minAmount,
Expand All @@ -128,4 +186,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();
}
}
22 changes: 22 additions & 0 deletions src/Helpers/LiFiData.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity ^0.8.17;

/// @title LiFiData
/// @author Li.Finance (https://li.finance)
/// @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;
}
Loading
Loading