Skip to content
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
2 changes: 1 addition & 1 deletion docs/ChainflipFacet.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
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
112 changes: 102 additions & 10 deletions src/Facets/AllBridgeFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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();
}
}
15 changes: 11 additions & 4 deletions src/Facets/ChainflipFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
}
Expand Down
24 changes: 18 additions & 6 deletions src/Facets/RelayFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand All @@ -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;
}
Expand Down Expand Up @@ -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),
Expand Down
21 changes: 21 additions & 0 deletions src/Helpers/LiFiData.sol
Original file line number Diff line number Diff line change
@@ -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;
}
6 changes: 3 additions & 3 deletions src/Helpers/SwapperV2.sol
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 ///

Expand Down Expand Up @@ -119,7 +119,7 @@ contract SwapperV2 is ILiFi {

if (finalBalance > initialBalance) {
LibAsset.transferAsset(
LibAsset.NATIVE_ASSETID,
LibAsset.NULL_ADDRESS,
_refundReceiver,
finalBalance - initialBalance
);
Expand Down
13 changes: 4 additions & 9 deletions src/Libraries/LibAsset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,18 @@ 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
/// @author LI.FI (https://li.fi)
/// @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
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;
Expand Down Expand Up @@ -191,7 +186,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
Expand Down
4 changes: 2 additions & 2 deletions src/Periphery/ReceiverChainflip.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
Loading