Skip to content

Commit efa7976

Browse files
authored
Add parameter validation to allbridge lf 14440 (#1275)
* added parameter validation to AllBridgeFacet * finalized test file * added Tron and Aptos custom chainIds * minor fixes * restore deleted file * formatting * move NON_EVM_ADDRESS to LiFiData * add parameter validation for RelayFacet
1 parent 7378ee1 commit efa7976

File tree

15 files changed

+358
-71
lines changed

15 files changed

+358
-71
lines changed

conventions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,10 @@ Follow the folder structure to locate resources and generate or modify code in a
201201
);
202202
}
203203
```
204-
- When a non-EVM address is used, the `bridgeData.receiver` must contain `LibAsset.NON_EVM_ADDRESS`, ensuring proper handling:
204+
- When a non-EVM address is used, the `bridgeData.receiver` must contain `NON_EVM_ADDRESS` (found in src/Helpers/LiFiData.sol), ensuring proper handling:
205205
```
206206
if (
207-
bridgeData.receiver != LibAsset.NON_EVM_ADDRESS
207+
bridgeData.receiver != NON_EVM_ADDRESS
208208
) {
209209
revert InvalidCallData();
210210
}

docs/ChainflipFacet.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ struct ChainflipData {
3232
}
3333
```
3434

35-
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`.
35+
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`.
3636

3737
## Supported Chains
3838

src/Errors/GenericErrors.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ error InvalidConfig();
1919
error InvalidContract();
2020
error InvalidDestinationChain();
2121
error InvalidFallbackAddress();
22+
error InvalidNonEVMReceiver();
2223
error InvalidReceiver();
2324
error InvalidSendingToken();
2425
error NativeAssetNotSupported();

src/Facets/AllBridgeFacet.sol

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,55 @@ import { SwapperV2 } from "../Helpers/SwapperV2.sol";
88
import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol";
99
import { Validatable } from "../Helpers/Validatable.sol";
1010
import { LibSwap } from "../Libraries/LibSwap.sol";
11+
import { InvalidConfig, InvalidCallData, InvalidNonEVMReceiver, InvalidReceiver } from "../Errors/GenericErrors.sol";
12+
import { LiFiData } from "../Helpers/LiFiData.sol";
1113

1214
/// @title Allbridge Facet
13-
/// @author Li.Finance (https://li.finance)
15+
/// @author LI.FI (https://li.fi)
1416
/// @notice Provides functionality for bridging through AllBridge
15-
/// @custom:version 2.0.0
16-
contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
17+
/// @custom:version 2.1.0
18+
contract AllBridgeFacet is
19+
ILiFi,
20+
ReentrancyGuard,
21+
SwapperV2,
22+
Validatable,
23+
LiFiData
24+
{
25+
uint32 private constant ALLBRIDGE_ID_ETHEREUM = 1;
26+
uint32 private constant ALLBRIDGE_ID_BSC = 2;
27+
uint32 private constant ALLBRIDGE_ID_TRON = 3;
28+
uint32 private constant ALLBRIDGE_ID_SOLANA = 4;
29+
uint32 private constant ALLBRIDGE_ID_POLYGON = 5;
30+
uint32 private constant ALLBRIDGE_ID_ARBITRUM = 6;
31+
uint32 private constant ALLBRIDGE_ID_AVALANCHE = 8;
32+
uint32 private constant ALLBRIDGE_ID_BASE = 9;
33+
uint32 private constant ALLBRIDGE_ID_OPTIMISM = 10;
34+
uint32 private constant ALLBRIDGE_ID_CELO = 11;
35+
uint32 private constant ALLBRIDGE_ID_SUI = 13;
36+
uint256 internal constant LIFI_CHAIN_ID_ARBITRUM = 42161;
37+
uint256 internal constant LIFI_CHAIN_ID_AVALANCHE = 43114;
38+
uint256 internal constant LIFI_CHAIN_ID_BASE = 8453;
39+
uint256 internal constant LIFI_CHAIN_ID_BSC = 56;
40+
uint256 internal constant LIFI_CHAIN_ID_CELO = 42220;
41+
uint256 internal constant LIFI_CHAIN_ID_POLYGON = 137;
42+
43+
error UnsupportedAllBridgeChainId();
44+
1745
/// @notice The contract address of the AllBridge router on the source chain.
1846
// solhint-disable-next-line immutable-vars-naming
19-
IAllBridge private immutable allBridge;
47+
IAllBridge private immutable ALLBRIDGE;
2048

2149
/// @notice The struct for the AllBridge data.
22-
/// @param fees The amount of token to pay the messenger and the bridge
2350
/// @param recipient The address of the token receiver after bridging.
51+
/// @param fees The amount of token to pay the messenger and the bridge
2452
/// @param destinationChainId The destination chain id.
2553
/// @param receiveToken The token to receive on the destination chain.
2654
/// @param nonce A random nonce to associate with the tx.
2755
/// @param messenger The messenger protocol enum
2856
/// @param payFeeWithSendingAsset Whether to pay the relayer fee with the sending asset or not
2957
struct AllBridgeData {
30-
uint256 fees;
3158
bytes32 recipient;
59+
uint256 fees;
3260
uint256 destinationChainId;
3361
bytes32 receiveToken;
3462
uint256 nonce;
@@ -39,7 +67,9 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
3967
/// @notice Initializes the AllBridge contract
4068
/// @param _allBridge The address of the AllBridge contract
4169
constructor(IAllBridge _allBridge) {
42-
allBridge = _allBridge;
70+
if (address(_allBridge) == address(0)) revert InvalidConfig();
71+
72+
ALLBRIDGE = _allBridge;
4373
}
4474

4575
/// @notice Bridge tokens to another chain via AllBridge
@@ -96,14 +126,38 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
96126
ILiFi.BridgeData memory _bridgeData,
97127
AllBridgeData calldata _allBridgeData
98128
) internal {
129+
// make sure destinationChainId matches in bridgeData and allBridgeData
130+
if (
131+
_allBridgeData.destinationChainId !=
132+
_getAllBridgeChainId(_bridgeData.destinationChainId)
133+
) revert InvalidCallData();
134+
135+
// validate receiver address
136+
if (_bridgeData.receiver == NON_EVM_ADDRESS) {
137+
// destination chain is non-EVM
138+
// make sure it's non-zero (we cannot validate further)
139+
if (_allBridgeData.recipient == bytes32(0))
140+
revert InvalidNonEVMReceiver();
141+
} else {
142+
// destination chain is EVM
143+
// make sure that bridgeData and allBridgeData receiver addresses match
144+
if (
145+
_bridgeData.receiver !=
146+
address(uint160(uint256(_allBridgeData.recipient)))
147+
) revert InvalidReceiver();
148+
}
149+
150+
// set max approval to allBridge, if current allowance is insufficient
99151
LibAsset.maxApproveERC20(
100152
IERC20(_bridgeData.sendingAssetId),
101-
address(allBridge),
153+
address(ALLBRIDGE),
102154
_bridgeData.minAmount
103155
);
104156

157+
// check if bridge fee should be paid with sending or native asset
105158
if (_allBridgeData.payFeeWithSendingAsset) {
106-
allBridge.swapAndBridge(
159+
// pay fee with sending asset
160+
ALLBRIDGE.swapAndBridge(
107161
bytes32(uint256(uint160(_bridgeData.sendingAssetId))),
108162
_bridgeData.minAmount,
109163
_allBridgeData.recipient,
@@ -114,7 +168,8 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
114168
_allBridgeData.fees
115169
);
116170
} else {
117-
allBridge.swapAndBridge{ value: _allBridgeData.fees }(
171+
// pay fee with native asset
172+
ALLBRIDGE.swapAndBridge{ value: _allBridgeData.fees }(
118173
bytes32(uint256(uint160(_bridgeData.sendingAssetId))),
119174
_bridgeData.minAmount,
120175
_allBridgeData.recipient,
@@ -128,4 +183,41 @@ contract AllBridgeFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
128183

129184
emit LiFiTransferStarted(_bridgeData);
130185
}
186+
187+
/// @notice Converts LiFi internal chain IDs to AllBridge chain IDs
188+
/// @param _destinationChainId The LiFi chain ID to convert
189+
/// @return The corresponding Chainflip chain ID
190+
/// @dev Reverts if the destination chain is not supported
191+
function _getAllBridgeChainId(
192+
uint256 _destinationChainId
193+
) internal pure returns (uint256) {
194+
// split possible values in half for more efficient search/matching
195+
196+
// first try to match cases where chainId is the same and does not need to be mapped
197+
if (
198+
_destinationChainId == ALLBRIDGE_ID_ETHEREUM ||
199+
_destinationChainId == ALLBRIDGE_ID_OPTIMISM
200+
) return _destinationChainId;
201+
// all others have custom chainIds
202+
else if (_destinationChainId == LIFI_CHAIN_ID_BSC)
203+
return ALLBRIDGE_ID_BSC;
204+
else if (_destinationChainId == LIFI_CHAIN_ID_TRON)
205+
return ALLBRIDGE_ID_TRON;
206+
else if (_destinationChainId == LIFI_CHAIN_ID_SOLANA)
207+
return ALLBRIDGE_ID_SOLANA;
208+
else if (_destinationChainId == LIFI_CHAIN_ID_POLYGON)
209+
return ALLBRIDGE_ID_POLYGON;
210+
else if (_destinationChainId == LIFI_CHAIN_ID_ARBITRUM)
211+
return ALLBRIDGE_ID_ARBITRUM;
212+
else if (_destinationChainId == LIFI_CHAIN_ID_AVALANCHE)
213+
return ALLBRIDGE_ID_AVALANCHE;
214+
else if (_destinationChainId == LIFI_CHAIN_ID_BASE)
215+
return ALLBRIDGE_ID_BASE;
216+
else if (_destinationChainId == LIFI_CHAIN_ID_CELO)
217+
return ALLBRIDGE_ID_CELO;
218+
else if (_destinationChainId == LIFI_CHAIN_ID_SUI)
219+
return ALLBRIDGE_ID_SUI;
220+
// revert if no match found
221+
else revert UnsupportedAllBridgeChainId();
222+
}
131223
}

src/Facets/ChainflipFacet.sol

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@ import { Validatable } from "../Helpers/Validatable.sol";
1010
import { IChainflipVault } from "../Interfaces/IChainflip.sol";
1111
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
1212
import { InformationMismatch, InvalidConfig, InvalidReceiver } from "../Errors/GenericErrors.sol";
13+
import { LiFiData } from "../Helpers/LiFiData.sol";
1314

14-
/// @title Chainflip Facet
15+
/// @title ChainflipFacet
1516
/// @author LI.FI (https://li.fi)
1617
/// @notice Allows bridging assets via Chainflip
17-
/// @custom:version 1.0.0
18-
contract ChainflipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
18+
/// @custom:version 1.0.1
19+
contract ChainflipFacet is
20+
ILiFi,
21+
ReentrancyGuard,
22+
SwapperV2,
23+
Validatable,
24+
LiFiData
25+
{
1926
/// Events ///
2027
event BridgeToNonEVMChain(
2128
bytes32 indexed transactionId,
@@ -134,7 +141,7 @@ contract ChainflipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
134141

135142
// Handle address encoding based on destination chain type
136143
bytes memory encodedDstAddress;
137-
if (_bridgeData.receiver == LibAsset.NON_EVM_ADDRESS) {
144+
if (_bridgeData.receiver == NON_EVM_ADDRESS) {
138145
if (_chainflipData.nonEVMReceiver.length == 0) {
139146
revert EmptyNonEvmAddress();
140147
}

src/Facets/RelayFacet.sol

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,20 @@ import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol";
99
import { SwapperV2 } from "../Helpers/SwapperV2.sol";
1010
import { Validatable } from "../Helpers/Validatable.sol";
1111
import { ECDSA } from "solady/utils/ECDSA.sol";
12+
import { LiFiData } from "../Helpers/LiFiData.sol";
13+
import { InvalidConfig } from "../Errors/GenericErrors.sol";
1214

13-
/// @title Relay Facet
15+
/// @title RelayFacet
1416
/// @author LI.FI (https://li.fi)
1517
/// @notice Provides functionality for bridging through Relay Protocol
16-
/// @custom:version 1.0.0
17-
contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
18+
/// @custom:version 1.0.1
19+
contract RelayFacet is
20+
ILiFi,
21+
ReentrancyGuard,
22+
SwapperV2,
23+
Validatable,
24+
LiFiData
25+
{
1826
// Receiver for native transfers
1927
// solhint-disable-next-line immutable-vars-naming
2028
address public immutable relayReceiver;
@@ -67,7 +75,7 @@ contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
6775

6876
// Ensure nonEVMAddress is not empty
6977
if (
70-
_bridgeData.receiver == LibAsset.NON_EVM_ADDRESS &&
78+
_bridgeData.receiver == NON_EVM_ADDRESS &&
7179
_relayData.nonEVMReceiver == bytes32(0)
7280
) {
7381
revert InvalidQuote();
@@ -84,7 +92,7 @@ contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
8492
bytes32(uint256(uint160(address(this)))),
8593
bytes32(uint256(uint160(_bridgeData.sendingAssetId))),
8694
_getMappedChainId(_bridgeData.destinationChainId),
87-
_bridgeData.receiver == LibAsset.NON_EVM_ADDRESS
95+
_bridgeData.receiver == NON_EVM_ADDRESS
8896
? _relayData.nonEVMReceiver
8997
: bytes32(uint256(uint160(_bridgeData.receiver))),
9098
_relayData.receivingAssetId
@@ -103,6 +111,10 @@ contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
103111
/// @param _relayReceiver The receiver for native transfers
104112
/// @param _relaySolver The relayer wallet for ERC20 transfers
105113
constructor(address _relayReceiver, address _relaySolver) {
114+
if (_relayReceiver == address(0) || _relaySolver == address(0)) {
115+
revert InvalidConfig();
116+
}
117+
106118
relayReceiver = _relayReceiver;
107119
relaySolver = _relaySolver;
108120
}
@@ -203,7 +215,7 @@ contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
203215
consumedIds[_relayData.requestId] = true;
204216

205217
// Emit special event if bridging to non-EVM chain
206-
if (_bridgeData.receiver == LibAsset.NON_EVM_ADDRESS) {
218+
if (_bridgeData.receiver == NON_EVM_ADDRESS) {
207219
emit BridgeToNonEVMChain(
208220
_bridgeData.transactionId,
209221
_getMappedChainId(_bridgeData.destinationChainId),

src/Helpers/LiFiData.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: LGPL-3.0-only
2+
pragma solidity ^0.8.17;
3+
4+
/// @title LiFiData
5+
/// @author LI.FI (https://li.fi)
6+
/// @notice A storage for LI.FI-internal config data (addresses, chainIDs, etc.)
7+
/// @custom:version 1.0.0
8+
contract LiFiData {
9+
address internal constant NON_EVM_ADDRESS =
10+
0x11f111f111f111F111f111f111F111f111f111F1;
11+
12+
// LI.FI non-EVM Custom Chain IDs (IDs are made up by the LI.FI team)
13+
uint256 internal constant LIFI_CHAIN_ID_APTOS = 9271000000000010;
14+
uint256 internal constant LIFI_CHAIN_ID_BCH = 20000000000002;
15+
uint256 internal constant LIFI_CHAIN_ID_BTC = 20000000000001;
16+
uint256 internal constant LIFI_CHAIN_ID_DGE = 20000000000004;
17+
uint256 internal constant LIFI_CHAIN_ID_LTC = 20000000000003;
18+
uint256 internal constant LIFI_CHAIN_ID_SOLANA = 1151111081099710;
19+
uint256 internal constant LIFI_CHAIN_ID_SUI = 9270000000000000;
20+
uint256 internal constant LIFI_CHAIN_ID_TRON = 1885080386571452;
21+
}

src/Helpers/SwapperV2.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// SPDX-License-Identifier: LGPL-3.0-only
2-
/// @custom:version 1.0.0
32
pragma solidity ^0.8.17;
43

54
import { ILiFi } from "../Interfaces/ILiFi.sol";
@@ -8,9 +7,10 @@ import { LibAsset } from "../Libraries/LibAsset.sol";
87
import { LibAllowList } from "../Libraries/LibAllowList.sol";
98
import { ContractCallNotAllowed, NoSwapDataProvided, CumulativeSlippageTooHigh } from "../Errors/GenericErrors.sol";
109

11-
/// @title Swapper
10+
/// @title SwapperV2
1211
/// @author LI.FI (https://li.fi)
1312
/// @notice Abstract contract to provide swap functionality
13+
/// @custom:version 1.0.1
1414
contract SwapperV2 is ILiFi {
1515
/// Types ///
1616

@@ -119,7 +119,7 @@ contract SwapperV2 is ILiFi {
119119

120120
if (finalBalance > initialBalance) {
121121
LibAsset.transferAsset(
122-
LibAsset.NATIVE_ASSETID,
122+
LibAsset.NULL_ADDRESS,
123123
_refundReceiver,
124124
finalBalance - initialBalance
125125
);

src/Libraries/LibAsset.sol

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,17 @@ import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
77
import { InvalidReceiver, NullAddrIsNotAValidSpender, InvalidAmount, NullAddrIsNotAnERC20Token } from "../Errors/GenericErrors.sol";
88

99
/// @title LibAsset
10-
/// @custom:version 2.1.0
10+
/// @custom:version 2.1.1
1111
/// @notice This library contains helpers for dealing with onchain transfers
1212
/// of assets, including accounting for the native asset `assetId`
1313
/// conventions and any noncompliant ERC20 transfers
1414
library LibAsset {
1515
using SafeTransferLib for address;
1616
using SafeTransferLib for address payable;
1717

18-
address internal constant NULL_ADDRESS = address(0);
19-
20-
address internal constant NON_EVM_ADDRESS =
21-
0x11f111f111f111F111f111f111F111f111f111F1;
22-
2318
/// @dev All native assets use the empty address for their asset id
2419
/// by convention
25-
26-
address internal constant NATIVE_ASSETID = NULL_ADDRESS;
20+
address internal constant NULL_ADDRESS = address(0);
2721

2822
/// @dev EIP-7702 delegation designator prefix for Account Abstraction
2923
bytes3 internal constant DELEGATION_DESIGNATOR = 0xef0100;
@@ -191,7 +185,7 @@ library LibAsset {
191185
/// @param assetId The asset identifier to evaluate
192186
/// @return Boolean indicating if the asset is the native asset
193187
function isNativeAsset(address assetId) internal pure returns (bool) {
194-
return assetId == NATIVE_ASSETID;
188+
return assetId == NULL_ADDRESS;
195189
}
196190

197191
/// @notice Checks if the given address is a contract

src/Periphery/ReceiverChainflip.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { InvalidConfig } from "../Errors/GenericErrors.sol";
1212
/// @title ReceiverChainflip
1313
/// @author LI.FI (https://li.fi)
1414
/// @notice Receiver contract for Chainflip cross-chain swaps and message passing
15-
/// @custom:version 1.0.0
15+
/// @custom:version 1.0.1
1616
contract ReceiverChainflip is ILiFi, WithdrawablePeriphery {
1717
using SafeTransferLib for address;
1818
using SafeTransferLib for address payable;
@@ -114,7 +114,7 @@ contract ReceiverChainflip is ILiFi, WithdrawablePeriphery {
114114
) private {
115115
// Group address conversion and store in memory to avoid multiple storage reads
116116
address actualAssetId = assetId == CHAINFLIP_NATIVE_ADDRESS
117-
? LibAsset.NATIVE_ASSETID
117+
? LibAsset.NULL_ADDRESS
118118
: assetId;
119119
bool isNative = LibAsset.isNativeAsset(actualAssetId);
120120

0 commit comments

Comments
 (0)