diff --git a/script/deploy/facets/DeployGasZipPeriphery.s.sol b/script/deploy/facets/DeployGasZipPeriphery.s.sol index 1bf95134f..54e6da2cc 100644 --- a/script/deploy/facets/DeployGasZipPeriphery.s.sol +++ b/script/deploy/facets/DeployGasZipPeriphery.s.sol @@ -28,7 +28,7 @@ contract DeployScript is DeployScriptBase { string.concat(".gasZipRouters.", network) ); - // get LiFiDEXAggregator address + // get LiFiDiamond address path = string.concat( root, "/deployments/", @@ -38,10 +38,7 @@ contract DeployScript is DeployScriptBase { "json" ); - address liFiDEXAggregator = _getConfigContractAddress( - path, - ".LiFiDEXAggregator" - ); + address liFiDiamond = _getConfigContractAddress(path, ".LiFiDiamond"); // get network's SAFE address to become contract owner for potential fund withdrawals path = string.concat(root, "/config/networks.json"); @@ -51,6 +48,6 @@ contract DeployScript is DeployScriptBase { string.concat(".", network, ".safeAddress") ); - return abi.encode(gasZipRouter, liFiDEXAggregator, safeAddress); + return abi.encode(gasZipRouter, liFiDiamond, safeAddress); } } diff --git a/script/deploy/zksync/DeployGasZipPeriphery.zksync.s.sol b/script/deploy/zksync/DeployGasZipPeriphery.zksync.s.sol index 1bf95134f..ec34b97a3 100644 --- a/script/deploy/zksync/DeployGasZipPeriphery.zksync.s.sol +++ b/script/deploy/zksync/DeployGasZipPeriphery.zksync.s.sol @@ -38,10 +38,7 @@ contract DeployScript is DeployScriptBase { "json" ); - address liFiDEXAggregator = _getConfigContractAddress( - path, - ".LiFiDEXAggregator" - ); + address liFiDiamond = _getConfigContractAddress(path, ".LiFiDiamond"); // get network's SAFE address to become contract owner for potential fund withdrawals path = string.concat(root, "/config/networks.json"); @@ -51,6 +48,6 @@ contract DeployScript is DeployScriptBase { string.concat(".", network, ".safeAddress") ); - return abi.encode(gasZipRouter, liFiDEXAggregator, safeAddress); + return abi.encode(gasZipRouter, liFiDiamond, safeAddress); } } diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 3589a2404..381d73b69 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -3,42 +3,49 @@ pragma solidity ^0.8.17; import { ILiFi } from "../Interfaces/ILiFi.sol"; import { IGasZip } from "../Interfaces/IGasZip.sol"; +import { IWhitelistManagerFacet } from "../Interfaces/IWhitelistManagerFacet.sol"; import { LibSwap } from "../Libraries/LibSwap.sol"; import { LibAsset, IERC20 } from "../Libraries/LibAsset.sol"; import { LibUtil } from "../Libraries/LibUtil.sol"; import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; -import { InvalidCallData } from "../Errors/GenericErrors.sol"; +import { InvalidCallData, ContractCallNotAllowed, InvalidConfig } from "../Errors/GenericErrors.sol"; /// @title GasZipPeriphery /// @author LI.FI (https://li.fi) /// @notice Provides functionality to swap ERC20 tokens to use the gas.zip protocol as a pre-bridge step (https://www.gas.zip/) -/// @custom:version 1.0.1 +/// @custom:version 1.0.2 contract GasZipPeriphery is ILiFi, WithdrawablePeriphery { using SafeTransferLib for address; /// State /// - // solhint-disable-next-line immutable-vars-naming - IGasZip public immutable gasZipRouter; - // solhint-disable-next-line immutable-vars-naming - address public immutable liFiDEXAggregator; - uint256 internal constant MAX_CHAINID_LENGTH_ALLOWED = 32; + IGasZip public immutable GAS_ZIP_ROUTER; + address public immutable LIFI_DIAMOND; + uint256 internal constant MAX_CHAINID_LENGTH_ALLOWED = 16; /// Errors /// error TooManyChainIds(); + error SwapOutputMustBeNative(); /// Constructor /// constructor( address _gasZipRouter, - address _liFiDEXAggregator, + address _liFiDiamond, address _owner ) WithdrawablePeriphery(_owner) { - gasZipRouter = IGasZip(_gasZipRouter); - liFiDEXAggregator = _liFiDEXAggregator; + if ( + _gasZipRouter == address(0) || + _liFiDiamond == address(0) || + _owner == address(0) + ) { + revert InvalidConfig(); + } + GAS_ZIP_ROUTER = IGasZip(_gasZipRouter); + LIFI_DIAMOND = _liFiDiamond; } /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract - /// Swaps are only allowed via the LiFiDEXAggregator + /// Swaps are allowed via any whitelisted contract from the Diamond's WhitelistManagerFacet /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge /// @param _swapData The swap data that executes the swap from ERC20 to native /// @param _gasZipData contains information about which chains gas should be sent to @@ -46,26 +53,48 @@ contract GasZipPeriphery is ILiFi, WithdrawablePeriphery { LibSwap.SwapData calldata _swapData, IGasZip.GasZipData calldata _gasZipData ) public { + if (_swapData.receivingAssetId != address(0)) { + revert SwapOutputMustBeNative(); + } + + IWhitelistManagerFacet whitelistManager = IWhitelistManagerFacet( + LIFI_DIAMOND + ); + + if ( + !whitelistManager.isAddressWhitelisted(_swapData.callTo) || + !whitelistManager.isFunctionApproved( + bytes4(_swapData.callData[:4]) + ) + ) { + revert ContractCallNotAllowed(); + } + // deposit ERC20 asset from diamond LibAsset.depositAsset(_swapData.sendingAssetId, _swapData.fromAmount); // max approve to DEX, if not already done LibAsset.maxApproveERC20( IERC20(_swapData.sendingAssetId), - liFiDEXAggregator, + _swapData.approveTo, _swapData.fromAmount ); - // execute swap using LiFiDEXAggregator + uint256 preSwapBal = address(this).balance; + + // execute swap using the whitelisted DEX + // Note on slippage protection: + // 1. Individual swap slippage is protected via minAmountOut parameter in _swapData.callData + // 2. Final output amount slippage is checked at diamond contract level in SwapperV2._depositAndSwap() // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory res) = liFiDEXAggregator.call( + (bool success, bytes memory res) = _swapData.callTo.call( _swapData.callData ); if (!success) { LibUtil.revertWith(res); } - // extract the swap output amount from the call return value - uint256 swapOutputAmount = abi.decode(res, (uint256)); + + uint256 swapOutputAmount = address(this).balance - preSwapBal; // deposit native tokens to Gas.zip protocol depositToGasZipNative(_gasZipData, swapOutputAmount); @@ -84,7 +113,7 @@ contract GasZipPeriphery is ILiFi, WithdrawablePeriphery { revert InvalidCallData(); // We are depositing to a new contract that supports deposits for EVM chains + Solana (therefore 'receiver' address is bytes32) - gasZipRouter.deposit{ value: _amount }( + GAS_ZIP_ROUTER.deposit{ value: _amount }( _gasZipData.destinationChains, _gasZipData.receiverAddress ); @@ -107,9 +136,9 @@ contract GasZipPeriphery is ILiFi, WithdrawablePeriphery { if (length > MAX_CHAINID_LENGTH_ALLOWED) revert TooManyChainIds(); for (uint256 i; i < length; ++i) { - // Shift destinationChains left by 8 bits and add the next chainID + // Shift destinationChains left by 16 bits and add the next chainID destinationChains = - (destinationChains << 8) | + (destinationChains << 16) | uint256(_chainIds[i]); } } diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index e27da1d3e..a8d23dbdb 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -3,34 +3,13 @@ pragma solidity ^0.8.17; import { GasZipPeriphery } from "lifi/Periphery/GasZipPeriphery.sol"; import { LibSwap } from "lifi/Libraries/LibSwap.sol"; -import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; import { TestGnosisBridgeFacet } from "test/solidity/Facets/GnosisBridgeFacet.t.sol"; -import { TestBase, ILiFi } from "../utils/TestBase.sol"; import { IXDaiBridge } from "lifi/Interfaces/IXDaiBridge.sol"; import { IGasZip } from "lifi/Interfaces/IGasZip.sol"; +import { WhitelistManagerFacet } from "lifi/Facets/WhitelistManagerFacet.sol"; +import { InvalidCallData, InvalidConfig } from "lifi/Errors/GenericErrors.sol"; +import { TestBase, ILiFi } from "../utils/TestBase.sol"; import { NonETHReceiver } from "../utils/TestHelpers.sol"; -import { InvalidCallData } from "lifi/Errors/GenericErrors.sol"; - -// Stub GenericSwapFacet Contract -contract TestGasZipPeriphery is GasZipPeriphery { - constructor( - address gasZipRouter, - address liFiDEXAggregator, - address owner - ) GasZipPeriphery(gasZipRouter, liFiDEXAggregator, owner) {} - - function addToWhitelist(address _address) external { - LibAllowList.addAllowedContract(_address); - } - - function removeFromWhitelist(address _address) external { - LibAllowList.removeAllowedContract(_address); - } - - function setFunctionApprovalBySignature(bytes4 _signature) external { - LibAllowList.addAllowedSelector(_signature); - } -} contract GasZipPeripheryTest is TestBase { address public constant GAS_ZIP_ROUTER_MAINNET = @@ -41,34 +20,98 @@ contract GasZipPeripheryTest is TestBase { 0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016; TestGnosisBridgeFacet internal gnosisBridgeFacet; - TestGasZipPeriphery internal gasZipPeriphery; + GasZipPeriphery internal gasZipPeriphery; + WhitelistManagerFacet internal whitelistManagerFacet; IGasZip.GasZipData internal defaultGasZipData; - address internal liFiDEXAggregator = LIFI_DEX_AGGREGATOR_MAINNET; bytes32 internal defaultReceiverBytes32 = bytes32(uint256(uint160(USER_RECEIVER))); uint256 internal defaultNativeDepositAmount = 1e16; uint256 public defaultDestinationChains = 96; + bytes4 internal constant PROCESS_ROUTE_SELECTOR = bytes4(hex"2646478b"); event Deposit(address from, uint256 chains, uint256 amount, bytes32 to); error TooManyChainIds(); error ETHTransferFailed(); + error SwapFailed(); function setUp() public { customBlockNumberForForking = 20931877; initTestBase(); - // deploy contracts - gasZipPeriphery = new TestGasZipPeriphery( + // Deploy contracts and set up the Diamond with the facets + gnosisBridgeFacet = _getGnosisBridgeFacet(); + + // Deploy WhitelistManagerFacet and add it to the diamond + whitelistManagerFacet = new WhitelistManagerFacet(); + + // Deploy GasZipPeriphery with diamond from TestBase + gasZipPeriphery = new GasZipPeriphery( GAS_ZIP_ROUTER_MAINNET, - LIFI_DEX_AGGREGATOR_MAINNET, + address(diamond), USER_DIAMOND_OWNER ); - defaultUSDCAmount = 10 * 10 ** usdc.decimals(); // 10 USDC - // set up diamond with GnosisBridgeFacet so we have a bridge to test with - gnosisBridgeFacet = _getGnosisBridgeFacet(); + bytes4[] memory functionSelectors = new bytes4[](4); + functionSelectors[0] = WhitelistManagerFacet.addToWhitelist.selector; + functionSelectors[1] = WhitelistManagerFacet + .setFunctionApprovalBySignature + .selector; + functionSelectors[2] = WhitelistManagerFacet + .isAddressWhitelisted + .selector; + functionSelectors[3] = WhitelistManagerFacet + .isFunctionApproved + .selector; + + addFacet(diamond, address(whitelistManagerFacet), functionSelectors); + whitelistManagerFacet = WhitelistManagerFacet(address(diamond)); + + // whitelist DEXs / Periphery contracts + whitelistManagerFacet.addToWhitelist(address(uniswap)); + whitelistManagerFacet.addToWhitelist(address(gasZipPeriphery)); + whitelistManagerFacet.addToWhitelist(address(feeCollector)); + + vm.label(address(uniswap), "Uniswap"); + vm.label(address(gasZipPeriphery), "GasZipPeriphery"); + vm.label(address(feeCollector), "FeeCollector"); + + // add function selectors for GasZipPeriphery + whitelistManagerFacet.setFunctionApprovalBySignature( + gasZipPeriphery.depositToGasZipERC20.selector, + true + ); + whitelistManagerFacet.setFunctionApprovalBySignature( + gasZipPeriphery.depositToGasZipNative.selector, + true + ); + + // add function selectors for FeeCollector + whitelistManagerFacet.setFunctionApprovalBySignature( + feeCollector.collectTokenFees.selector, + true + ); + + // add function selectors for Uniswap + whitelistManagerFacet.setFunctionApprovalBySignature( + uniswap.swapExactTokensForTokens.selector, + true + ); + whitelistManagerFacet.setFunctionApprovalBySignature( + uniswap.swapExactTokensForETH.selector, + true + ); + whitelistManagerFacet.setFunctionApprovalBySignature( + uniswap.swapETHForExactTokens.selector, + true + ); + whitelistManagerFacet.setFunctionApprovalBySignature( + uniswap.swapExactETHForTokens.selector, + true + ); + + defaultUSDCAmount = 10 * 10 ** usdc.decimals(); // 10 USDC defaultGasZipData = IGasZip.GasZipData({ receiverAddress: defaultReceiverBytes32, @@ -85,20 +128,17 @@ contract GasZipPeripheryTest is TestBase { } function test_WillStoreConstructorParametersCorrectly() public { - gasZipPeriphery = new TestGasZipPeriphery( + gasZipPeriphery = new GasZipPeriphery( GAS_ZIP_ROUTER_MAINNET, - LIFI_DEX_AGGREGATOR_MAINNET, + address(diamond), USER_DIAMOND_OWNER ); assertEq( - address(gasZipPeriphery.gasZipRouter()), + address(gasZipPeriphery.GAS_ZIP_ROUTER()), GAS_ZIP_ROUTER_MAINNET ); - assertEq( - gasZipPeriphery.liFiDEXAggregator(), - LIFI_DEX_AGGREGATOR_MAINNET - ); + assertEq(gasZipPeriphery.LIFI_DIAMOND(), address(diamond)); } function test_CanDepositNative() public { @@ -120,6 +160,7 @@ contract GasZipPeripheryTest is TestBase { function test_WillReturnAnyExcessNativeValueAfterDeposit() public { vm.startPrank(USER_SENDER); uint256 balanceBefore = USER_SENDER.balance; + // set up expected event vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); emit Deposit( @@ -160,7 +201,7 @@ contract GasZipPeripheryTest is TestBase { // Testcase: // 1. pay 1 USDC fee to FeeCollector in USDC // 2. swap remaining (9) USDC to DAI - // 3. deposit 2 DAI to gasZip + // 3. deposit 2 DAI to GasZipPeriphery which will be swapped to ETH and sent to the GasZip contract // 4. bridge remaining DAI to Gnosis using GnosisBridgeFacet deal(ADDRESS_USDC, address(this), defaultUSDCAmount); @@ -221,10 +262,7 @@ contract GasZipPeripheryTest is TestBase { ( LibSwap.SwapData memory gasZipSwapData, - ) = _getLiFiDEXAggregatorCalldataForERC20ToNativeSwap( - ADDRESS_DAI, - gasZipERC20Amount - ); + ) = _getUniswapERC20ToNativeSwapData(ADDRESS_DAI, gasZipERC20Amount); swapData[2] = LibSwap.SwapData( address(gasZipPeriphery), @@ -254,16 +292,6 @@ contract GasZipPeripheryTest is TestBase { hasDestinationCall: false }); - // whitelist gasZipPeriphery and FeeCollector - gasZipPeriphery.addToWhitelist(address(gasZipPeriphery)); - gasZipPeriphery.setFunctionApprovalBySignature( - gasZipPeriphery.depositToGasZipERC20.selector - ); - gasZipPeriphery.addToWhitelist(address(feeCollector)); - gasZipPeriphery.setFunctionApprovalBySignature( - feeCollector.collectTokenFees.selector - ); - // set approval for bridging usdc.approve(address(gnosisBridgeFacet), defaultUSDCAmount); @@ -346,12 +374,6 @@ contract GasZipPeripheryTest is TestBase { hasDestinationCall: false }); - // whitelist gasZipPeriphery and FeeCollector - gasZipPeriphery.addToWhitelist(address(gasZipPeriphery)); - gasZipPeriphery.setFunctionApprovalBySignature( - gasZipPeriphery.depositToGasZipNative.selector - ); - gnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge{ value: nativeFromAmount }(bridgeData, swapData); @@ -369,7 +391,7 @@ contract GasZipPeripheryTest is TestBase { chainIds[0] = 51; chainIds[1] = 52; - assertEq(gasZipPeriphery.getDestinationChainsValue(chainIds), 13108); + assertEq(gasZipPeriphery.getDestinationChainsValue(chainIds), 3342388); // case 3 chainIds = new uint8[](5); @@ -381,7 +403,30 @@ contract GasZipPeripheryTest is TestBase { assertEq( gasZipPeriphery.getDestinationChainsValue(chainIds), - 65336774203 + 276716361166703427643 + ); + + chainIds = new uint8[](16); + chainIds[0] = 255; // Chain ID 255 + chainIds[1] = 57; // Chain ID 57 + chainIds[2] = 62; // Chain ID 62 + chainIds[3] = 15; // Chain ID 15 + chainIds[4] = 54; // Chain ID 54 + chainIds[5] = 96; // Chain ID 96 + chainIds[6] = 140; // Chain ID 140 + chainIds[7] = 148; // Chain ID 148 + chainIds[8] = 21; // Chain ID 21 + chainIds[9] = 20; // Chain ID 20 + chainIds[10] = 10; // Chain ID 10 + chainIds[11] = 31; // Chain ID 31 + chainIds[12] = 16; // Chain ID 16 + chainIds[13] = 59; // Chain ID 59 + chainIds[14] = 13; // Chain ID 13 + chainIds[15] = 30; // Chain ID 30 + + assertEq( + gasZipPeriphery.getDestinationChainsValue(chainIds), + 450547538260953446430386195920619374874770272090431965477324569820816801822 ); } @@ -393,18 +438,23 @@ contract GasZipPeripheryTest is TestBase { // set DAI approval for GasZipPeriphery dai.approve(address(gasZipPeriphery), type(uint256).max); - // // get swapData for gas zip uint256 gasZipERC20Amount = 2 * 10 ** dai.decimals(); ( LibSwap.SwapData memory gasZipSwapData, - ) = _getLiFiDEXAggregatorCalldataForERC20ToNativeSwap( - ADDRESS_DAI, - gasZipERC20Amount - ); + ) = _getUniswapERC20ToNativeSwapData(ADDRESS_DAI, gasZipERC20Amount); // use an invalid function selector to force the call to LiFiDEXAggregator to fail - gasZipSwapData.callData = hex"3a3f7332"; + // Note: The function must pass the whitelist check but fail when executed + gasZipSwapData.callData = abi.encodeWithSelector( + PROCESS_ROUTE_SELECTOR, + address(0), // invalid params to make the call fail + 0, + address(0), + 0, + address(0), + "" + ); // expect the following call to fail without an error reason vm.expectRevert(); @@ -439,6 +489,108 @@ contract GasZipPeripheryTest is TestBase { }(defaultGasZipData, defaultNativeDepositAmount); } + function testRevert_WillFailIfReceivingAssetIsNotNative() public { + vm.startPrank(USER_SENDER); + + // create SwapData with non-native receiving asset (e.g., DAI instead of ETH/address(0)) + uint256 daiAmount = 1e18; + + LibSwap.SwapData memory swapData = LibSwap.SwapData( + address(uniswap), + address(uniswap), + ADDRESS_USDC, // sending USDC + ADDRESS_DAI, // receiving DAI (non-native) - this should cause revert + daiAmount, + abi.encodeWithSelector( + uniswap.swapExactTokensForTokens.selector, + daiAmount, + 0, + new address[](0), // not important for this test + address(0), + 0 + ), + true + ); + + vm.expectRevert(GasZipPeriphery.SwapOutputMustBeNative.selector); + + // call depositToGasZipERC20 with non-native receiving asset, should revert immediately + gasZipPeriphery.depositToGasZipERC20(swapData, defaultGasZipData); + } + + function testRevert_WillFailWhenSwapOperationIsUnsuccessful() public { + // deploy a simple mock contract that can be called and will revert with our custom error + MockFailingDexWithCustomError mockDex = new MockFailingDexWithCustomError(); + + // whitelist the mock DEX in the WhitelistManager + vm.startPrank(USER_DIAMOND_OWNER); + whitelistManagerFacet.addToWhitelist(address(mockDex)); + + bytes4 mockSelector = uniswap.swapExactTokensForETH.selector; + whitelistManagerFacet.setFunctionApprovalBySignature( + mockSelector, + true + ); + vm.stopPrank(); + + vm.startPrank(USER_SENDER); + + // SwapData with the mock DEX that will fail + LibSwap.SwapData memory swapData = LibSwap.SwapData( + address(mockDex), // callTo - the mock contract that will revert + address(mockDex), + ADDRESS_DAI, + address(0), // receivingAssetId - set to native to pass the initial check + 1e18, // fromAmount + abi.encodeWithSelector( + mockSelector, + 1e18, + 0, + new address[](2), + address(0), + 0 + ), + true + ); + + deal(ADDRESS_DAI, USER_SENDER, 1e18); + + dai.approve(address(gasZipPeriphery), 1e18); + + vm.expectRevert(SwapFailed.selector); + gasZipPeriphery.depositToGasZipERC20(swapData, defaultGasZipData); + } + + function testRevert_WillFailWithZeroGasZipRouter() public { + // Try to deploy with zero address for gasZipRouter + vm.expectRevert(InvalidConfig.selector); + new GasZipPeriphery( + address(0), // zero address for gasZipRouter + address(diamond), + USER_DIAMOND_OWNER + ); + } + + function testRevert_WillFailWithZeroLiFiDiamond() public { + // Try to deploy with zero address for liFiDiamond + vm.expectRevert(InvalidConfig.selector); + new GasZipPeriphery( + GAS_ZIP_ROUTER_MAINNET, + address(0), // zero address for liFiDiamond + USER_DIAMOND_OWNER + ); + } + + function testRevert_WillFailWithZeroOwner() public { + // Try to deploy with zero address for owner + vm.expectRevert(InvalidConfig.selector); + new GasZipPeriphery( + GAS_ZIP_ROUTER_MAINNET, + address(diamond), + address(0) // zero address for owner + ); + } + function _getGnosisBridgeFacet() internal returns (TestGnosisBridgeFacet _gnosisBridgeFacet) @@ -447,58 +599,22 @@ contract GasZipPeripheryTest is TestBase { IXDaiBridge(XDAI_BRIDGE) ); - bytes4[] memory functionSelectors = new bytes4[](4); + bytes4[] memory functionSelectors = new bytes4[](2); functionSelectors[0] = _gnosisBridgeFacet .startBridgeTokensViaXDaiBridge .selector; functionSelectors[1] = _gnosisBridgeFacet .swapAndStartBridgeTokensViaXDaiBridge .selector; - functionSelectors[2] = _gnosisBridgeFacet.addToWhitelist.selector; - functionSelectors[3] = _gnosisBridgeFacet - .setFunctionApprovalBySignature - .selector; addFacet(diamond, address(_gnosisBridgeFacet), functionSelectors); _gnosisBridgeFacet = TestGnosisBridgeFacet(address(diamond)); - // whitelist DEXs / Periphery contracts - _gnosisBridgeFacet.addToWhitelist(address(uniswap)); - _gnosisBridgeFacet.addToWhitelist(address(gasZipPeriphery)); - _gnosisBridgeFacet.addToWhitelist(address(feeCollector)); - - // add function selectors for GasZipPeriphery - _gnosisBridgeFacet.setFunctionApprovalBySignature( - gasZipPeriphery.depositToGasZipERC20.selector - ); - _gnosisBridgeFacet.setFunctionApprovalBySignature( - gasZipPeriphery.depositToGasZipNative.selector - ); - - // add function selectors for FeeCollector - _gnosisBridgeFacet.setFunctionApprovalBySignature( - feeCollector.collectTokenFees.selector - ); - - // add function selectors for Uniswap - _gnosisBridgeFacet.setFunctionApprovalBySignature( - uniswap.swapExactTokensForTokens.selector - ); - _gnosisBridgeFacet.setFunctionApprovalBySignature( - uniswap.swapExactTokensForETH.selector - ); - _gnosisBridgeFacet.setFunctionApprovalBySignature( - uniswap.swapETHForExactTokens.selector - ); - _gnosisBridgeFacet.setFunctionApprovalBySignature( - uniswap.swapExactETHForTokens.selector - ); - setFacetAddressInTestBase(address(gnosisBridgeFacet), "GnosisFacet"); } - function _getLiFiDEXAggregatorCalldataForERC20ToNativeSwap( + function _getUniswapERC20ToNativeSwapData( address sendingAssetId, uint256 fromAmount ) @@ -511,19 +627,46 @@ contract GasZipPeripheryTest is TestBase { path[0] = sendingAssetId; path[1] = ADDRESS_WRAPPED_NATIVE; - // Calculate USDC input amount + // Calculate expected amount out uint256[] memory amounts = uniswap.getAmountsOut(fromAmount, path); amountOutMin = amounts[1]; + // Use Uniswap directly instead of LiFiDEXAggregator swapData = LibSwap.SwapData( - liFiDEXAggregator, - liFiDEXAggregator, + address(uniswap), + address(uniswap), sendingAssetId, - address(0), + address(0), // receiving native ETH fromAmount, - // this is calldata for the DEXAggregator to swap 2 DAI to native - hex"2646478b0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000001bc16d674ec80000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000002f70244563dc70000000000000000000000007f2922c09dd671055c5abbc4f5657f874c18062900000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000073026B175474E89094C44Da98b954EedeAC495271d0F01ffff00A478c2975Ab1Ea89e8196811F51A7B7Ade33eB1101e43ca1Dee3F0fc1e2df73A0745674545F11A59F5000bb801C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc201ffff02007F2922c09DD671055C5aBBC4F5657f874c18062900000000000000000000000000", + abi.encodeWithSelector( + uniswap.swapExactTokensForETH.selector, // Use swapExactTokensForETH + fromAmount, + amountOutMin, + path, + address(gasZipPeriphery), // Send ETH to GasZipPeriphery + block.timestamp + 20 minutes + ), true ); } } + +contract MockFailingDexWithCustomError { + error SwapFailed(); + + function swapExactTokensForETH( + uint256, + uint256, + address[] calldata, + address, + uint256 + ) external pure { + revert SwapFailed(); + } + + fallback() external { + revert SwapFailed(); + } + + receive() external payable {} +}