From 58984a13019b88dd22f73c87bb204d6578cddde1 Mon Sep 17 00:00:00 2001 From: Leonardo Cascianelli Date: Thu, 2 May 2024 15:30:42 +0700 Subject: [PATCH 001/100] wip zip --- config/gaszip.json | 73 ++++++++++++++++ script/deploy/facets/DeployGasZip.s.sol | 42 +++++++++ src/Periphery/GasZip.sol | 111 ++++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 config/gaszip.json create mode 100644 script/deploy/facets/DeployGasZip.s.sol create mode 100644 src/Periphery/GasZip.sol diff --git a/config/gaszip.json b/config/gaszip.json new file mode 100644 index 000000000..522407bb4 --- /dev/null +++ b/config/gaszip.json @@ -0,0 +1,73 @@ +{ + "withdrawWallet": "0x0000000000000000000000000000000000000000", + "uniswapRouters": { + "mainnet": "0x8731d54E9D02c286767d56ac03e8037C07e01e98", + "arbitrum": "0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614", + "aurora": "0x0000000000000000000000000000000000000000", + "avalanche": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", + "base": "0x45f1A95A4D3f3836523F5c83673c797f4d4d263B", + "boba": "0x0000000000000000000000000000000000000000", + "bsc": "0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8", + "celo": "0x0000000000000000000000000000000000000000", + "cronos": "0x0000000000000000000000000000000000000000", + "evmos": "0x0000000000000000000000000000000000000000", + "fantom": "0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6", + "fuse": "0x0000000000000000000000000000000000000000", + "harmony": "0x0000000000000000000000000000000000000000", + "heco": "0x0000000000000000000000000000000000000000", + "gnosis": "0x0000000000000000000000000000000000000000", + "moonbeam": "0x0000000000000000000000000000000000000000", + "moonriver": "0x0000000000000000000000000000000000000000", + "nova": "0x0000000000000000000000000000000000000000", + "okx": "0x0000000000000000000000000000000000000000", + "opbnb": "0x0000000000000000000000000000000000000000", + "optimism": "0xB0D502E938ed5f4df2E681fE6E419ff29631d62b", + "polygon": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", + "polygonzkevm": "0x0000000000000000000000000000000000000000", + "velas": "0x0000000000000000000000000000000000000000", + "goerli": "0x7612aE2a34E5A363E137De748801FB4c86499152", + "bsc-testnet": "0x0000000000000000000000000000000000000000", + "lineatest": "0x0000000000000000000000000000000000000000", + "linea": "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590", + "metis": "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590", + "scroll": "0x36d4686e19c052787D7f24E6913cEbC025714895", + "localanvil": "0x0000000000000000000000000000000000000000", + "mumbai": "0x817436a076060D158204d955E5403b6Ed0A5fac0", + "sepolia": "0x0000000000000000000000000000000000000000" + }, + "gasZipRouters": { + "mainnet": "0x8731d54E9D02c286767d56ac03e8037C07e01e98", + "arbitrum": "0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614", + "aurora": "0x0000000000000000000000000000000000000000", + "avalanche": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", + "base": "0x45f1A95A4D3f3836523F5c83673c797f4d4d263B", + "boba": "0x0000000000000000000000000000000000000000", + "bsc": "0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8", + "celo": "0x0000000000000000000000000000000000000000", + "cronos": "0x0000000000000000000000000000000000000000", + "evmos": "0x0000000000000000000000000000000000000000", + "fantom": "0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6", + "fuse": "0x0000000000000000000000000000000000000000", + "harmony": "0x0000000000000000000000000000000000000000", + "heco": "0x0000000000000000000000000000000000000000", + "gnosis": "0x0000000000000000000000000000000000000000", + "moonbeam": "0x0000000000000000000000000000000000000000", + "moonriver": "0x0000000000000000000000000000000000000000", + "nova": "0x0000000000000000000000000000000000000000", + "okx": "0x0000000000000000000000000000000000000000", + "opbnb": "0x0000000000000000000000000000000000000000", + "optimism": "0xB0D502E938ed5f4df2E681fE6E419ff29631d62b", + "polygon": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", + "polygonzkevm": "0x0000000000000000000000000000000000000000", + "velas": "0x0000000000000000000000000000000000000000", + "goerli": "0x7612aE2a34E5A363E137De748801FB4c86499152", + "bsc-testnet": "0x0000000000000000000000000000000000000000", + "lineatest": "0x0000000000000000000000000000000000000000", + "linea": "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590", + "metis": "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590", + "scroll": "0x36d4686e19c052787D7f24E6913cEbC025714895", + "localanvil": "0x0000000000000000000000000000000000000000", + "mumbai": "0x817436a076060D158204d955E5403b6Ed0A5fac0", + "sepolia": "0x0000000000000000000000000000000000000000" + } +} diff --git a/script/deploy/facets/DeployGasZip.s.sol b/script/deploy/facets/DeployGasZip.s.sol new file mode 100644 index 000000000..b0ba637c5 --- /dev/null +++ b/script/deploy/facets/DeployGasZip.s.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { GasZip } from "lifi/Periphery/GasZip.sol"; +import { stdJson } from "forge-std/Script.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("GasZip") {} + + function run() + public + returns (GasZip deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + + deployed = GasZip(deploy(type(GasZip).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + string memory gasZipConfig = string.concat( + root, + "/config/gaszip.json" + ); + + string memory gasZipConfigJson = vm.readFile(gasZipConfig); + + address uniswapRouter = gasZipConfigJson.readAddress( + string.concat(".uniswapRouters.", network) + ); + address gasZipRouters = gasZipConfigJson.readAddress( + string.concat(".gasZipRouters.", network) + ); + address owner = gasZipConfigJson.readAddress( + string.concat(".withdrawWallet") + ); + + return abi.encode(owner, uniswapRouter, gasZipRouters); + } +} diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol new file mode 100644 index 000000000..cd97e5f7d --- /dev/null +++ b/src/Periphery/GasZip.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.17; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { LibAsset } from "../Libraries/LibAsset.sol"; +import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; + +struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; +} + +interface ISwapRouter { + function exactInputSingle( + ExactInputSingleParams memory params + ) external returns (uint256 amountOut); +} + +interface IGasZip { + function deposit( + uint256 destinationChain, + address recipient + ) external payable; +} + +/// @title Fee Collector +/// @author LI.FI (https://li.fi) +/// @notice Provides functionality for collecting integrator fees +/// @custom:version 1.0.0 +contract GasZip is TransferrableOwnership { + address public immutable ZERO = address(0); + + /// State /// + + mapping(address => bool) public allowedInboundTokens; + ISwapRouter public immutable uniswapRouter; + IGasZip public immutable gasZipRouter; + + /// Errors /// + error SwapFailed(address, address); + error GasZipFailed(uint256); + error TransferFailed(); + + /// Events /// + + /// Constructor /// + + // solhint-disable-next-line no-empty-blocks + constructor( + address _owner, + address _uniswapRouter, + address _gasZipRouter + ) TransferrableOwnership(_owner) { + uniswapRouter = ISwapRouter(_uniswapRouter); + gasZipRouter = IGasZip(_gasZipRouter); + } + + function allowToken(address token, bool allowed) external onlyOwner { + allowedInboundTokens[token] = allowed; + } + + function zipERC20( + address fromToken, + uint256 minAmount, + uint256 destinationChain, + address recipient + ) public { + uint256 availableNative = swapERC20(fromToken, minAmount); + gasZipRouter.deposit{ value: availableNative }( + destinationChain, + recipient + ); + } + + function zip( + uint256 amountToZip, + uint256 destinationChain, + address recipient + ) public payable { + gasZipRouter.deposit{ value: amountToZip }( + destinationChain, + recipient + ); + (bool success, ) = msg.sender.call{ value: address(this).balance }(""); + if (!success) revert TransferFailed(); + } + + function swapERC20( + address fromERC20, + uint256 minAmount + ) internal returns (uint256) { + return + uniswapRouter.exactInputSingle( + ExactInputSingleParams({ + tokenIn: fromERC20, + tokenOut: ZERO, + fee: 3000, + recipient: address(this), + deadline: block.timestamp, + amountIn: LibAsset.getOwnBalance(fromERC20), + amountOutMinimum: minAmount, + sqrtPriceLimitX96: 0 + }) + ); + } +} From d7fae0a0e35d83373b2206d872c7559676c4633d Mon Sep 17 00:00:00 2001 From: Leonardo Cascianelli Date: Sun, 5 May 2024 15:54:51 +0700 Subject: [PATCH 002/100] feat: zip erc20 with libswap --- config/gaszip.json | 35 ----------------------- deployments/_deployments_log_file.json | 16 +++++++++++ deployments/bsc.diamond.staging.json | 3 +- deployments/bsc.staging.json | 3 +- script/deploy/_targetState.json | 5 ++++ script/deploy/facets/DeployGasZip.s.sol | 7 ++--- src/Periphery/GasZip.sol | 38 +++++++------------------ 7 files changed, 38 insertions(+), 69 deletions(-) diff --git a/config/gaszip.json b/config/gaszip.json index 522407bb4..6fa96c043 100644 --- a/config/gaszip.json +++ b/config/gaszip.json @@ -1,40 +1,5 @@ { "withdrawWallet": "0x0000000000000000000000000000000000000000", - "uniswapRouters": { - "mainnet": "0x8731d54E9D02c286767d56ac03e8037C07e01e98", - "arbitrum": "0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614", - "aurora": "0x0000000000000000000000000000000000000000", - "avalanche": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", - "base": "0x45f1A95A4D3f3836523F5c83673c797f4d4d263B", - "boba": "0x0000000000000000000000000000000000000000", - "bsc": "0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8", - "celo": "0x0000000000000000000000000000000000000000", - "cronos": "0x0000000000000000000000000000000000000000", - "evmos": "0x0000000000000000000000000000000000000000", - "fantom": "0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6", - "fuse": "0x0000000000000000000000000000000000000000", - "harmony": "0x0000000000000000000000000000000000000000", - "heco": "0x0000000000000000000000000000000000000000", - "gnosis": "0x0000000000000000000000000000000000000000", - "moonbeam": "0x0000000000000000000000000000000000000000", - "moonriver": "0x0000000000000000000000000000000000000000", - "nova": "0x0000000000000000000000000000000000000000", - "okx": "0x0000000000000000000000000000000000000000", - "opbnb": "0x0000000000000000000000000000000000000000", - "optimism": "0xB0D502E938ed5f4df2E681fE6E419ff29631d62b", - "polygon": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", - "polygonzkevm": "0x0000000000000000000000000000000000000000", - "velas": "0x0000000000000000000000000000000000000000", - "goerli": "0x7612aE2a34E5A363E137De748801FB4c86499152", - "bsc-testnet": "0x0000000000000000000000000000000000000000", - "lineatest": "0x0000000000000000000000000000000000000000", - "linea": "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590", - "metis": "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590", - "scroll": "0x36d4686e19c052787D7f24E6913cEbC025714895", - "localanvil": "0x0000000000000000000000000000000000000000", - "mumbai": "0x817436a076060D158204d955E5403b6Ed0A5fac0", - "sepolia": "0x0000000000000000000000000000000000000000" - }, "gasZipRouters": { "mainnet": "0x8731d54E9D02c286767d56ac03e8037C07e01e98", "arbitrum": "0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614", diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index fd5b75c27..1e2f3ef34 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -17320,5 +17320,21 @@ ] } } + }, + "GasZip": { + "bsc": { + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x70f365dC8d4cF91C2318337157E76BB9c786C116", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-05-02 16:20:25", + "CONSTRUCTOR_ARGS": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a364f8c717caad9a442737eb7b8a55cc6cf18d80000000000000000000000004a364f8c717caad9a442737eb7b8a55cc6cf18d8", + "SALT": "", + "VERIFIED": "false" + } + ] + } + } } } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 16ef7f51f..0e7089705 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -79,7 +79,8 @@ "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", "RelayerCelerIM": "", "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZip": "0x70f365dC8d4cF91C2318337157E76BB9c786C116" } } } \ No newline at end of file diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 9add3c733..54e20e89b 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -24,5 +24,6 @@ "OFTWrapperFacet": "0x3004db169fa7956609A872736452E4951D4BDA8b", "StargateFacet": "0xAfcC5c55d5Ec3082675D51331E7Ed9AdE195db48", "ThorSwapFacet": "0xa6aAe470E7B8E8916e692882A5db25bB40C398A7", - "AmarokFacetPacked": "0x7ac3EB2D191EBAb9E925CAbFD4F8155be066b3aa" + "AmarokFacetPacked": "0x7ac3EB2D191EBAb9E925CAbFD4F8155be066b3aa", + "GasZip": "0x70f365dC8d4cF91C2318337157E76BB9c786C116" } \ No newline at end of file diff --git a/script/deploy/_targetState.json b/script/deploy/_targetState.json index 8e86f7bf8..9302e6e89 100644 --- a/script/deploy/_targetState.json +++ b/script/deploy/_targetState.json @@ -354,6 +354,11 @@ } }, "bsc": { + "staging": { + "LiFiDiamond": { + "GasZip": "1.0.0" + } + }, "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", diff --git a/script/deploy/facets/DeployGasZip.s.sol b/script/deploy/facets/DeployGasZip.s.sol index b0ba637c5..835e29e2d 100644 --- a/script/deploy/facets/DeployGasZip.s.sol +++ b/script/deploy/facets/DeployGasZip.s.sol @@ -27,16 +27,13 @@ contract DeployScript is DeployScriptBase { string memory gasZipConfigJson = vm.readFile(gasZipConfig); - address uniswapRouter = gasZipConfigJson.readAddress( - string.concat(".uniswapRouters.", network) - ); - address gasZipRouters = gasZipConfigJson.readAddress( + address gasZipRouter = gasZipConfigJson.readAddress( string.concat(".gasZipRouters.", network) ); address owner = gasZipConfigJson.readAddress( string.concat(".withdrawWallet") ); - return abi.encode(owner, uniswapRouter, gasZipRouters); + return abi.encode(owner, gasZipRouter); } } diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index cd97e5f7d..799993578 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.17; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LibAsset } from "../Libraries/LibAsset.sol"; +import { LibSwap } from "../Libraries/LibSwap.sol"; import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; struct ExactInputSingleParams { @@ -38,25 +39,27 @@ contract GasZip is TransferrableOwnership { /// State /// mapping(address => bool) public allowedInboundTokens; - ISwapRouter public immutable uniswapRouter; IGasZip public immutable gasZipRouter; /// Errors /// error SwapFailed(address, address); error GasZipFailed(uint256); error TransferFailed(); + error InboundTokenDisallowed(); /// Events /// /// Constructor /// - // solhint-disable-next-line no-empty-blocks + modifier inboundTokenIsAllowed(address token) { + if(!allowedInboundTokens[token]) revert InboundTokenDisallowed(); + _; + } + constructor( address _owner, - address _uniswapRouter, address _gasZipRouter ) TransferrableOwnership(_owner) { - uniswapRouter = ISwapRouter(_uniswapRouter); gasZipRouter = IGasZip(_gasZipRouter); } @@ -65,12 +68,12 @@ contract GasZip is TransferrableOwnership { } function zipERC20( - address fromToken, - uint256 minAmount, + LibSwap.SwapData calldata _swap, uint256 destinationChain, address recipient - ) public { - uint256 availableNative = swapERC20(fromToken, minAmount); + ) inboundTokenIsAllowed(_swap.sendingAssetId) public { + LibSwap.swap(0, _swap); + uint256 availableNative = LibAsset.getOwnBalance(ZERO); gasZipRouter.deposit{ value: availableNative }( destinationChain, recipient @@ -89,23 +92,4 @@ contract GasZip is TransferrableOwnership { (bool success, ) = msg.sender.call{ value: address(this).balance }(""); if (!success) revert TransferFailed(); } - - function swapERC20( - address fromERC20, - uint256 minAmount - ) internal returns (uint256) { - return - uniswapRouter.exactInputSingle( - ExactInputSingleParams({ - tokenIn: fromERC20, - tokenOut: ZERO, - fee: 3000, - recipient: address(this), - deadline: block.timestamp, - amountIn: LibAsset.getOwnBalance(fromERC20), - amountOutMinimum: minAmount, - sqrtPriceLimitX96: 0 - }) - ); - } } From 978955b183310ea957bcf7018774ec22ebbb8a81 Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Thu, 20 Jun 2024 17:31:56 +0200 Subject: [PATCH 003/100] chore: update bsc gaszip address --- config/gaszip.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/gaszip.json b/config/gaszip.json index 6fa96c043..af42debfc 100644 --- a/config/gaszip.json +++ b/config/gaszip.json @@ -7,7 +7,7 @@ "avalanche": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", "base": "0x45f1A95A4D3f3836523F5c83673c797f4d4d263B", "boba": "0x0000000000000000000000000000000000000000", - "bsc": "0x4a364f8c717cAAD9A442737Eb7b8A55cc6cf18D8", + "bsc": "0xC7FC1802D86aE031EC979158F8E1D90bAb30dBC1", "celo": "0x0000000000000000000000000000000000000000", "cronos": "0x0000000000000000000000000000000000000000", "evmos": "0x0000000000000000000000000000000000000000", From 277f7e802675383d093a60cf8ff66e8a3477693d Mon Sep 17 00:00:00 2001 From: Max Klenk Date: Tue, 25 Jun 2024 07:40:58 +0200 Subject: [PATCH 004/100] clean --- src/Periphery/GasZip.sol | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 799993578..6b1d6d21d 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -1,27 +1,10 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + import { LibAsset } from "../Libraries/LibAsset.sol"; import { LibSwap } from "../Libraries/LibSwap.sol"; import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; -struct ExactInputSingleParams { - address tokenIn; - address tokenOut; - uint24 fee; - address recipient; - uint256 deadline; - uint256 amountIn; - uint256 amountOutMinimum; - uint160 sqrtPriceLimitX96; -} - -interface ISwapRouter { - function exactInputSingle( - ExactInputSingleParams memory params - ) external returns (uint256 amountOut); -} - interface IGasZip { function deposit( uint256 destinationChain, @@ -29,9 +12,9 @@ interface IGasZip { ) external payable; } -/// @title Fee Collector +/// @title GasZip /// @author LI.FI (https://li.fi) -/// @notice Provides functionality for collecting integrator fees +/// @notice Provides functionality to swap and trigger gaz.zip protocol /// @custom:version 1.0.0 contract GasZip is TransferrableOwnership { address public immutable ZERO = address(0); @@ -52,7 +35,7 @@ contract GasZip is TransferrableOwnership { /// Constructor /// modifier inboundTokenIsAllowed(address token) { - if(!allowedInboundTokens[token]) revert InboundTokenDisallowed(); + if (!allowedInboundTokens[token]) revert InboundTokenDisallowed(); _; } @@ -71,7 +54,7 @@ contract GasZip is TransferrableOwnership { LibSwap.SwapData calldata _swap, uint256 destinationChain, address recipient - ) inboundTokenIsAllowed(_swap.sendingAssetId) public { + ) public inboundTokenIsAllowed(_swap.sendingAssetId) { LibSwap.swap(0, _swap); uint256 availableNative = LibAsset.getOwnBalance(ZERO); gasZipRouter.deposit{ value: availableNative }( From dc58aa20c45242f2e5a81a949058c23d170d4a43 Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 12:33:35 +0200 Subject: [PATCH 005/100] chore: updated gaszip routers + add docs --- config/gaszip.json | 48 +++++++++------------------- deployments/bsc.diamond.staging.json | 4 +-- deployments/bsc.staging.json | 4 +-- docs/GasZip.md | 38 ++++++++++++++++++++++ 4 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 docs/GasZip.md diff --git a/config/gaszip.json b/config/gaszip.json index af42debfc..8d728beea 100644 --- a/config/gaszip.json +++ b/config/gaszip.json @@ -1,38 +1,20 @@ { "withdrawWallet": "0x0000000000000000000000000000000000000000", "gasZipRouters": { - "mainnet": "0x8731d54E9D02c286767d56ac03e8037C07e01e98", - "arbitrum": "0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614", - "aurora": "0x0000000000000000000000000000000000000000", - "avalanche": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", - "base": "0x45f1A95A4D3f3836523F5c83673c797f4d4d263B", - "boba": "0x0000000000000000000000000000000000000000", - "bsc": "0xC7FC1802D86aE031EC979158F8E1D90bAb30dBC1", - "celo": "0x0000000000000000000000000000000000000000", - "cronos": "0x0000000000000000000000000000000000000000", - "evmos": "0x0000000000000000000000000000000000000000", - "fantom": "0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6", - "fuse": "0x0000000000000000000000000000000000000000", - "harmony": "0x0000000000000000000000000000000000000000", - "heco": "0x0000000000000000000000000000000000000000", - "gnosis": "0x0000000000000000000000000000000000000000", - "moonbeam": "0x0000000000000000000000000000000000000000", - "moonriver": "0x0000000000000000000000000000000000000000", - "nova": "0x0000000000000000000000000000000000000000", - "okx": "0x0000000000000000000000000000000000000000", - "opbnb": "0x0000000000000000000000000000000000000000", - "optimism": "0xB0D502E938ed5f4df2E681fE6E419ff29631d62b", - "polygon": "0x45A01E4e04F14f7A4a6702c74187c5F6222033cd", - "polygonzkevm": "0x0000000000000000000000000000000000000000", - "velas": "0x0000000000000000000000000000000000000000", - "goerli": "0x7612aE2a34E5A363E137De748801FB4c86499152", - "bsc-testnet": "0x0000000000000000000000000000000000000000", - "lineatest": "0x0000000000000000000000000000000000000000", - "linea": "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590", - "metis": "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590", - "scroll": "0x36d4686e19c052787D7f24E6913cEbC025714895", - "localanvil": "0x0000000000000000000000000000000000000000", - "mumbai": "0x817436a076060D158204d955E5403b6Ed0A5fac0", - "sepolia": "0x0000000000000000000000000000000000000000" + "mainnet": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", + "arbitrum": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", + "blast": "0x6d4Ac5Ba1D649030227718C05ca4399F8522828b", + "avalanche": "0x5Fa63e70a6aa982bDaDbdc0f520B32dB7CADA1eF", + "base": "0x40a132f0ed6e1d63621586db1fabfb8a7587f2ac", + "mode": "0xB7D54342E5d993FE38897ECf7B5081eF5cdB5c18", + "bsc": "0x85e5fb57844be79b42997c898d177a39f328ccf0", + "zksync": "0x370475E7f574CA67C9bf9C43fc57191545C2a84b", + "mantle": "0x4C80aa9cEc1651DC42Bfca15BBe08bb6bbf8AbBC", + "gnosis": "0x87709e691A8C2037407eCdB4C271000ec2caC6Ee", + "optimism": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", + "polygon": "0xea0b31a1bb831ca9b32d950fc17f369444744a8e", + "linea": "0x6dB5Dd4eaaAd3C1B1890BDF81272a79E6223c051", + "metis": "0x73aaa8230Cd7564b088a8CD1fC7447B7D4C0Bb3c", + "scroll": "0x5B5040638C7e6283d6D0780b09a854d395C12e59" } } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 0e7089705..5e056ff00 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -80,7 +80,7 @@ "RelayerCelerIM": "", "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZip": "0x70f365dC8d4cF91C2318337157E76BB9c786C116" + "GasZip": "0xC7FC1802D86aE031EC979158F8E1D90bAb30dBC1" } } -} \ No newline at end of file +} diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 54e20e89b..5e9f2d09d 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -25,5 +25,5 @@ "StargateFacet": "0xAfcC5c55d5Ec3082675D51331E7Ed9AdE195db48", "ThorSwapFacet": "0xa6aAe470E7B8E8916e692882A5db25bB40C398A7", "AmarokFacetPacked": "0x7ac3EB2D191EBAb9E925CAbFD4F8155be066b3aa", - "GasZip": "0x70f365dC8d4cF91C2318337157E76BB9c786C116" -} \ No newline at end of file + "GasZip": "0xC7FC1802D86aE031EC979158F8E1D90bAb30dBC1" +} diff --git a/docs/GasZip.md b/docs/GasZip.md new file mode 100644 index 000000000..2f7f40465 --- /dev/null +++ b/docs/GasZip.md @@ -0,0 +1,38 @@ +# GasZip + +## Description + +Periphery contract used for sending gas on requested chain. + +## How To Use + +The contract is meant to be used to call the GasZip routers which will execute the refueling part. + +There are two methods. +One for ERC20 tokens + +```solidity +/// @notice Refuel the gas on the requested chain from ERC20 token +/// @param _swap data needed for swap before the router call +/// @param destinationChain native token will be received on this chain +/// @param recipient address that will receive tokens in the end +function zipERC20( + SwapData calldata _swap, + uint256 destinationChain, + address recipient +) +``` + +and another for Native tokens (here no swap before the router call will be performed) + +```solidity +/// @notice Refuel the gas on the requested chain from native token +/// @param amountToZip amount of sending token +/// @param destinationChain native token will be received on this chain +/// @param recipient address that will receive tokens in the end +function zipERC20( + uint256 amountToZip, + uint256 destinationChain, + address recipient +) +``` From 384462e1605b9cb4bba45b8eefb725dbc1228739 Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 13:18:31 +0200 Subject: [PATCH 006/100] chore: deployRequirements --- script/deploy/resources/deployRequirements.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/script/deploy/resources/deployRequirements.json b/script/deploy/resources/deployRequirements.json index f56ff988d..9e7fe5908 100644 --- a/script/deploy/resources/deployRequirements.json +++ b/script/deploy/resources/deployRequirements.json @@ -430,5 +430,19 @@ "allowToDeployWithZeroAddress": "false" } } + }, + "GasZip": { + "configData": { + "_owner": { + "configFileName": "gaszip.json", + "keyInConfigFile": ".withdrawWallet", + "allowToDeployWithZeroAddress": "false" + }, + "_gasZipRouter": { + "configFileName": "gaszip.json", + "keyInConfigFile": ".gasZipRouters.", + "allowToDeployWithZeroAddress": "false" + } + } } } From c46f5755a129ff598df0003ab58b74084d116de2 Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 13:57:20 +0200 Subject: [PATCH 007/100] chore: remove allowedToken --- src/Periphery/GasZip.sol | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 6b1d6d21d..3117c7134 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -20,8 +20,6 @@ contract GasZip is TransferrableOwnership { address public immutable ZERO = address(0); /// State /// - - mapping(address => bool) public allowedInboundTokens; IGasZip public immutable gasZipRouter; /// Errors /// @@ -33,12 +31,6 @@ contract GasZip is TransferrableOwnership { /// Events /// /// Constructor /// - - modifier inboundTokenIsAllowed(address token) { - if (!allowedInboundTokens[token]) revert InboundTokenDisallowed(); - _; - } - constructor( address _owner, address _gasZipRouter @@ -46,21 +38,19 @@ contract GasZip is TransferrableOwnership { gasZipRouter = IGasZip(_gasZipRouter); } - function allowToken(address token, bool allowed) external onlyOwner { - allowedInboundTokens[token] = allowed; - } - function zipERC20( LibSwap.SwapData calldata _swap, uint256 destinationChain, address recipient - ) public inboundTokenIsAllowed(_swap.sendingAssetId) { + ) public { LibSwap.swap(0, _swap); uint256 availableNative = LibAsset.getOwnBalance(ZERO); gasZipRouter.deposit{ value: availableNative }( destinationChain, recipient ); + (bool success, ) = msg.sender.call{ value: address(this).balance }(""); + if (!success) revert TransferFailed(); } function zip( From 6d4a65b705c21756ad14d7dc7ff2de429937518a Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 14:03:27 +0200 Subject: [PATCH 008/100] chore: withdraw wallet from global config --- config/gaszip.json | 1 - script/deploy/facets/DeployGasZip.s.sol | 12 +++++++++--- script/deploy/resources/deployRequirements.json | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/config/gaszip.json b/config/gaszip.json index 8d728beea..92050cbdd 100644 --- a/config/gaszip.json +++ b/config/gaszip.json @@ -1,5 +1,4 @@ { - "withdrawWallet": "0x0000000000000000000000000000000000000000", "gasZipRouters": { "mainnet": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", "arbitrum": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", diff --git a/script/deploy/facets/DeployGasZip.s.sol b/script/deploy/facets/DeployGasZip.s.sol index 835e29e2d..e26c0e5a5 100644 --- a/script/deploy/facets/DeployGasZip.s.sol +++ b/script/deploy/facets/DeployGasZip.s.sol @@ -25,14 +25,20 @@ contract DeployScript is DeployScriptBase { "/config/gaszip.json" ); + string memory globalConfigPath = string.concat( + root, + "/config/global.json" + ); + string memory gasZipConfigJson = vm.readFile(gasZipConfig); + string memory globalConfigJson = vm.readFile(globalConfigPath); + address gasZipRouter = gasZipConfigJson.readAddress( string.concat(".gasZipRouters.", network) ); - address owner = gasZipConfigJson.readAddress( - string.concat(".withdrawWallet") - ); + + address owner = globalConfigJson.readAddress(".withdrawWallet"); return abi.encode(owner, gasZipRouter); } diff --git a/script/deploy/resources/deployRequirements.json b/script/deploy/resources/deployRequirements.json index 9e7fe5908..321a72b73 100644 --- a/script/deploy/resources/deployRequirements.json +++ b/script/deploy/resources/deployRequirements.json @@ -434,7 +434,7 @@ "GasZip": { "configData": { "_owner": { - "configFileName": "gaszip.json", + "configFileName": "global.json", "keyInConfigFile": ".withdrawWallet", "allowToDeployWithZeroAddress": "false" }, From 4befbe66fc0e62f7704695bb159d90483668d7c2 Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 14:19:50 +0200 Subject: [PATCH 009/100] chore: comments --- src/Periphery/GasZip.sol | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 3117c7134..31629e589 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -49,8 +49,15 @@ contract GasZip is TransferrableOwnership { destinationChain, recipient ); - (bool success, ) = msg.sender.call{ value: address(this).balance }(""); - if (!success) revert TransferFailed(); + + // Send back any remaining sendingAsset token to the sender + IERC20 sendingAsset = IERC20(_swap.sendingAssetId); + uint256 remainingBalance = sendingAsset.balanceOf(address(this)); + + if (remainingBalance > 0) { + bool success = sendingAsset.transfer(msg.sender, remainingBalance); + if (!success) revert TransferFailed(); + } } function zip( @@ -62,7 +69,13 @@ contract GasZip is TransferrableOwnership { destinationChain, recipient ); - (bool success, ) = msg.sender.call{ value: address(this).balance }(""); - if (!success) revert TransferFailed(); + uint256 nativeBalance = address(this).balance; + + if (nativeBalance > 0) { + (bool success, ) = msg.sender.call{ value: address(this).balance }( + "" + ); + if (!success) revert TransferFailed(); + } } } From 11f2132f3393f7c9fb9e86c25c6f080132539f3d Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 14:24:41 +0200 Subject: [PATCH 010/100] chore: import --- src/Periphery/GasZip.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 31629e589..2e049abdb 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { LibAsset } from "../Libraries/LibAsset.sol"; import { LibSwap } from "../Libraries/LibSwap.sol"; import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; From 5e6f2d11b314ec5c55f979b4688a0bdeb78d656b Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 14:37:39 +0200 Subject: [PATCH 011/100] chore: remove owner --- lib/solmate | 2 +- script/deploy/facets/DeployGasZip.s.sol | 11 +---------- script/deploy/resources/deployRequirements.json | 5 ----- src/Periphery/GasZip.sol | 5 +---- 4 files changed, 3 insertions(+), 20 deletions(-) diff --git a/lib/solmate b/lib/solmate index 2001af43a..c89230993 160000 --- a/lib/solmate +++ b/lib/solmate @@ -1 +1 @@ -Subproject commit 2001af43aedb46fdc2335d2a7714fb2dae7cfcd1 +Subproject commit c892309933b25c03d32b1b0d674df7ae292ba925 diff --git a/script/deploy/facets/DeployGasZip.s.sol b/script/deploy/facets/DeployGasZip.s.sol index e26c0e5a5..8d95f31ad 100644 --- a/script/deploy/facets/DeployGasZip.s.sol +++ b/script/deploy/facets/DeployGasZip.s.sol @@ -25,21 +25,12 @@ contract DeployScript is DeployScriptBase { "/config/gaszip.json" ); - string memory globalConfigPath = string.concat( - root, - "/config/global.json" - ); - string memory gasZipConfigJson = vm.readFile(gasZipConfig); - string memory globalConfigJson = vm.readFile(globalConfigPath); - address gasZipRouter = gasZipConfigJson.readAddress( string.concat(".gasZipRouters.", network) ); - address owner = globalConfigJson.readAddress(".withdrawWallet"); - - return abi.encode(owner, gasZipRouter); + return abi.encode(gasZipRouter); } } diff --git a/script/deploy/resources/deployRequirements.json b/script/deploy/resources/deployRequirements.json index 321a72b73..f080990df 100644 --- a/script/deploy/resources/deployRequirements.json +++ b/script/deploy/resources/deployRequirements.json @@ -433,11 +433,6 @@ }, "GasZip": { "configData": { - "_owner": { - "configFileName": "global.json", - "keyInConfigFile": ".withdrawWallet", - "allowToDeployWithZeroAddress": "false" - }, "_gasZipRouter": { "configFileName": "gaszip.json", "keyInConfigFile": ".gasZipRouters.", diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 2e049abdb..8dcae0313 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -32,10 +32,7 @@ contract GasZip is TransferrableOwnership { /// Events /// /// Constructor /// - constructor( - address _owner, - address _gasZipRouter - ) TransferrableOwnership(_owner) { + constructor(address _gasZipRouter) { gasZipRouter = IGasZip(_gasZipRouter); } From db0a14176fb7d389f750380a033f9b8bea467b46 Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 14:39:12 +0200 Subject: [PATCH 012/100] chore: unused import --- src/Periphery/GasZip.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 8dcae0313..0127946de 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.17; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { LibAsset } from "../Libraries/LibAsset.sol"; import { LibSwap } from "../Libraries/LibSwap.sol"; -import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; interface IGasZip { function deposit( @@ -17,7 +16,7 @@ interface IGasZip { /// @author LI.FI (https://li.fi) /// @notice Provides functionality to swap and trigger gaz.zip protocol /// @custom:version 1.0.0 -contract GasZip is TransferrableOwnership { +contract GasZip { address public immutable ZERO = address(0); /// State /// From aea3404bb398851b6ac70b59e43f5fdee80514ad Mon Sep 17 00:00:00 2001 From: Daniela Chybisova Date: Tue, 25 Jun 2024 16:28:03 +0200 Subject: [PATCH 013/100] chore: remove unused errors --- src/Periphery/GasZip.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 0127946de..335a6ae7b 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -23,10 +23,7 @@ contract GasZip { IGasZip public immutable gasZipRouter; /// Errors /// - error SwapFailed(address, address); - error GasZipFailed(uint256); error TransferFailed(); - error InboundTokenDisallowed(); /// Events /// From 423d3b6d231318088f27155be0b6d70bd176a2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 10:57:03 +0700 Subject: [PATCH 014/100] forge install: solady v0.0.208 --- .gitmodules | 3 +++ lib/solady | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/solady diff --git a/.gitmodules b/.gitmodules index c94340a43..916ca16ca 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts +[submodule "lib/solady"] + path = lib/solady + url = https://github.com/Vectorized/solady diff --git a/lib/solady b/lib/solady new file mode 160000 index 000000000..678c91635 --- /dev/null +++ b/lib/solady @@ -0,0 +1 @@ +Subproject commit 678c9163550810b08f0ffb09624c9f7532392303 From e61e22bd30a20de960caf9efed93f91e27a0e2f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 10:57:05 +0700 Subject: [PATCH 015/100] forge install: solady v0.0.208 --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 916ca16ca..82d55abfc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,4 +21,4 @@ url = https://github.com/openzeppelin/openzeppelin-contracts [submodule "lib/solady"] path = lib/solady - url = https://github.com/Vectorized/solady + url = https://github.com/lib/solady From 123df8739d43f84b1d42cacfcd3edd1466e1b129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 13:22:06 +0700 Subject: [PATCH 016/100] added test and fixed some issues in the contract --- .gitmodules | 1 + lcov-filtered.info | 1868 +++++++++++++------------- remappings.txt | 1 + solady | 1 + src/Periphery/GasZip.sol | 83 +- test/solidity/Periphery/GasZip.t.sol | 128 ++ 6 files changed, 1152 insertions(+), 930 deletions(-) create mode 160000 solady create mode 100644 test/solidity/Periphery/GasZip.t.sol diff --git a/.gitmodules b/.gitmodules index 82d55abfc..a41d0934f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,4 @@ [submodule "lib/solady"] path = lib/solady url = https://github.com/lib/solady + diff --git a/lcov-filtered.info b/lcov-filtered.info index f45509dbb..683b6eb1d 100644 --- a/lcov-filtered.info +++ b/lcov-filtered.info @@ -860,208 +860,229 @@ DA:89,8 DA:89,8 DA:92,10 DA:92,10 -FN:110,CalldataVerificationFacet.extractGenericSwapParameters -FNDA:2,CalldataVerificationFacet.extractGenericSwapParameters +FN:106,CalldataVerificationFacet.extractNonEVMAddress +FNDA:4,CalldataVerificationFacet.extractNonEVMAddress +DA:109,4 +DA:109,4 +DA:110,4 +DA:110,4 +DA:110,4 +DA:113,4 +DA:113,4 +DA:113,4 +BRDA:112,2,0,2 +BRDA:112,2,1,4 +DA:116,2 +DA:116,2 +DA:120,4 +BRDA:120,3,0,2 +BRDA:120,3,1,2 DA:123,2 DA:123,2 -DA:124,2 -DA:124,2 -DA:127,2 -DA:127,2 -DA:127,2 -BRDA:126,2,0,1 -BRDA:126,2,1,2 -DA:130,1 -DA:130,1 -DA:132,2 -DA:132,2 -DA:137,2 -DA:137,2 -DA:138,2 -DA:138,2 -DA:139,2 -DA:139,2 -DA:140,2 -DA:140,2 -FN:162,CalldataVerificationFacet.validateCalldata +DA:128,2 +DA:128,2 +FN:140,CalldataVerificationFacet.extractGenericSwapParameters +FNDA:2,CalldataVerificationFacet.extractGenericSwapParameters +DA:153,2 +DA:153,2 +DA:154,2 +DA:154,2 +DA:157,2 +DA:157,2 +DA:157,2 +BRDA:156,4,0,1 +BRDA:156,4,1,2 +DA:160,1 +DA:160,1 +DA:162,2 +DA:162,2 +DA:167,2 +DA:167,2 +DA:168,2 +DA:168,2 +DA:169,2 +DA:169,2 +DA:170,2 +DA:170,2 +FN:192,CalldataVerificationFacet.validateCalldata FNDA:4,CalldataVerificationFacet.validateCalldata -DA:172,4 -DA:172,4 -DA:173,4 -DA:173,4 -DA:182,4 -DA:182,4 -DA:184,4 -DA:184,4 -DA:184,4 -DA:184,4 -DA:184,4 -DA:184,4 -DA:184,4 -DA:200,2 -DA:200,2 -DA:202,2 -DA:202,2 -FN:210,CalldataVerificationFacet.validateDestinationCalldata +DA:202,4 +DA:202,4 +DA:203,4 +DA:203,4 +DA:212,4 +DA:212,4 +DA:214,4 +DA:214,4 +DA:214,4 +DA:214,4 +DA:214,4 +DA:214,4 +DA:214,4 +DA:230,2 +DA:230,2 +DA:232,2 +DA:232,2 +FN:240,CalldataVerificationFacet.validateDestinationCalldata FNDA:18,CalldataVerificationFacet.validateDestinationCalldata -DA:215,18 -DA:215,18 -DA:219,18 -DA:219,18 -DA:219,18 -BRDA:218,3,0,9 -BRDA:218,3,1,18 -DA:221,9 -DA:221,9 -DA:224,18 -DA:224,18 -DA:224,18 -DA:227,18 -DA:227,18 -BRDA:227,4,0,2 -BRDA:227,4,1,4 -DA:228,4 -DA:228,4 -DA:228,4 -DA:233,4 -DA:233,4 -DA:234,4 -DA:234,4 -DA:234,4 -DA:234,4 -DA:234,4 -DA:235,2 -DA:235,2 -DA:235,2 -DA:238,14 -DA:238,14 -BRDA:237,5,0,2 -BRDA:237,5,1,2 -DA:240,2 -DA:240,2 -DA:240,2 -DA:244,2 -DA:244,2 -DA:245,2 -DA:245,2 -DA:245,2 -DA:245,2 -DA:245,2 -DA:246,2 -DA:246,2 -DA:246,2 -DA:250,12 -DA:250,12 -BRDA:250,6,0,2 -BRDA:250,6,1,4 -DA:251,4 -DA:251,4 -DA:251,4 -DA:255,4 -DA:255,4 -DA:256,4 -DA:256,4 -DA:256,4 -DA:256,4 -DA:256,4 -DA:257,2 -DA:257,2 -DA:257,2 -DA:257,2 -DA:260,8 -DA:260,8 -BRDA:259,7,0,2 -BRDA:259,7,1,2 -DA:263,2 -DA:263,2 -DA:263,2 -DA:271,2 -DA:271,2 -DA:272,2 -DA:272,2 -DA:272,2 -DA:272,2 -DA:272,2 -DA:273,2 -DA:273,2 -DA:273,2 -DA:273,2 -DA:277,6 -DA:277,6 -BRDA:276,8,0,3 -BRDA:276,8,1,5 -DA:279,5 -DA:279,5 -DA:279,5 -DA:283,5 -DA:283,5 -DA:284,5 -DA:284,5 -DA:284,5 -DA:284,5 -DA:284,5 -DA:285,3 -DA:285,3 -DA:285,3 -DA:285,3 -DA:288,1 -DA:288,1 -BRDA:287,9,0,1 -BRDA:287,9,1,1 -DA:291,1 -DA:291,1 -DA:291,1 -DA:295,1 -DA:295,1 -DA:296,1 -DA:296,1 -DA:296,1 -DA:296,1 -DA:296,1 -DA:297,1 -DA:297,1 -DA:297,1 -DA:297,1 -DA:301,0 -DA:301,0 -FN:309,CalldataVerificationFacet._extractBridgeData -FNDA:19,CalldataVerificationFacet._extractBridgeData -DA:313,19 -DA:313,19 -DA:313,19 -BRDA:312,10,0,9 -BRDA:312,10,1,10 -DA:316,9 -DA:316,9 -DA:316,9 -DA:317,9 -DA:317,9 -DA:321,9 -DA:321,9 -DA:324,10 -DA:324,10 -FN:330,CalldataVerificationFacet._extractSwapData +DA:245,18 +DA:245,18 +DA:249,18 +DA:249,18 +DA:249,18 +BRDA:248,5,0,9 +BRDA:248,5,1,18 +DA:251,9 +DA:251,9 +DA:254,18 +DA:254,18 +DA:254,18 +DA:257,18 +DA:257,18 +BRDA:257,6,0,2 +BRDA:257,6,1,4 +DA:258,4 +DA:258,4 +DA:258,4 +DA:263,4 +DA:263,4 +DA:264,4 +DA:264,4 +DA:264,4 +DA:264,4 +DA:264,4 +DA:265,2 +DA:265,2 +DA:265,2 +DA:268,14 +DA:268,14 +BRDA:267,7,0,2 +BRDA:267,7,1,2 +DA:270,2 +DA:270,2 +DA:270,2 +DA:274,2 +DA:274,2 +DA:275,2 +DA:275,2 +DA:275,2 +DA:275,2 +DA:275,2 +DA:276,2 +DA:276,2 +DA:276,2 +DA:280,12 +DA:280,12 +BRDA:280,8,0,2 +BRDA:280,8,1,4 +DA:281,4 +DA:281,4 +DA:281,4 +DA:285,4 +DA:285,4 +DA:286,4 +DA:286,4 +DA:286,4 +DA:286,4 +DA:286,4 +DA:287,2 +DA:287,2 +DA:287,2 +DA:287,2 +DA:290,8 +DA:290,8 +BRDA:289,9,0,2 +BRDA:289,9,1,2 +DA:293,2 +DA:293,2 +DA:293,2 +DA:301,2 +DA:301,2 +DA:302,2 +DA:302,2 +DA:302,2 +DA:302,2 +DA:302,2 +DA:303,2 +DA:303,2 +DA:303,2 +DA:303,2 +DA:307,6 +DA:307,6 +BRDA:306,10,0,3 +BRDA:306,10,1,5 +DA:309,5 +DA:309,5 +DA:309,5 +DA:313,5 +DA:313,5 +DA:314,5 +DA:314,5 +DA:314,5 +DA:314,5 +DA:314,5 +DA:315,3 +DA:315,3 +DA:315,3 +DA:315,3 +DA:318,1 +DA:318,1 +BRDA:317,11,0,1 +BRDA:317,11,1,1 +DA:321,1 +DA:321,1 +DA:321,1 +DA:325,1 +DA:325,1 +DA:326,1 +DA:326,1 +DA:326,1 +DA:326,1 +DA:326,1 +DA:327,1 +DA:327,1 +DA:327,1 +DA:327,1 +DA:331,0 +DA:331,0 +FN:339,CalldataVerificationFacet._extractBridgeData +FNDA:23,CalldataVerificationFacet._extractBridgeData +DA:343,23 +DA:343,23 +DA:343,23 +BRDA:342,12,0,11 +BRDA:342,12,1,12 +DA:346,11 +DA:346,11 +DA:346,11 +DA:347,11 +DA:347,11 +DA:351,11 +DA:351,11 +DA:354,12 +DA:354,12 +FN:360,CalldataVerificationFacet._extractSwapData FNDA:8,CalldataVerificationFacet._extractSwapData -DA:334,8 -DA:334,8 -DA:334,8 -BRDA:333,11,0,4 -BRDA:333,11,1,4 -DA:337,4 -DA:337,4 -DA:337,4 -DA:338,4 -DA:338,4 -DA:342,4 -DA:342,4 -DA:345,4 -DA:345,4 -FNF:9 -FNH:9 -LF:73 -LH:72 -BRF:24 -BRH:23 +DA:364,8 +DA:364,8 +DA:364,8 +BRDA:363,13,0,4 +BRDA:363,13,1,4 +DA:367,4 +DA:367,4 +DA:367,4 +DA:368,4 +DA:368,4 +DA:372,4 +DA:372,4 +DA:375,4 +DA:375,4 +FNF:10 +FNH:10 +LF:80 +LH:79 +BRF:28 +BRH:27 end_of_record TN: SF:src/Facets/CelerCircleBridgeFacet.sol @@ -1439,11 +1460,11 @@ end_of_record TN: SF:src/Facets/DiamondCutFacet.sol FN:18,DiamondCutFacet.diamondCut -FNDA:1488,DiamondCutFacet.diamondCut -DA:23,1488 -DA:23,1488 -DA:24,1488 -DA:24,1488 +FNDA:1508,DiamondCutFacet.diamondCut +DA:23,1508 +DA:23,1508 +DA:24,1508 +DA:24,1508 FNF:1 FNH:1 LF:2 @@ -1723,16 +1744,8 @@ DA:353,1 DA:353,1 DA:356,10 DA:356,10 -FN:359,GenericSwapFacetV3._executeSwaps +FN:363,GenericSwapFacetV3._executeSwaps FNDA:20,GenericSwapFacetV3._executeSwaps -DA:365,20 -DA:365,20 -DA:366,20 -DA:366,20 -DA:367,20 -DA:367,20 -DA:368,20 -DA:368,20 DA:369,20 DA:369,20 DA:370,20 @@ -1741,156 +1754,164 @@ DA:371,20 DA:371,20 DA:372,20 DA:372,20 +DA:373,20 +DA:373,20 +DA:374,20 +DA:374,20 DA:375,20 DA:375,20 -DA:375,52 -DA:376,37 -DA:376,37 -DA:377,37 -DA:377,37 -DA:378,37 -DA:378,37 -DA:379,37 -DA:379,37 +DA:376,20 +DA:376,20 +DA:379,20 +DA:379,20 +DA:379,52 +DA:380,37 +DA:380,37 +DA:381,37 +DA:381,37 +DA:382,37 +DA:382,37 DA:383,37 DA:383,37 -DA:383,37 -DA:384,35 -DA:384,35 -BRDA:382,12,0,2 -BRDA:382,12,1,35 -DA:388,2 -DA:388,2 -DA:393,35 -DA:393,35 -DA:393,35 -DA:394,1 -DA:394,1 -BRDA:392,13,0,1 -BRDA:392,13,1,34 -DA:396,1 -DA:396,1 -DA:399,34 -DA:399,34 -BRDA:399,14,0,4 -BRDA:399,14,1,6 -DA:402,7 -DA:402,7 -DA:405,7 -DA:405,7 -BRDA:405,15,0,1 -BRDA:405,15,1,6 -DA:406,1 -DA:406,1 -DA:411,6 -DA:411,6 -BRDA:411,16,0,4 -BRDA:411,16,1,6 -DA:412,4 -DA:412,4 -DA:416,27 -DA:416,27 +DA:387,37 +DA:387,37 +DA:387,37 +DA:388,35 +DA:388,35 +BRDA:386,12,0,2 +BRDA:386,12,1,35 +DA:392,2 +DA:392,2 +DA:397,35 +DA:397,35 +DA:397,35 +DA:398,1 +DA:398,1 +BRDA:396,13,0,1 +BRDA:396,13,1,34 +DA:400,1 +DA:400,1 +DA:403,34 +DA:403,34 +BRDA:403,14,0,4 +BRDA:403,14,1,6 +DA:406,7 +DA:406,7 +DA:409,7 +DA:409,7 +BRDA:409,15,0,1 +BRDA:409,15,1,6 +DA:410,1 +DA:410,1 +DA:415,6 +DA:415,6 +BRDA:415,16,0,4 +BRDA:415,16,1,6 +DA:416,4 +DA:416,4 DA:420,27 DA:420,27 -BRDA:420,17,0,25 -BRDA:420,17,1,27 -DA:421,25 -DA:421,25 -DA:422,25 -DA:422,25 -DA:429,27 -DA:429,27 -DA:432,27 -DA:432,27 -BRDA:432,18,0,1 -BRDA:432,18,1,26 -DA:433,1 -DA:433,1 -DA:438,26 -DA:438,26 -BRDA:438,19,0,19 -BRDA:438,19,1,26 -DA:439,19 -DA:439,19 -DA:443,32 -DA:443,32 -DA:456,32 -DA:456,32 -FN:461,GenericSwapFacetV3._transferERC20TokensAndEmitEvent +DA:424,27 +DA:424,27 +BRDA:424,17,0,25 +BRDA:424,17,1,27 +DA:425,25 +DA:425,25 +DA:426,25 +DA:426,25 +DA:433,27 +DA:433,27 +DA:436,27 +DA:436,27 +BRDA:436,18,0,1 +BRDA:436,18,1,26 +DA:437,1 +DA:437,1 +DA:442,26 +DA:442,26 +BRDA:442,19,0,19 +BRDA:442,19,1,26 +DA:443,19 +DA:443,19 +DA:450,32 +DA:450,32 +DA:463,32 +DA:463,32 +FN:468,GenericSwapFacetV3._transferERC20TokensAndEmitEvent FNDA:10,GenericSwapFacetV3._transferERC20TokensAndEmitEvent -DA:470,10 -DA:470,10 -DA:472,10 -DA:472,10 -DA:472,10 -DA:475,10 -DA:475,10 -BRDA:475,20,0,1 -BRDA:475,20,1,9 -DA:476,1 -DA:476,1 -DA:479,9 -DA:479,9 -DA:482,9 -DA:482,9 -FN:494,GenericSwapFacetV3._transferNativeTokensAndEmitEvent +DA:477,10 +DA:477,10 +DA:479,10 +DA:479,10 +DA:479,10 +DA:482,10 +DA:482,10 +BRDA:482,20,0,1 +BRDA:482,20,1,9 +DA:483,1 +DA:483,1 +DA:486,9 +DA:486,9 +DA:489,9 +DA:489,9 +FN:501,GenericSwapFacetV3._transferNativeTokensAndEmitEvent FNDA:5,GenericSwapFacetV3._transferNativeTokensAndEmitEvent -DA:502,5 -DA:502,5 -DA:503,5 -DA:503,5 -DA:506,5 -DA:506,5 -BRDA:506,21,0,1 -BRDA:506,21,1,4 -DA:507,1 -DA:507,1 -DA:511,4 -DA:511,4 -DA:511,4 -DA:512,4 -DA:512,4 -BRDA:512,22,0,1 -BRDA:512,22,1,3 -DA:513,1 -DA:513,1 +DA:509,5 +DA:509,5 +DA:510,5 +DA:510,5 +DA:513,5 +DA:513,5 +BRDA:513,21,0,1 +BRDA:513,21,1,4 DA:514,1 DA:514,1 -DA:518,3 -DA:518,3 -FN:531,GenericSwapFacetV3._returnPositiveSlippageERC20 +DA:518,4 +DA:518,4 +DA:518,4 +DA:519,4 +DA:519,4 +BRDA:519,22,0,1 +BRDA:519,22,1,3 +DA:520,1 +DA:520,1 +DA:521,1 +DA:521,1 +DA:525,3 +DA:525,3 +FN:538,GenericSwapFacetV3._returnPositiveSlippageERC20 FNDA:29,GenericSwapFacetV3._returnPositiveSlippageERC20 -DA:536,29 -DA:536,29 -DA:536,29 -DA:536,29 -BRDA:536,23,0,5 -BRDA:536,23,1,29 -DA:537,29 -DA:537,29 -DA:537,29 -DA:541,29 -DA:541,29 -BRDA:541,24,0,5 -BRDA:541,24,1,29 -DA:542,5 -DA:542,5 -FN:548,GenericSwapFacetV3._returnPositiveSlippageNative +DA:543,29 +DA:543,29 +DA:543,29 +DA:543,29 +BRDA:543,23,0,5 +BRDA:543,23,1,29 +DA:544,29 +DA:544,29 +DA:544,29 +DA:548,29 +DA:548,29 +BRDA:548,24,0,5 +BRDA:548,24,1,29 +DA:549,5 +DA:549,5 +FN:555,GenericSwapFacetV3._returnPositiveSlippageNative FNDA:9,GenericSwapFacetV3._returnPositiveSlippageNative -DA:550,9 -DA:550,9 -DA:552,9 -DA:552,9 -BRDA:552,25,0,1 -BRDA:552,25,1,1 -DA:554,2 -DA:554,2 -DA:554,2 -DA:555,2 -DA:555,2 -BRDA:555,26,0,1 -BRDA:555,26,1,1 -DA:555,1 +DA:557,9 +DA:557,9 +DA:559,9 +DA:559,9 +BRDA:559,25,0,1 +BRDA:559,25,1,1 +DA:561,2 +DA:561,2 +DA:561,2 +DA:562,2 +DA:562,2 +BRDA:562,26,0,1 +BRDA:562,26,1,1 +DA:562,1 FNF:13 FNH:13 LF:127 @@ -2577,118 +2598,85 @@ BRH:0 end_of_record TN: SF:src/Facets/MayanFacet.sol -FN:86,MayanFacet. +FN:53,MayanFacet. FNDA:0,MayanFacet. -DA:87,0 -DA:87,0 -FN:94,MayanFacet.initMayan -FNDA:0,MayanFacet.initMayan -DA:95,0 -DA:95,0 -DA:97,0 -DA:97,0 -DA:97,0 -DA:99,0 -DA:99,0 -DA:100,0 -DA:100,0 -DA:100,0 -DA:100,0 -DA:101,0 -DA:101,0 -DA:105,0 -DA:105,0 -FN:113,MayanFacet.setMayanChainIdMapping -FNDA:16,MayanFacet.setMayanChainIdMapping -DA:117,16 -DA:117,16 -DA:118,16 -DA:118,16 -DA:118,16 -DA:119,16 -DA:119,16 -DA:120,16 -DA:120,16 -FN:126,MayanFacet.startBridgeTokensViaMayan -FNDA:265,MayanFacet.startBridgeTokensViaMayan -DA:138,260 -DA:138,260 -DA:138,260 -DA:138,260 -DA:142,260 -DA:142,260 -DA:146,259 -DA:146,259 -FN:153,MayanFacet.swapAndStartBridgeTokensViaMayan +DA:54,0 +DA:54,0 +FN:62,MayanFacet.startBridgeTokensViaMayan +FNDA:266,MayanFacet.startBridgeTokensViaMayan +DA:74,261 +DA:74,261 +DA:78,260 +DA:78,260 +FN:85,MayanFacet.swapAndStartBridgeTokensViaMayan FNDA:7,MayanFacet.swapAndStartBridgeTokensViaMayan -DA:166,4 -DA:166,4 -DA:166,4 -DA:166,4 -DA:169,4 -DA:169,4 -DA:170,4 -DA:170,4 -DA:177,3 -DA:177,3 -FN:185,MayanFacet._startBridge -FNDA:262,MayanFacet._startBridge -DA:190,262 -DA:190,262 -DA:190,262 -DA:194,262 -DA:194,262 -DA:194,262 -DA:200,262 -DA:200,262 -DA:200,262 -DA:210,262 -DA:210,262 -DA:210,262 -DA:219,262 -DA:219,262 -BRDA:219,0,0,- -BRDA:219,0,1,260 -DA:220,260 -DA:220,260 -DA:226,260 -DA:226,260 -DA:236,2 -DA:236,2 -DA:245,262 -DA:245,262 -BRDA:245,1,0,- -BRDA:245,1,1,262 -DA:246,0 -DA:246,0 -DA:253,262 -DA:253,262 -FN:259,MayanFacet.getWormholeChainId -FNDA:262,MayanFacet.getWormholeChainId -DA:262,262 -DA:262,262 -DA:262,262 -DA:263,262 -DA:263,262 -DA:264,262 -DA:264,262 -BRDA:264,2,0,- -BRDA:264,2,1,262 -DA:264,0 -DA:265,262 -DA:265,262 -FN:269,MayanFacet.getStorage -FNDA:278,MayanFacet.getStorage -DA:270,278 -DA:270,278 -DA:273,278 -DA:273,278 -FNF:8 -FNH:6 -LF:35 -LH:27 -BRF:6 -BRH:3 +DA:98,4 +DA:98,4 +DA:104,3 +DA:104,3 +FN:112,MayanFacet._startBridge +FNDA:263,MayanFacet._startBridge +DA:117,263 +DA:117,263 +BRDA:117,0,0,1 +BRDA:117,0,1,- +DA:118,1 +DA:118,1 +DA:118,1 +BRDA:118,1,0,- +BRDA:118,1,1,1 +DA:119,0 +DA:119,0 +DA:124,1 +DA:124,1 +DA:124,1 +DA:125,1 +DA:125,1 +BRDA:125,2,0,1 +BRDA:125,2,1,- +DA:126,1 +DA:126,1 +DA:132,262 +DA:132,262 +DA:132,262 +DA:135,262 +DA:135,262 +BRDA:135,3,0,- +BRDA:135,3,1,262 +DA:136,0 +DA:136,0 +DA:140,262 +DA:140,262 +DA:142,262 +DA:142,262 +BRDA:142,4,0,- +BRDA:142,4,1,260 +DA:143,260 +DA:143,260 +DA:149,260 +DA:149,260 +DA:157,2 +DA:157,2 +DA:163,262 +DA:163,262 +BRDA:163,5,0,- +BRDA:163,5,1,262 +DA:164,0 +DA:164,0 +DA:171,262 +DA:171,262 +FN:177,MayanFacet._parseReceiver +FNDA:263,MayanFacet._parseReceiver +DA:180,263 +DA:180,263 +DA:183,263 +DA:183,263 +FNF:5 +FNH:4 +LF:24 +LH:20 +BRF:12 +BRH:6 end_of_record TN: SF:src/Facets/MultichainFacet.sol @@ -3265,22 +3253,38 @@ TN: SF:src/Facets/StandardizedCallFacet.sol FN:15,StandardizedCallFacet.standardizedCall FNDA:2,StandardizedCallFacet.standardizedCall -DA:18,2 -DA:18,2 -DA:18,2 -DA:19,2 -DA:19,2 -DA:23,2 -DA:23,2 -DA:23,2 -BRDA:23,0,0,1 -BRDA:23,0,1,1 -DA:24,1 -DA:24,1 -FNF:1 -FNH:1 -LF:4 -LH:4 +DA:16,2 +DA:16,2 +FN:21,StandardizedCallFacet.standardizedSwapCall +FNDA:2,StandardizedCallFacet.standardizedSwapCall +DA:22,2 +DA:22,2 +FN:27,StandardizedCallFacet.standardizedBridgeCall +FNDA:2,StandardizedCallFacet.standardizedBridgeCall +DA:28,2 +DA:28,2 +FN:33,StandardizedCallFacet.standardizedSwapAndBridgeCall +FNDA:2,StandardizedCallFacet.standardizedSwapAndBridgeCall +DA:36,2 +DA:36,2 +FN:39,StandardizedCallFacet.execute +FNDA:8,StandardizedCallFacet.execute +DA:42,8 +DA:42,8 +DA:42,8 +DA:43,8 +DA:43,8 +DA:47,8 +DA:47,8 +DA:47,8 +BRDA:47,0,0,4 +BRDA:47,0,1,4 +DA:48,4 +DA:48,4 +FNF:5 +FNH:5 +LF:8 +LH:8 BRF:2 BRH:2 end_of_record @@ -3321,22 +3325,22 @@ DA:143,3 DA:143,3 DA:144,3 DA:144,3 -DA:154,2 -DA:154,2 -FN:157,StargateFacet.quoteLayerZeroFee +DA:152,2 +DA:152,2 +FN:155,StargateFacet.quoteLayerZeroFee FNDA:46,StargateFacet.quoteLayerZeroFee -DA:161,46 -DA:161,46 -DA:162,46 -DA:162,46 -FN:180,StargateFacet._startBridge +DA:159,46 +DA:159,46 +DA:160,46 +DA:160,46 +FN:178,StargateFacet._startBridge FNDA:265,StargateFacet._startBridge -DA:184,265 -DA:184,265 -BRDA:184,0,0,- -BRDA:184,0,1,3 -DA:185,3 -DA:185,3 +DA:182,265 +DA:182,265 +BRDA:182,0,0,- +BRDA:182,0,1,3 +DA:183,3 +DA:183,3 DA:201,262 DA:201,262 DA:207,262 @@ -3774,25 +3778,25 @@ end_of_record TN: SF:src/Helpers/ReentrancyGuard.sol FN:29,ReentrancyGuard.nonReentrant -FNDA:3572,ReentrancyGuard.nonReentrant -DA:30,3572 -DA:30,3572 -DA:30,3572 -DA:31,3572 -DA:31,3572 +FNDA:3314,ReentrancyGuard.nonReentrant +DA:30,3314 +DA:30,3314 +DA:30,3314 +DA:31,3314 +DA:31,3314 BRDA:31,0,0,2 -BRDA:31,0,1,2784 +BRDA:31,0,1,3043 DA:31,2 -DA:32,3570 -DA:32,3570 -DA:34,3429 -DA:34,3429 +DA:32,3312 +DA:32,3312 +DA:34,3173 +DA:34,3173 FN:40,ReentrancyGuard.reentrancyStorage -FNDA:6341,ReentrancyGuard.reentrancyStorage -DA:45,6341 -DA:45,6341 -DA:48,6341 -DA:48,6341 +FNDA:6342,ReentrancyGuard.reentrancyStorage +DA:45,6342 +DA:45,6342 +DA:48,6342 +DA:48,6342 FNF:2 FNH:2 LF:6 @@ -3803,11 +3807,11 @@ end_of_record TN: SF:src/Helpers/SwapperV2.sol FN:30,SwapperV2.noLeftovers -FNDA:63,SwapperV2.noLeftovers -DA:35,63 -DA:35,63 -DA:36,63 -DA:36,63 +FNDA:66,SwapperV2.noLeftovers +DA:35,66 +DA:35,66 +DA:36,66 +DA:36,66 BRDA:36,0,0,4 BRDA:36,0,1,15 DA:37,18 @@ -3835,11 +3839,11 @@ DA:50,4 DA:58,18 DA:58,18 FN:71,SwapperV2.noLeftoversReserve -FNDA:26,SwapperV2.noLeftoversReserve -DA:77,26 -DA:77,26 -DA:78,26 -DA:78,26 +FNDA:23,SwapperV2.noLeftoversReserve +DA:77,23 +DA:77,23 +DA:78,23 +DA:78,23 BRDA:78,3,0,- BRDA:78,3,1,- DA:79,0 @@ -3870,109 +3874,109 @@ DA:95,0 DA:103,0 DA:103,0 FN:114,SwapperV2.refundExcessNative -FNDA:3042,SwapperV2.refundExcessNative -DA:115,3042 -DA:115,3042 -DA:115,3042 -DA:117,2910 -DA:117,2910 -DA:119,2910 -DA:119,2910 -BRDA:119,6,0,4 -BRDA:119,6,1,2129 -DA:120,6 -DA:120,6 +FNDA:2784,SwapperV2.refundExcessNative +DA:115,2784 +DA:115,2784 +DA:115,2784 +DA:117,2654 +DA:117,2654 +DA:119,2654 +DA:119,2654 +BRDA:119,6,0,5 +BRDA:119,6,1,2385 +DA:120,5 +DA:120,5 FN:136,SwapperV2._depositAndSwap -FNDA:81,SwapperV2._depositAndSwap -DA:142,81 -DA:142,81 -DA:144,81 -DA:144,81 -BRDA:144,7,0,18 -BRDA:144,7,1,63 -DA:145,18 -DA:145,18 -DA:148,63 -DA:148,63 -DA:149,63 -DA:149,63 -DA:149,63 -DA:151,63 -DA:151,63 -BRDA:151,8,0,18 -BRDA:151,8,1,63 -DA:152,18 -DA:152,18 -DA:155,63 -DA:155,63 -DA:155,63 -DA:157,63 -DA:157,63 -DA:158,63 -DA:158,63 -DA:165,63 -DA:165,63 -DA:165,63 -DA:165,63 -DA:168,63 -DA:168,63 +FNDA:85,SwapperV2._depositAndSwap +DA:142,85 +DA:142,85 +DA:144,85 +DA:144,85 +BRDA:144,7,0,19 +BRDA:144,7,1,66 +DA:145,19 +DA:145,19 +DA:148,66 +DA:148,66 +DA:149,66 +DA:149,66 +DA:149,66 +DA:151,66 +DA:151,66 +BRDA:151,8,0,19 +BRDA:151,8,1,66 +DA:152,19 +DA:152,19 +DA:155,66 +DA:155,66 +DA:155,66 +DA:157,66 +DA:157,66 +DA:158,66 +DA:158,66 +DA:165,66 +DA:165,66 +DA:165,66 +DA:165,66 +DA:168,66 +DA:168,66 BRDA:168,9,0,- -BRDA:168,9,1,63 +BRDA:168,9,1,66 DA:169,0 DA:169,0 -DA:172,63 -DA:172,63 +DA:172,66 +DA:172,66 FN:181,SwapperV2._depositAndSwap -FNDA:36,SwapperV2._depositAndSwap -DA:188,36 -DA:188,36 -DA:190,36 -DA:190,36 -BRDA:190,10,0,10 -BRDA:190,10,1,26 -DA:191,10 -DA:191,10 -DA:194,26 -DA:194,26 -DA:195,26 -DA:195,26 -DA:195,26 -DA:197,26 -DA:197,26 -BRDA:197,11,0,10 -BRDA:197,11,1,26 -DA:198,10 -DA:198,10 -DA:201,26 -DA:201,26 -DA:201,26 -DA:203,26 -DA:203,26 -DA:204,26 -DA:204,26 -DA:204,26 -DA:209,26 -DA:209,26 -DA:211,26 -DA:211,26 -DA:211,26 -DA:211,26 -DA:214,26 -DA:214,26 -BRDA:214,12,0,10 -BRDA:214,12,1,26 -DA:215,10 -DA:215,10 -DA:218,26 -DA:218,26 +FNDA:32,SwapperV2._depositAndSwap +DA:188,32 +DA:188,32 +DA:190,32 +DA:190,32 +BRDA:190,10,0,9 +BRDA:190,10,1,23 +DA:191,9 +DA:191,9 +DA:194,23 +DA:194,23 +DA:195,23 +DA:195,23 +DA:195,23 +DA:197,23 +DA:197,23 +BRDA:197,11,0,9 +BRDA:197,11,1,23 +DA:198,9 +DA:198,9 +DA:201,23 +DA:201,23 +DA:201,23 +DA:203,23 +DA:203,23 +DA:204,23 +DA:204,23 +DA:204,23 +DA:209,23 +DA:209,23 +DA:211,23 +DA:211,23 +DA:211,23 +DA:211,23 +DA:214,23 +DA:214,23 +BRDA:214,12,0,9 +BRDA:214,12,1,23 +DA:215,9 +DA:215,9 +DA:218,23 +DA:218,23 BRDA:218,13,0,- -BRDA:218,13,1,26 +BRDA:218,13,1,23 DA:219,0 DA:219,0 -DA:222,26 -DA:222,26 +DA:222,23 +DA:222,23 FN:232,SwapperV2._executeSwaps -FNDA:63,SwapperV2._executeSwaps +FNDA:66,SwapperV2._executeSwaps DA:238,18 DA:238,18 DA:239,18 @@ -3983,7 +3987,7 @@ DA:240,36 DA:243,36 DA:243,36 BRDA:242,14,0,- -BRDA:242,14,1,45 +BRDA:242,14,1,48 DA:249,0 DA:249,0 DA:251,36 @@ -3991,7 +3995,7 @@ DA:251,36 DA:254,36 DA:254,36 FN:262,SwapperV2._executeSwaps -FNDA:26,SwapperV2._executeSwaps +FNDA:23,SwapperV2._executeSwaps DA:275,0 DA:275,0 DA:276,0 @@ -4002,7 +4006,7 @@ DA:277,0 DA:280,0 DA:280,0 BRDA:279,15,0,- -BRDA:279,15,1,26 +BRDA:279,15,1,23 DA:286,0 DA:286,0 DA:288,0 @@ -4106,23 +4110,23 @@ end_of_record TN: SF:src/Helpers/Validatable.sol FN:11,Validatable.validateBridgeData -FNDA:3525,Validatable.validateBridgeData -DA:12,3525 -DA:12,3525 +FNDA:3267,Validatable.validateBridgeData +DA:12,3267 +DA:12,3267 BRDA:12,0,0,27 -BRDA:12,0,1,2721 +BRDA:12,0,1,2980 DA:13,26 DA:13,26 -DA:15,3499 -DA:15,3499 +DA:15,3241 +DA:15,3241 BRDA:15,1,0,27 -BRDA:15,1,1,2694 +BRDA:15,1,1,2953 DA:16,26 DA:16,26 -DA:18,3473 -DA:18,3473 +DA:18,3215 +DA:18,3215 BRDA:18,2,0,26 -BRDA:18,2,1,2668 +BRDA:18,2,1,2927 DA:19,26 DA:19,26 FN:24,Validatable.noNativeAsset @@ -4158,19 +4162,19 @@ BRDA:52,6,1,159 DA:53,0 DA:53,0 FN:58,Validatable.doesNotContainSourceSwaps -FNDA:6114,Validatable.doesNotContainSourceSwaps -DA:59,6114 +FNDA:6115,Validatable.doesNotContainSourceSwaps +DA:59,6115 BRDA:59,7,0,27 -BRDA:59,7,1,6087 +BRDA:59,7,1,6088 DA:60,27 DA:60,27 FN:65,Validatable.doesNotContainDestinationCalls -FNDA:2951,Validatable.doesNotContainDestinationCalls -DA:68,2951 -BRDA:68,8,0,9 -BRDA:68,8,1,2439 -DA:69,12 -DA:69,12 +FNDA:2697,Validatable.doesNotContainDestinationCalls +DA:68,2697 +BRDA:68,8,0,10 +BRDA:68,8,1,2693 +DA:69,11 +DA:69,11 FNF:7 FNH:7 LF:18 @@ -4197,20 +4201,20 @@ DA:20,0 DA:25,0 DA:25,0 FN:31,LiFiDiamond. -FNDA:12095,LiFiDiamond. -DA:32,12095 -DA:32,12095 -DA:33,12095 -DA:33,12095 -DA:38,12095 -DA:38,12095 -DA:42,12095 -DA:42,12095 -DA:44,12095 -DA:44,12095 -DA:44,12095 +FNDA:12110,LiFiDiamond. +DA:32,12110 +DA:32,12110 +DA:33,12110 +DA:33,12110 +DA:38,12110 +DA:38,12110 +DA:42,12110 +DA:42,12110 +DA:44,12110 +DA:44,12110 +DA:44,12110 BRDA:44,0,0,- -BRDA:44,0,1,12095 +BRDA:44,0,1,12110 DA:45,0 DA:45,0 FNF:2 @@ -4316,20 +4320,20 @@ end_of_record TN: SF:src/Libraries/LibAllowList.sol FN:22,LibAllowList.addAllowedContract -FNDA:775,LibAllowList.addAllowedContract -DA:23,775 -DA:23,775 -DA:25,771 -DA:25,771 -DA:25,771 -DA:27,771 +FNDA:776,LibAllowList.addAllowedContract +DA:23,776 +DA:23,776 +DA:25,772 +DA:25,772 +DA:25,772 +DA:27,772 BRDA:27,0,0,114 -BRDA:27,0,1,657 +BRDA:27,0,1,658 DA:27,114 -DA:29,657 -DA:29,657 -DA:30,657 -DA:30,657 +DA:29,658 +DA:29,658 +DA:30,658 +DA:30,658 FN:35,LibAllowList.contractIsAllowed FNDA:266,LibAllowList.contractIsAllowed DA:38,266 @@ -4368,9 +4372,9 @@ FNDA:4,LibAllowList.getAllowedContracts DA:67,4 DA:67,4 FN:72,LibAllowList.addAllowedSelector -FNDA:2314,LibAllowList.addAllowedSelector -DA:73,2314 -DA:73,2314 +FNDA:2317,LibAllowList.addAllowedSelector +DA:73,2317 +DA:73,2317 FN:78,LibAllowList.removeAllowedSelector FNDA:0,LibAllowList.removeAllowedSelector DA:79,0 @@ -4380,23 +4384,23 @@ FNDA:166,LibAllowList.selectorIsAllowed DA:85,166 DA:85,166 FN:89,LibAllowList._getStorage -FNDA:3527,LibAllowList._getStorage -DA:94,3527 -DA:94,3527 -DA:97,3527 -DA:97,3527 +FNDA:3531,LibAllowList._getStorage +DA:94,3531 +DA:94,3531 +DA:97,3531 +DA:97,3531 FN:103,LibAllowList._checkAddress -FNDA:775,LibAllowList._checkAddress -DA:104,775 -DA:104,775 -DA:104,775 +FNDA:776,LibAllowList._checkAddress +DA:104,776 +DA:104,776 +DA:104,776 BRDA:104,3,0,2 -BRDA:104,3,1,773 +BRDA:104,3,1,774 DA:104,2 -DA:106,773 -DA:106,773 +DA:106,774 +DA:106,774 BRDA:106,4,0,2 -BRDA:106,4,1,771 +BRDA:106,4,1,772 DA:106,2 FNF:9 FNH:8 @@ -4408,11 +4412,11 @@ end_of_record TN: SF:src/Libraries/LibAsset.sol FN:25,LibAsset.getOwnBalance -FNDA:767,LibAsset.getOwnBalance -DA:26,767 -DA:26,767 -DA:27,767 -DA:27,767 +FNDA:770,LibAsset.getOwnBalance +DA:26,770 +DA:26,770 +DA:27,770 +DA:27,770 FN:36,LibAsset.transferNativeAsset FNDA:22,LibAsset.transferNativeAsset DA:40,22 @@ -4435,28 +4439,28 @@ BRDA:45,2,0,4 BRDA:45,2,1,18 DA:45,4 FN:53,LibAsset.maxApproveERC20 -FNDA:6632,LibAsset.maxApproveERC20 -DA:58,6632 -DA:58,6632 -BRDA:58,3,0,6629 -BRDA:58,3,1,6632 -DA:59,6632 -DA:59,6632 -DA:61,6629 -DA:61,6629 +FNDA:6633,LibAsset.maxApproveERC20 +DA:58,6633 +DA:58,6633 +BRDA:58,3,0,6630 +BRDA:58,3,1,6633 +DA:59,6633 +DA:59,6633 +DA:61,6630 +DA:61,6630 BRDA:61,4,0,- -BRDA:61,4,1,6629 +BRDA:61,4,1,6630 DA:62,0 DA:62,0 -DA:65,6629 -DA:65,6629 -DA:65,6629 -BRDA:65,5,0,6624 -BRDA:65,5,1,6629 -DA:66,6624 -DA:66,6624 -DA:67,6624 -DA:67,6624 +DA:65,6630 +DA:65,6630 +DA:65,6630 +BRDA:65,5,0,6625 +BRDA:65,5,1,6630 +DA:66,6625 +DA:66,6625 +DA:67,6625 +DA:67,6625 FN:76,LibAsset.transferERC20 FNDA:46,LibAsset.transferERC20 DA:81,46 @@ -4483,44 +4487,44 @@ DA:90,0 DA:92,46 DA:92,46 FN:100,LibAsset.transferFromERC20 -FNDA:6576,LibAsset.transferFromERC20 -DA:106,6576 -DA:106,6576 +FNDA:6577,LibAsset.transferFromERC20 +DA:106,6577 +DA:106,6577 BRDA:106,9,0,- -BRDA:106,9,1,6576 +BRDA:106,9,1,6577 DA:107,0 DA:107,0 -DA:109,6576 -DA:109,6576 +DA:109,6577 +DA:109,6577 BRDA:109,10,0,- -BRDA:109,10,1,6576 +BRDA:109,10,1,6577 DA:110,0 DA:110,0 -DA:113,6576 -DA:113,6576 -DA:113,6576 -DA:114,6576 -DA:114,6576 -DA:114,6576 -DA:115,6576 -DA:115,6576 -DA:116,6573 -DA:116,6573 -DA:116,6573 -DA:116,6573 +DA:113,6577 +DA:113,6577 +DA:113,6577 +DA:114,6577 +DA:114,6577 +DA:114,6577 +DA:115,6577 +DA:115,6577 +DA:116,6574 +DA:116,6574 +DA:116,6574 +DA:116,6574 BRDA:116,11,0,- -BRDA:116,11,1,6573 +BRDA:116,11,1,6574 DA:117,0 DA:117,0 FN:121,LibAsset.depositAsset -FNDA:6115,LibAsset.depositAsset -DA:122,6115 -DA:122,6115 +FNDA:6116,LibAsset.depositAsset +DA:122,6116 +DA:122,6116 BRDA:122,12,0,- -BRDA:122,12,1,6115 +BRDA:122,12,1,6116 DA:122,0 -DA:123,6115 -DA:123,6115 +DA:123,6116 +DA:123,6116 BRDA:123,13,0,3 BRDA:123,13,1,35 DA:124,38 @@ -4528,16 +4532,16 @@ DA:124,38 BRDA:124,14,0,3 BRDA:124,14,1,35 DA:124,3 -DA:126,6077 -DA:126,6077 -DA:126,6077 -DA:127,6077 -DA:127,6077 +DA:126,6078 +DA:126,6078 +DA:126,6078 +DA:127,6078 +DA:127,6078 BRDA:127,15,0,25 -BRDA:127,15,1,6052 +BRDA:127,15,1,6053 DA:127,25 -DA:128,6052 -DA:128,6052 +DA:128,6053 +DA:128,6053 FN:132,LibAsset.depositAssets FNDA:89,LibAsset.depositAssets DA:133,89 @@ -4553,23 +4557,23 @@ DA:136,90 DA:139,107 DA:139,107 FN:147,LibAsset.isNativeAsset -FNDA:26589,LibAsset.isNativeAsset -DA:148,26589 -DA:148,26589 -DA:148,26589 +FNDA:26593,LibAsset.isNativeAsset +DA:148,26593 +DA:148,26593 +DA:148,26593 FN:158,LibAsset.transferAsset FNDA:68,LibAsset.transferAsset DA:163,68 DA:163,68 FN:169,LibAsset.isContract -FNDA:397,LibAsset.isContract -DA:170,397 -DA:170,397 -DA:173,397 -DA:173,397 -DA:175,397 -DA:175,397 -DA:175,397 +FNDA:398,LibAsset.isContract +DA:170,398 +DA:170,398 +DA:173,398 +DA:173,398 +DA:175,398 +DA:175,398 +DA:175,398 FNF:10 FNH:10 LF:47 @@ -4580,23 +4584,23 @@ end_of_record TN: SF:src/Libraries/LibBytes.sol FN:16,LibBytes.slice -FNDA:33,LibBytes.slice -DA:21,33 -DA:21,33 -DA:21,33 +FNDA:35,LibBytes.slice +DA:21,35 +DA:21,35 +DA:21,35 BRDA:21,0,0,- -BRDA:21,0,1,33 +BRDA:21,0,1,35 DA:21,0 -DA:22,33 -DA:22,33 -DA:22,33 +DA:22,35 +DA:22,35 +DA:22,35 BRDA:22,1,0,- -BRDA:22,1,1,33 +BRDA:22,1,1,35 DA:22,0 -DA:24,33 -DA:24,33 -DA:87,33 -DA:87,33 +DA:24,35 +DA:24,35 +DA:87,35 +DA:87,35 FN:90,LibBytes.toAddress FNDA:0,LibBytes.toAddress DA:94,0 @@ -4648,11 +4652,11 @@ end_of_record TN: SF:src/Libraries/LibDiamond.sol FN:53,LibDiamond.diamondStorage -FNDA:4668,LibDiamond.diamondStorage -DA:58,4668 -DA:58,4668 -DA:61,4668 -DA:61,4668 +FNDA:4712,LibDiamond.diamondStorage +DA:58,4712 +DA:58,4712 +DA:61,4712 +DA:61,4712 FN:70,LibDiamond.setContractOwner FNDA:1,LibDiamond.setContractOwner DA:71,1 @@ -4669,26 +4673,26 @@ FNDA:24,LibDiamond.contractOwner DA:78,24 DA:78,24 FN:81,LibDiamond.enforceIsContractOwner -FNDA:1725,LibDiamond.enforceIsContractOwner -DA:82,1725 -DA:82,1725 +FNDA:1729,LibDiamond.enforceIsContractOwner +DA:82,1729 +DA:82,1729 BRDA:82,0,0,8 -BRDA:82,0,1,1717 +BRDA:82,0,1,1721 DA:83,8 DA:83,8 FN:93,LibDiamond.diamondCut -FNDA:1488,LibDiamond.diamondCut -DA:98,1488 -DA:98,1488 -DA:98,4404 -DA:99,2916 -DA:99,2916 -DA:100,2916 -DA:100,2916 +FNDA:1508,LibDiamond.diamondCut +DA:98,1508 +DA:98,1508 +DA:98,4458 +DA:99,2950 +DA:99,2950 +DA:100,2950 +DA:100,2950 BRDA:100,1,0,- -BRDA:100,1,1,2916 -DA:101,2916 -DA:101,2916 +BRDA:100,1,1,2950 +DA:101,2950 +DA:101,2950 DA:105,0 DA:105,0 BRDA:105,2,0,- @@ -4703,58 +4707,58 @@ DA:111,0 DA:111,0 DA:116,0 DA:116,0 -DA:119,2916 -DA:119,2916 -DA:122,1488 -DA:122,1488 -DA:123,1488 -DA:123,1488 +DA:119,2950 +DA:119,2950 +DA:122,1508 +DA:122,1508 +DA:123,1508 +DA:123,1508 FN:126,LibDiamond.addFunctions -FNDA:2916,LibDiamond.addFunctions -DA:130,2916 -DA:130,2916 +FNDA:2950,LibDiamond.addFunctions +DA:130,2950 +DA:130,2950 BRDA:130,4,0,- -BRDA:130,4,1,2916 +BRDA:130,4,1,2950 DA:131,0 DA:131,0 -DA:133,2916 -DA:133,2916 -DA:133,2916 -DA:134,2916 -DA:134,2916 +DA:133,2950 +DA:133,2950 +DA:133,2950 +DA:134,2950 +DA:134,2950 BRDA:134,5,0,- -BRDA:134,5,1,2916 +BRDA:134,5,1,2950 DA:135,0 DA:135,0 -DA:137,2916 -DA:137,2916 -DA:137,2916 -DA:141,2916 -DA:141,2916 -BRDA:141,6,0,2916 -BRDA:141,6,1,2916 -DA:142,2916 -DA:142,2916 -DA:145,2916 -DA:145,2916 -DA:146,14680 -DA:146,14680 -DA:149,11764 -DA:149,11764 -DA:150,11764 -DA:150,11764 -DA:153,11764 -DA:153,11764 +DA:137,2950 +DA:137,2950 +DA:137,2950 +DA:141,2950 +DA:141,2950 +BRDA:141,6,0,2950 +BRDA:141,6,1,2950 +DA:142,2950 +DA:142,2950 +DA:145,2950 +DA:145,2950 +DA:146,14815 +DA:146,14815 +DA:149,11865 +DA:149,11865 +DA:150,11865 +DA:150,11865 +DA:153,11865 +DA:153,11865 BRDA:153,7,0,- -BRDA:153,7,1,11764 +BRDA:153,7,1,11865 DA:154,0 DA:154,0 -DA:156,11764 -DA:156,11764 -DA:158,11764 -DA:158,11764 -DA:159,11764 -DA:159,11764 +DA:156,11865 +DA:156,11865 +DA:158,11865 +DA:158,11865 +DA:159,11865 +DA:159,11865 FN:164,LibDiamond.replaceFunctions FNDA:0,LibDiamond.replaceFunctions DA:168,0 @@ -4833,21 +4837,21 @@ DA:224,0 DA:226,0 DA:226,0 FN:231,LibDiamond.addFacet -FNDA:2916,LibDiamond.addFacet -DA:235,2916 -DA:235,2916 -DA:236,2916 -DA:236,2916 -DA:239,2916 -DA:239,2916 +FNDA:2950,LibDiamond.addFacet +DA:235,2950 +DA:235,2950 +DA:236,2950 +DA:236,2950 +DA:239,2950 +DA:239,2950 FN:242,LibDiamond.addFunction -FNDA:11764,LibDiamond.addFunction -DA:248,11764 -DA:248,11764 -DA:251,11764 -DA:251,11764 -DA:254,11764 -DA:254,11764 +FNDA:11865,LibDiamond.addFunction +DA:248,11865 +DA:248,11865 +DA:251,11865 +DA:251,11865 +DA:254,11865 +DA:254,11865 FN:257,LibDiamond.removeFunction FNDA:0,LibDiamond.removeFunction DA:262,0 @@ -4906,35 +4910,35 @@ DA:309,0 DA:310,0 DA:310,0 FN:316,LibDiamond.initializeDiamondCut -FNDA:1488,LibDiamond.initializeDiamondCut -DA:320,1488 -DA:320,1488 +FNDA:1508,LibDiamond.initializeDiamondCut +DA:320,1508 +DA:320,1508 BRDA:320,19,0,- -BRDA:320,19,1,1486 -DA:321,1486 -DA:321,1486 +BRDA:320,19,1,1500 +DA:321,1500 +DA:321,1500 BRDA:321,20,0,- -BRDA:321,20,1,1486 +BRDA:321,20,1,1500 DA:322,0 DA:322,0 -DA:325,2 -DA:325,2 +DA:325,8 +DA:325,8 BRDA:325,21,0,- -BRDA:325,21,1,2 +BRDA:325,21,1,8 DA:326,0 DA:326,0 -DA:328,2 -DA:328,2 -DA:328,2 -BRDA:328,22,0,2 -BRDA:328,22,1,2 -DA:329,2 -DA:329,2 -DA:332,2 -DA:332,2 -DA:332,2 -DA:333,2 -DA:333,2 +DA:328,8 +DA:328,8 +DA:328,8 +BRDA:328,22,0,8 +BRDA:328,22,1,8 +DA:329,8 +DA:329,8 +DA:332,8 +DA:332,8 +DA:332,8 +DA:333,8 +DA:333,8 BRDA:333,23,0,- BRDA:333,23,1,- DA:334,0 @@ -4946,15 +4950,15 @@ DA:336,0 DA:338,0 DA:338,0 FN:344,LibDiamond.enforceHasContractCode -FNDA:2918,LibDiamond.enforceHasContractCode -DA:345,2918 -DA:345,2918 -DA:348,2918 -DA:348,2918 -DA:350,2918 -DA:350,2918 +FNDA:2958,LibDiamond.enforceHasContractCode +DA:345,2958 +DA:345,2958 +DA:348,2958 +DA:348,2958 +DA:350,2958 +DA:350,2958 BRDA:350,25,0,- -BRDA:350,25,1,2918 +BRDA:350,25,1,2958 DA:351,0 DA:351,0 FNF:13 @@ -4967,54 +4971,54 @@ end_of_record TN: SF:src/Libraries/LibSwap.sol FN:30,LibSwap.swap -FNDA:133,LibSwap.swap -DA:31,133 -DA:31,133 +FNDA:134,LibSwap.swap +DA:31,134 +DA:31,134 BRDA:31,0,0,- -BRDA:31,0,1,133 +BRDA:31,0,1,134 DA:31,0 -DA:32,133 -DA:32,133 -DA:33,133 -DA:33,133 +DA:32,134 +DA:32,134 +DA:33,134 +DA:33,134 BRDA:33,1,0,- -BRDA:33,1,1,133 +BRDA:33,1,1,134 DA:33,0 -DA:34,133 -DA:34,133 -DA:34,133 -DA:37,133 -DA:37,133 -DA:37,133 -DA:40,133 -DA:40,133 -DA:40,133 -DA:44,133 -DA:44,133 -BRDA:44,2,0,111 -BRDA:44,2,1,133 -DA:45,111 -DA:45,111 -DA:52,133 -DA:52,133 +DA:34,134 +DA:34,134 +DA:34,134 +DA:37,134 +DA:37,134 +DA:37,134 +DA:40,134 +DA:40,134 +DA:40,134 +DA:44,134 +DA:44,134 +BRDA:44,2,0,112 +BRDA:44,2,1,134 +DA:45,112 +DA:45,112 +DA:52,134 +DA:52,134 BRDA:52,3,0,2 -BRDA:52,3,1,131 +BRDA:52,3,1,132 DA:53,2 DA:53,2 -DA:60,131 -DA:60,131 -DA:60,131 -DA:63,131 -DA:63,131 +DA:60,132 +DA:60,132 +DA:60,132 +DA:63,132 +DA:63,132 BRDA:63,4,0,1 -BRDA:63,4,1,130 +BRDA:63,4,1,131 DA:64,1 DA:64,1 -DA:67,130 -DA:67,130 -DA:67,130 -DA:69,130 -DA:69,130 +DA:67,131 +DA:67,131 +DA:67,131 +DA:69,131 +DA:69,131 FNF:1 FNH:1 LF:15 @@ -5038,11 +5042,11 @@ DA:15,0 DA:15,0 DA:15,0 FN:21,LibUtil.isZeroAddress -FNDA:22470,LibUtil.isZeroAddress -DA:22,22470 -DA:22,22470 -DA:22,22470 -DA:22,22470 +FNDA:22626,LibUtil.isZeroAddress +DA:22,22626 +DA:22,22626 +DA:22,22626 +DA:22,22626 FN:25,LibUtil.revertWith FNDA:9,LibUtil.revertWith FNF:3 @@ -5452,6 +5456,62 @@ BRF:6 BRH:6 end_of_record TN: +SF:src/Periphery/GasZip.sol +FN:35,GasZip. +FNDA:4,GasZip. +DA:36,4 +DA:36,4 +FN:43,GasZip.zipERC20 +FNDA:1,GasZip.zipERC20 +DA:49,1 +DA:49,1 +DA:56,1 +DA:56,1 +DA:59,1 +DA:59,1 +DA:65,1 +DA:65,1 +DA:65,1 +DA:70,1 +DA:70,1 +BRDA:70,0,0,- +BRDA:70,0,1,1 +DA:71,0 +DA:71,0 +FN:82,GasZip.zip +FNDA:1,GasZip.zip +DA:87,1 +DA:87,1 +DA:88,1 +DA:88,1 +DA:89,1 +DA:89,1 +DA:91,1 +DA:91,1 +DA:96,1 +DA:96,1 +DA:98,1 +DA:98,1 +DA:102,1 +DA:102,1 +BRDA:102,1,0,- +BRDA:102,1,1,- +DA:104,0 +DA:104,0 +DA:104,0 +DA:105,0 +DA:105,0 +BRDA:105,2,0,- +BRDA:105,2,1,- +DA:105,0 +FNF:3 +FNH:3 +LF:16 +LH:13 +BRF:6 +BRH:1 +end_of_record +TN: SF:src/Periphery/LiFuelFeeCollector.sol FN:33,LiFuelFeeCollector. FNDA:20,LiFuelFeeCollector. diff --git a/remappings.txt b/remappings.txt index a933e8bf9..cf75a7770 100644 --- a/remappings.txt +++ b/remappings.txt @@ -8,6 +8,7 @@ hardhat-deploy/=node_modules/hardhat-deploy/ celer-network/=lib/sgn-v2-contracts/ create3-factory/=lib/create3-factory/src/ solmate/=lib/solmate/src/ +solady/=lib/solady/src/ ds-test/=lib/ds-test/src/ forge-std/=lib/forge-std/src/ diff --git a/solady b/solady new file mode 160000 index 000000000..65a32cda3 --- /dev/null +++ b/solady @@ -0,0 +1 @@ +Subproject commit 65a32cda377153622c4ad49ca79c0127e0f32a73 diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 335a6ae7b..3cba54d50 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -1,9 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { LibAsset } from "../Libraries/LibAsset.sol"; import { LibSwap } from "../Libraries/LibSwap.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; +import { ERC20 } from "solady/tokens/ERC20.sol"; + +//TODO: REMOVE <<<<<<<<<<<<<<<<<<<<< +import { console2 } from "forge-std/console2.sol"; interface IGasZip { function deposit( @@ -14,12 +17,13 @@ interface IGasZip { /// @title GasZip /// @author LI.FI (https://li.fi) -/// @notice Provides functionality to swap and trigger gaz.zip protocol +/// @notice Provides functionality to swap and trigger gas.zip protocol /// @custom:version 1.0.0 contract GasZip { - address public immutable ZERO = address(0); + using SafeTransferLib for address; /// State /// + address public immutable ZERO = address(0); IGasZip public immutable gasZipRouter; /// Errors /// @@ -32,44 +36,71 @@ contract GasZip { gasZipRouter = IGasZip(_gasZipRouter); } + /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract + /// @param _swapData The swap data struct + /// @param _destinationChainId the id of the chain where gas should be made available + /// @param _recipient the address to receive the gas on dst chain function zipERC20( - LibSwap.SwapData calldata _swap, - uint256 destinationChain, - address recipient + LibSwap.SwapData calldata _swapData, + uint256 _destinationChainId, + address _recipient ) public { - LibSwap.swap(0, _swap); - uint256 availableNative = LibAsset.getOwnBalance(ZERO); - gasZipRouter.deposit{ value: availableNative }( - destinationChain, - recipient + // pull tokens from caller (e.g. LI.FI diamond) + _swapData.sendingAssetId.safeTransferFrom( + msg.sender, + address(this), + _swapData.fromAmount + ); + + // execute the swapData that swaps the ERC20 token into native + LibSwap.swap(0, _swapData); + + // call the gas zip router and deposit tokens + gasZipRouter.deposit{ value: address(this).balance }( + _destinationChainId, + _recipient ); - // Send back any remaining sendingAsset token to the sender - IERC20 sendingAsset = IERC20(_swap.sendingAssetId); - uint256 remainingBalance = sendingAsset.balanceOf(address(this)); + // check remaining balance of sendingAsset + uint256 remainingBalance = ERC20(_swapData.sendingAssetId).balanceOf( + address(this) + ); + // Send back any remaining sendingAsset tokens to the sender if (remainingBalance > 0) { - bool success = sendingAsset.transfer(msg.sender, remainingBalance); - if (!success) revert TransferFailed(); + _swapData.sendingAssetId.safeTransfer( + msg.sender, //TODO: why send it back to msg.sender? That would mean that unused tokens are sent to the diamond. Should this be sent to _receiver instead? + remainingBalance + ); } } + /// @notice Deposits native tokens in the GasZip router contract and returns any unused + /// @param _amountToZip The swap data struct + /// @param _destinationChainId the id of the chain where gas should be made available + /// @param _recipient the address to receive the gas on dst chain function zip( - uint256 amountToZip, - uint256 destinationChain, - address recipient + uint256 _amountToZip, + uint256 _destinationChainId, + address _recipient ) public payable { - gasZipRouter.deposit{ value: amountToZip }( - destinationChain, - recipient + // call the gas zip router and deposit tokens + gasZipRouter.deposit{ value: _amountToZip }( + _destinationChainId, + _recipient ); + + // TODO: why do we need this? Costs unnecessary gas....is it not sufficient to just run the deposit? uint256 nativeBalance = address(this).balance; + // Send back any remaining native balance to the sender + //TODO: is this required for multi-swaps? Otherwise this should also be sent to _recipient instead I believe if (nativeBalance > 0) { - (bool success, ) = msg.sender.call{ value: address(this).balance }( - "" - ); + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = msg.sender.call{ value: nativeBalance }(""); if (!success) revert TransferFailed(); } } + + receive() external payable {} } diff --git a/test/solidity/Periphery/GasZip.t.sol b/test/solidity/Periphery/GasZip.t.sol new file mode 100644 index 000000000..d1ce8e676 --- /dev/null +++ b/test/solidity/Periphery/GasZip.t.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity 0.8.17; + +import { Test, DSTest } from "forge-std/Test.sol"; +import { console } from "../utils/Console.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; +import { GasZip } from "lifi/Periphery/GasZip.sol"; +import { LibSwap } from "lifi/Libraries/LibSwap.sol"; +import { UniswapV2Router02 } from "../utils/Interfaces.sol"; +import { ERC20 } from "solady/tokens/ERC20.sol"; + +contract GasZipTest is Test { + address public constant GAS_ZIP_ROUTER_MAINNET = + 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; + address internal ADDRESS_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address internal ADDRESS_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address internal ADDRESS_UNISWAP = + 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; + + GasZip public gasZip; + uint256 public defaultDestinationChains = 96; + address public defaultRecipient = address(12345); + uint256 public defaultNativeAmount = 0.0006 ether; + uint256 public defaultUSDCAmount = 10e6; + UniswapV2Router02 public uniswap; + + event Deposit(address from, uint256 chains, uint256 amount, address to); + + function setUp() public { + fork(); + gasZip = new GasZip(GAS_ZIP_ROUTER_MAINNET); + + uniswap = UniswapV2Router02(ADDRESS_UNISWAP); + + deal(address(this), 1 ether); + } + + function fork() internal { + string memory rpcUrl = vm.envString("ETH_NODE_URI_MAINNET"); + uint256 blockNumber = 20173181; + vm.createSelectFork(rpcUrl, blockNumber); + } + + function test_contractIsSetUpCorrectly() public { + gasZip = new GasZip(GAS_ZIP_ROUTER_MAINNET); + assertEq(address(gasZip.gasZipRouter()), GAS_ZIP_ROUTER_MAINNET); + } + + function test_canDepositNative() public { + // set up expected event + vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); + emit Deposit( + address(gasZip), + defaultDestinationChains, + defaultNativeAmount, + defaultRecipient + ); + + // deposit via GasZip periphery contract + gasZip.zip{ value: defaultNativeAmount }( + defaultNativeAmount, + defaultDestinationChains, + defaultRecipient + ); + } + + function test_canSwapERC20ToNativeAndDeposit() public { + ( + LibSwap.SwapData memory swapData, + uint256 amountOutMin + ) = _getUniswapCalldataForERC20ToNativeSwap( + ADDRESS_USDC, + defaultUSDCAmount + ); + + deal(ADDRESS_USDC, address(this), defaultUSDCAmount); + + ERC20(ADDRESS_USDC).approve(address(gasZip), defaultUSDCAmount); + + // set up expected event + vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); + emit Deposit( + address(gasZip), + defaultDestinationChains, + amountOutMin, + defaultRecipient + ); + + // deposit via GasZip periphery contract + gasZip.zipERC20(swapData, defaultDestinationChains, defaultRecipient); + } + + function _getUniswapCalldataForERC20ToNativeSwap( + address sendingAssetId, + uint256 fromAmount + ) + internal + view + returns (LibSwap.SwapData memory swapData, uint256 amountOutMin) + { + // prepare swap data + address[] memory path = new address[](2); + path[0] = sendingAssetId; + path[1] = ADDRESS_WETH; + + // Calculate USDC input amount + uint256[] memory amounts = uniswap.getAmountsOut(fromAmount, path); + amountOutMin = amounts[1]; + + swapData = LibSwap.SwapData( + address(uniswap), + address(uniswap), + sendingAssetId, + ADDRESS_WETH, + fromAmount, + abi.encodeWithSelector( + uniswap.swapExactTokensForETH.selector, + fromAmount, + amountOutMin, + path, + address(gasZip), + block.timestamp + 20 seconds + ), + true + ); + } +} From 1b98a5f6c462c56856ec434b9f94709bad0642b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 14:20:03 +0700 Subject: [PATCH 017/100] adds refundAddress parameter for ERC20 --- lib/solady | 1 - solady | 1 - src/Periphery/GasZip.sol | 11 +++++------ 3 files changed, 5 insertions(+), 8 deletions(-) delete mode 160000 lib/solady delete mode 160000 solady diff --git a/lib/solady b/lib/solady deleted file mode 160000 index 678c91635..000000000 --- a/lib/solady +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 678c9163550810b08f0ffb09624c9f7532392303 diff --git a/solady b/solady deleted file mode 160000 index 65a32cda3..000000000 --- a/solady +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 65a32cda377153622c4ad49ca79c0127e0f32a73 diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 3cba54d50..34598ba5a 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -40,10 +40,12 @@ contract GasZip { /// @param _swapData The swap data struct /// @param _destinationChainId the id of the chain where gas should be made available /// @param _recipient the address to receive the gas on dst chain + /// @param _refundAddress the address to receive the gas on dst chain function zipERC20( LibSwap.SwapData calldata _swapData, uint256 _destinationChainId, - address _recipient + address _recipient, + address _refundAddress ) public { // pull tokens from caller (e.g. LI.FI diamond) _swapData.sendingAssetId.safeTransferFrom( @@ -69,7 +71,7 @@ contract GasZip { // Send back any remaining sendingAsset tokens to the sender if (remainingBalance > 0) { _swapData.sendingAssetId.safeTransfer( - msg.sender, //TODO: why send it back to msg.sender? That would mean that unused tokens are sent to the diamond. Should this be sent to _receiver instead? + _refundAddress, remainingBalance ); } @@ -90,11 +92,8 @@ contract GasZip { _recipient ); - // TODO: why do we need this? Costs unnecessary gas....is it not sufficient to just run the deposit? + // Send back any remaining native balance to the msg.sender (i.e. LI.FI diamond) uint256 nativeBalance = address(this).balance; - - // Send back any remaining native balance to the sender - //TODO: is this required for multi-swaps? Otherwise this should also be sent to _recipient instead I believe if (nativeBalance > 0) { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = msg.sender.call{ value: nativeBalance }(""); From 5a9b78b2261dd6206ddd38a518ab3be307e3d4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 14:20:15 +0700 Subject: [PATCH 018/100] forge install: solady v0.0.208 --- .gitmodules | 2 +- lib/solady | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 lib/solady diff --git a/.gitmodules b/.gitmodules index a41d0934f..9b49e2820 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,5 +21,5 @@ url = https://github.com/openzeppelin/openzeppelin-contracts [submodule "lib/solady"] path = lib/solady - url = https://github.com/lib/solady + url = https://github.com/Vectorized/solady diff --git a/lib/solady b/lib/solady new file mode 160000 index 000000000..678c91635 --- /dev/null +++ b/lib/solady @@ -0,0 +1 @@ +Subproject commit 678c9163550810b08f0ffb09624c9f7532392303 From 91aa0ec663907e460b57237b814ca02adad9901e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 14:20:16 +0700 Subject: [PATCH 019/100] forge install: solady v0.0.208 --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 9b49e2820..a41d0934f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,5 +21,5 @@ url = https://github.com/openzeppelin/openzeppelin-contracts [submodule "lib/solady"] path = lib/solady - url = https://github.com/Vectorized/solady + url = https://github.com/lib/solady From 4dd4db134fa82a06725f30425b250851937e55a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 14:24:47 +0700 Subject: [PATCH 020/100] tests updated --- test/solidity/Periphery/GasZip.t.sol | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/solidity/Periphery/GasZip.t.sol b/test/solidity/Periphery/GasZip.t.sol index d1ce8e676..762522761 100644 --- a/test/solidity/Periphery/GasZip.t.sol +++ b/test/solidity/Periphery/GasZip.t.sol @@ -21,6 +21,7 @@ contract GasZipTest is Test { GasZip public gasZip; uint256 public defaultDestinationChains = 96; address public defaultRecipient = address(12345); + address public defaultRefundAddress = address(56789); uint256 public defaultNativeAmount = 0.0006 ether; uint256 public defaultUSDCAmount = 10e6; UniswapV2Router02 public uniswap; @@ -88,7 +89,12 @@ contract GasZipTest is Test { ); // deposit via GasZip periphery contract - gasZip.zipERC20(swapData, defaultDestinationChains, defaultRecipient); + gasZip.zipERC20( + swapData, + defaultDestinationChains, + defaultRecipient, + defaultRefundAddress + ); } function _getUniswapCalldataForERC20ToNativeSwap( From 8e743bdd515dd1e8d08c30bb84bd495d66726f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 14:34:38 +0700 Subject: [PATCH 021/100] gasZip redeployed to BSC staging --- deployments/bsc.diamond.staging.json | 4 ++-- deployments/bsc.staging.json | 4 ++-- script/tasks/diamondUpdatePeriphery.sh | 2 +- src/Periphery/GasZip.sol | 3 --- tmpfile | 0 5 files changed, 5 insertions(+), 8 deletions(-) create mode 100644 tmpfile diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 8b7323b36..fe1b3889c 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -80,7 +80,7 @@ "RelayerCelerIM": "", "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZip": "0xC7FC1802D86aE031EC979158F8E1D90bAb30dBC1" + "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A" } } -} +} \ No newline at end of file diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index d23c543d9..a2ecd34f7 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -25,7 +25,7 @@ "StargateFacet": "0xAfcC5c55d5Ec3082675D51331E7Ed9AdE195db48", "ThorSwapFacet": "0xa6aAe470E7B8E8916e692882A5db25bB40C398A7", "AmarokFacetPacked": "0x7ac3EB2D191EBAb9E925CAbFD4F8155be066b3aa", - "GasZip": "0xC7FC1802D86aE031EC979158F8E1D90bAb30dBC1", + "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A", "MayanBridgeFacet": "0x5Ba4FeD1DAd2fD057A9f687B399B8e4cF2368214", "MayanFacet": "0xcED4B608A468ce334c75c6652e18E2Ba7f3F44dA" -} +} \ No newline at end of file diff --git a/script/tasks/diamondUpdatePeriphery.sh b/script/tasks/diamondUpdatePeriphery.sh index 0d766b958..393b63e00 100755 --- a/script/tasks/diamondUpdatePeriphery.sh +++ b/script/tasks/diamondUpdatePeriphery.sh @@ -170,7 +170,7 @@ register() { echo "" # check that the contract is actually deployed - local CODE_SIZE = $(cast codesize "$ADDR" --rpc-url "${!RPC}") + local CODE_SIZE=$(cast codesize "$ADDR" --rpc-url "${!RPC}") if [ $CODE_SIZE -eq 0 ]; then error "contract $CONTRACT_NAME is not deployed on network $NETWORK - exiting script now" return 1 diff --git a/src/Periphery/GasZip.sol b/src/Periphery/GasZip.sol index 34598ba5a..d0108335e 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Periphery/GasZip.sol @@ -5,9 +5,6 @@ import { LibSwap } from "../Libraries/LibSwap.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { ERC20 } from "solady/tokens/ERC20.sol"; -//TODO: REMOVE <<<<<<<<<<<<<<<<<<<<< -import { console2 } from "forge-std/console2.sol"; - interface IGasZip { function deposit( uint256 destinationChain, diff --git a/tmpfile b/tmpfile new file mode 100644 index 000000000..e69de29bb From bd118ff6d50d4c8bb81955bb883a68ff0c349bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 26 Jun 2024 16:08:17 +0700 Subject: [PATCH 022/100] update .gitmodules file --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index a41d0934f..9b49e2820 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,5 +21,5 @@ url = https://github.com/openzeppelin/openzeppelin-contracts [submodule "lib/solady"] path = lib/solady - url = https://github.com/lib/solady + url = https://github.com/Vectorized/solady From 37bcf04bd90a9fe12bdbc8f0de88d47bd3edd873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 27 Jun 2024 09:07:14 +0700 Subject: [PATCH 023/100] removes ERC20 refund handling and adds complex test case --- ...oyGasZip.s.sol => DeployGasZipFacet.s.sol} | 8 +- .../GasZip.sol => Facets/GasZipFacet.sol} | 36 +- test/solidity/Facets/GasZipFacet.t.sol | 345 ++++++++++++++++++ test/solidity/Facets/GenericSwapFacetV3.t.sol | 1 - test/solidity/Periphery/GasZip.t.sol | 134 ------- 5 files changed, 357 insertions(+), 167 deletions(-) rename script/deploy/facets/{DeployGasZip.s.sol => DeployGasZipFacet.s.sol} (75%) rename src/{Periphery/GasZip.sol => Facets/GasZipFacet.sol} (74%) create mode 100644 test/solidity/Facets/GasZipFacet.t.sol delete mode 100644 test/solidity/Periphery/GasZip.t.sol diff --git a/script/deploy/facets/DeployGasZip.s.sol b/script/deploy/facets/DeployGasZipFacet.s.sol similarity index 75% rename from script/deploy/facets/DeployGasZip.s.sol rename to script/deploy/facets/DeployGasZipFacet.s.sol index 8d95f31ad..627bb265f 100644 --- a/script/deploy/facets/DeployGasZip.s.sol +++ b/script/deploy/facets/DeployGasZipFacet.s.sol @@ -2,21 +2,21 @@ pragma solidity ^0.8.17; import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; -import { GasZip } from "lifi/Periphery/GasZip.sol"; +import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; import { stdJson } from "forge-std/Script.sol"; contract DeployScript is DeployScriptBase { using stdJson for string; - constructor() DeployScriptBase("GasZip") {} + constructor() DeployScriptBase("GasZipFacet") {} function run() public - returns (GasZip deployed, bytes memory constructorArgs) + returns (GasZipFacet deployed, bytes memory constructorArgs) { constructorArgs = getConstructorArgs(); - deployed = GasZip(deploy(type(GasZip).creationCode)); + deployed = GasZipFacet(deploy(type(GasZipFacet).creationCode)); } function getConstructorArgs() internal override returns (bytes memory) { diff --git a/src/Periphery/GasZip.sol b/src/Facets/GasZipFacet.sol similarity index 74% rename from src/Periphery/GasZip.sol rename to src/Facets/GasZipFacet.sol index d0108335e..9bea8f27b 100644 --- a/src/Periphery/GasZip.sol +++ b/src/Facets/GasZipFacet.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.17; import { LibSwap } from "../Libraries/LibSwap.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { ERC20 } from "solady/tokens/ERC20.sol"; +import { NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; interface IGasZip { function deposit( @@ -12,22 +13,16 @@ interface IGasZip { ) external payable; } -/// @title GasZip +/// @title GasZipFacet /// @author LI.FI (https://li.fi) -/// @notice Provides functionality to swap and trigger gas.zip protocol +/// @notice Provides functionality to swap ERC20 tokens to native and deposit them to the gas.zip protocol (https://www.gas.zip/) /// @custom:version 1.0.0 -contract GasZip { +contract GasZipFacet { using SafeTransferLib for address; /// State /// - address public immutable ZERO = address(0); IGasZip public immutable gasZipRouter; - /// Errors /// - error TransferFailed(); - - /// Events /// - /// Constructor /// constructor(address _gasZipRouter) { gasZipRouter = IGasZip(_gasZipRouter); @@ -37,12 +32,10 @@ contract GasZip { /// @param _swapData The swap data struct /// @param _destinationChainId the id of the chain where gas should be made available /// @param _recipient the address to receive the gas on dst chain - /// @param _refundAddress the address to receive the gas on dst chain - function zipERC20( + function depositToGasZipERC20( LibSwap.SwapData calldata _swapData, uint256 _destinationChainId, - address _recipient, - address _refundAddress + address _recipient ) public { // pull tokens from caller (e.g. LI.FI diamond) _swapData.sendingAssetId.safeTransferFrom( @@ -59,26 +52,13 @@ contract GasZip { _destinationChainId, _recipient ); - - // check remaining balance of sendingAsset - uint256 remainingBalance = ERC20(_swapData.sendingAssetId).balanceOf( - address(this) - ); - - // Send back any remaining sendingAsset tokens to the sender - if (remainingBalance > 0) { - _swapData.sendingAssetId.safeTransfer( - _refundAddress, - remainingBalance - ); - } } /// @notice Deposits native tokens in the GasZip router contract and returns any unused /// @param _amountToZip The swap data struct /// @param _destinationChainId the id of the chain where gas should be made available /// @param _recipient the address to receive the gas on dst chain - function zip( + function depositToGasZipNative( uint256 _amountToZip, uint256 _destinationChainId, address _recipient @@ -94,7 +74,7 @@ contract GasZip { if (nativeBalance > 0) { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = msg.sender.call{ value: nativeBalance }(""); - if (!success) revert TransferFailed(); + if (!success) revert NativeAssetTransferFailed(); } } diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol new file mode 100644 index 000000000..0da49d11d --- /dev/null +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.17; + +import { Test, DSTest } from "forge-std/Test.sol"; +import { console } from "../utils/Console.sol"; +import { DiamondTest, LiFiDiamond } from "../utils/DiamondTest.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; +import { LibSwap } from "lifi/Libraries/LibSwap.sol"; +import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; +import { FeeCollector } from "lifi/Periphery/FeeCollector.sol"; +import { ContractCallNotAllowed, CumulativeSlippageTooHigh, NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; + +import { UniswapV2Router02 } from "../utils/Interfaces.sol"; +import { TestHelpers, MockUniswapDEX, NonETHReceiver } from "../utils/TestHelpers.sol"; +import { ERC20 } from "solady/tokens/ERC20.sol"; +import { GnosisBridgeFacet } from "lifi/Facets/GnosisBridgeFacet.sol"; +import { IXDaiBridge } from "lifi/Interfaces/IXDaiBridge.sol"; +import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; + +// Stub GenericSwapFacet Contract +contract TestGasZipFacet is GasZipFacet { + constructor(address gasZipRouter) GasZipFacet(gasZipRouter) {} + + function addDex(address _dex) external { + LibAllowList.addAllowedContract(_dex); + } + + function removeDex(address _dex) external { + LibAllowList.removeAllowedContract(_dex); + } + + function setFunctionApprovalBySignature(bytes4 _signature) external { + LibAllowList.addAllowedSelector(_signature); + } +} + +contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { + address public constant GAS_ZIP_ROUTER_MAINNET = + 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; + address internal ADDRESS_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address internal ADDRESS_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address internal ADDRESS_DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address internal ADDRESS_UNISWAP = + 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; + address internal constant XDAI_BRIDGE = + 0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016; + + LiFiDiamond internal diamond; + TestGasZipFacet internal gasZipFacet; + ERC20 internal usdc; + ERC20 internal dai; + UniswapV2Router02 internal uniswap; + FeeCollector internal feeCollector; + + uint256 public defaultDestinationChains = 96; + address public defaultRecipientAddress = address(12345); + address public defaultRefundAddress = address(56789); + uint256 public defaultNativeAmount = 0.0006 ether; + uint256 public defaultUSDCAmount; + + event Deposit(address from, uint256 chains, uint256 amount, address to); + + function fork() internal { + string memory rpcUrl = vm.envString("ETH_NODE_URI_MAINNET"); + uint256 blockNumber = 20173181; + vm.createSelectFork(rpcUrl, blockNumber); + } + + function setUp() public { + fork(); + + // deploy contracts + diamond = createDiamond(); + gasZipFacet = new TestGasZipFacet(GAS_ZIP_ROUTER_MAINNET); + usdc = ERC20(ADDRESS_USDC); + dai = ERC20(ADDRESS_DAI); + uniswap = UniswapV2Router02(ADDRESS_UNISWAP); + feeCollector = new FeeCollector(address(this)); + + defaultUSDCAmount = 10 * 10 ** usdc.decimals(); // 10 USDC + + // add genericSwapFacet (v1) to diamond (for gas usage comparison) + bytes4[] memory functionSelectors = new bytes4[](5); + functionSelectors[0] = gasZipFacet.depositToGasZipNative.selector; + functionSelectors[1] = gasZipFacet.depositToGasZipERC20.selector; + functionSelectors[2] = gasZipFacet.addDex.selector; + functionSelectors[3] = gasZipFacet.removeDex.selector; + functionSelectors[4] = gasZipFacet + .setFunctionApprovalBySignature + .selector; + addFacet(diamond, address(gasZipFacet), functionSelectors); + + gasZipFacet = TestGasZipFacet(payable(address(diamond))); + + // whitelist uniswap dex with function selectors + // v1 + gasZipFacet.addDex(address(uniswap)); + gasZipFacet.setFunctionApprovalBySignature( + uniswap.swapExactTokensForTokens.selector + ); + + vm.label(address(gasZipFacet), "LiFiDiamond"); + vm.label(ADDRESS_WETH, "WETH_TOKEN"); + vm.label(ADDRESS_USDC, "USDC_TOKEN"); + vm.label(ADDRESS_UNISWAP, "UNISWAP_V2_ROUTER"); + } + + function test_canDepositNative() public { + // set up expected event + vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); + emit Deposit( + address(gasZipFacet), + defaultDestinationChains, + defaultNativeAmount, + defaultRecipientAddress + ); + + // deposit via GasZip periphery contract + gasZipFacet.depositToGasZipNative{ value: defaultNativeAmount }( + defaultNativeAmount, + defaultDestinationChains, + defaultRecipientAddress + ); + } + + function test_canCollectFeesThenSwapThenDepositERC20ThenBridge() public { + // Testcase: + // 1. pay 1 USDC fee to FeeCollector in USDC + // 2. swap remaining (9) USDC to DAI + // 3. deposit 2 DAI to gasZip + // 4. bridge remaining DAI to Gnosis using GnosisBridgeFacet + + deal(ADDRESS_USDC, address(this), defaultUSDCAmount); + + // get swapData for feeCollection + LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](3); + uint256 feeCollectionAmount = 1 * 10 ** usdc.decimals(); // 1 USD + + swapData[0] = LibSwap.SwapData( + address(feeCollector), + address(feeCollector), + ADDRESS_USDC, + ADDRESS_USDC, + defaultUSDCAmount, + abi.encodeWithSelector( + feeCollector.collectTokenFees.selector, + ADDRESS_USDC, + feeCollectionAmount, + 0, + address(this) + ), + true + ); + + // get swapData for swap + uint256 swapInputAmount = defaultUSDCAmount - feeCollectionAmount; + // prepare swap data + address[] memory path = new address[](2); + path[0] = ADDRESS_USDC; + path[1] = ADDRESS_DAI; + + // Calculate USDC input amount + uint256[] memory amounts = uniswap.getAmountsOut( + swapInputAmount, + path + ); + uint256 swapOutputAmount = amounts[1]; + + swapData[1] = LibSwap.SwapData( + address(uniswap), + address(uniswap), + ADDRESS_USDC, + ADDRESS_DAI, + swapInputAmount, + abi.encodeWithSelector( + uniswap.swapExactTokensForTokens.selector, + swapInputAmount, + swapOutputAmount, + path, + address(diamond), + block.timestamp + 20 minutes + ), + false // not required since tokens are already in diamond + ); + + // // get swapData for gas zip + uint256 gasZipERC20Amount = 2 * 10 ** dai.decimals(); + ( + LibSwap.SwapData memory gasZipSwapData, + + ) = _getUniswapCalldataForERC20ToNativeSwap( + ADDRESS_DAI, + gasZipERC20Amount + ); + + swapData[2] = LibSwap.SwapData( + address(gasZipFacet), + address(gasZipFacet), + ADDRESS_DAI, + ADDRESS_DAI, + gasZipERC20Amount, + abi.encodeWithSelector( + gasZipFacet.depositToGasZipERC20.selector, + gasZipSwapData, + defaultDestinationChains, + defaultRecipientAddress + ), + false // not required since tokens are already in the diamond + ); + + // get BridgeData + ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({ + transactionId: "", + bridge: "GnosisBridge", + integrator: "", + referrer: address(0), + sendingAssetId: ADDRESS_DAI, + receiver: defaultRecipientAddress, + minAmount: swapOutputAmount - gasZipERC20Amount, + destinationChainId: 100, + hasSourceSwaps: true, + hasDestinationCall: false + }); + + // whitelist gasZipFacet and FeeCollector + gasZipFacet.addDex(address(gasZipFacet)); + gasZipFacet.setFunctionApprovalBySignature( + gasZipFacet.depositToGasZipERC20.selector + ); + gasZipFacet.addDex(address(feeCollector)); + gasZipFacet.setFunctionApprovalBySignature( + feeCollector.collectTokenFees.selector + ); + + // bridge using (standalone) GnosisBridgeFacet + TestGnosisBridgeFacet gnosisBridgeFacet = _getGnosisBridgeFacet(); + + // set approval for bridging + usdc.approve(address(gnosisBridgeFacet), defaultUSDCAmount); + + gnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge( + bridgeData, + swapData + ); + } + + function _getGnosisBridgeFacet() + internal + returns (TestGnosisBridgeFacet gnosisBridgeFacet) + { + gnosisBridgeFacet = new TestGnosisBridgeFacet( + IXDaiBridge(XDAI_BRIDGE) + ); + + bytes4[] memory functionSelectors = new bytes4[](2); + functionSelectors[0] = gnosisBridgeFacet + .startBridgeTokensViaXDaiBridge + .selector; + functionSelectors[1] = gnosisBridgeFacet + .swapAndStartBridgeTokensViaXDaiBridge + .selector; + + addFacet(diamond, address(gnosisBridgeFacet), functionSelectors); + + gnosisBridgeFacet = TestGnosisBridgeFacet(address(diamond)); + } + + function test_canSwapERC20ToNativeAndDeposit() public { + ( + LibSwap.SwapData memory swapData, + uint256 amountOutMin + ) = _getUniswapCalldataForERC20ToNativeSwap( + ADDRESS_USDC, + defaultUSDCAmount + ); + + deal(ADDRESS_USDC, address(this), defaultUSDCAmount); + + ERC20(ADDRESS_USDC).approve(address(gasZipFacet), defaultUSDCAmount); + + // set up expected event + vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); + emit Deposit( + address(gasZipFacet), + defaultDestinationChains, + amountOutMin, + defaultRecipientAddress + ); + + // deposit via GasZip periphery contract + gasZipFacet.depositToGasZipERC20( + swapData, + defaultDestinationChains, + defaultRecipientAddress + ); + } + + function _getUniswapCalldataForERC20ToNativeSwap( + address sendingAssetId, + uint256 fromAmount + ) + internal + view + returns (LibSwap.SwapData memory swapData, uint256 amountOutMin) + { + // prepare swap data + address[] memory path = new address[](2); + path[0] = sendingAssetId; + path[1] = ADDRESS_WETH; + + // Calculate USDC input amount + uint256[] memory amounts = uniswap.getAmountsOut(fromAmount, path); + amountOutMin = amounts[1]; + + swapData = LibSwap.SwapData( + address(uniswap), + address(uniswap), + sendingAssetId, + ADDRESS_WETH, + fromAmount, + abi.encodeWithSelector( + uniswap.swapExactTokensForETH.selector, + fromAmount, + amountOutMin, + path, + address(gasZipFacet), + block.timestamp + 20 seconds + ), + false // not required since tokens are already in diamond + ); + } +} + +contract TestGnosisBridgeFacet is GnosisBridgeFacet { + constructor(IXDaiBridge _xDaiBridge) GnosisBridgeFacet(_xDaiBridge) {} + + function addDex(address _dex) external { + LibAllowList.addAllowedContract(_dex); + } + + function setFunctionApprovalBySignature(bytes4 _signature) external { + LibAllowList.addAllowedSelector(_signature); + } +} diff --git a/test/solidity/Facets/GenericSwapFacetV3.t.sol b/test/solidity/Facets/GenericSwapFacetV3.t.sol index 815ac05fe..166c62237 100644 --- a/test/solidity/Facets/GenericSwapFacetV3.t.sol +++ b/test/solidity/Facets/GenericSwapFacetV3.t.sol @@ -14,7 +14,6 @@ import { ERC20 } from "solmate/tokens/ERC20.sol"; import { ContractCallNotAllowed, CumulativeSlippageTooHigh, NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; import { UniswapV2Router02 } from "../utils/Interfaces.sol"; -// import { MockUniswapDEX } from "../utils/MockUniswapDEX.sol"; import { TestHelpers, MockUniswapDEX, NonETHReceiver } from "../utils/TestHelpers.sol"; import { ERC20, SafeTransferLib } from "solmate/utils/SafeTransferLib.sol"; diff --git a/test/solidity/Periphery/GasZip.t.sol b/test/solidity/Periphery/GasZip.t.sol deleted file mode 100644 index 762522761..000000000 --- a/test/solidity/Periphery/GasZip.t.sol +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: Unlicensed -pragma solidity 0.8.17; - -import { Test, DSTest } from "forge-std/Test.sol"; -import { console } from "../utils/Console.sol"; -import { Vm } from "forge-std/Vm.sol"; -import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; -import { GasZip } from "lifi/Periphery/GasZip.sol"; -import { LibSwap } from "lifi/Libraries/LibSwap.sol"; -import { UniswapV2Router02 } from "../utils/Interfaces.sol"; -import { ERC20 } from "solady/tokens/ERC20.sol"; - -contract GasZipTest is Test { - address public constant GAS_ZIP_ROUTER_MAINNET = - 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; - address internal ADDRESS_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address internal ADDRESS_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address internal ADDRESS_UNISWAP = - 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; - - GasZip public gasZip; - uint256 public defaultDestinationChains = 96; - address public defaultRecipient = address(12345); - address public defaultRefundAddress = address(56789); - uint256 public defaultNativeAmount = 0.0006 ether; - uint256 public defaultUSDCAmount = 10e6; - UniswapV2Router02 public uniswap; - - event Deposit(address from, uint256 chains, uint256 amount, address to); - - function setUp() public { - fork(); - gasZip = new GasZip(GAS_ZIP_ROUTER_MAINNET); - - uniswap = UniswapV2Router02(ADDRESS_UNISWAP); - - deal(address(this), 1 ether); - } - - function fork() internal { - string memory rpcUrl = vm.envString("ETH_NODE_URI_MAINNET"); - uint256 blockNumber = 20173181; - vm.createSelectFork(rpcUrl, blockNumber); - } - - function test_contractIsSetUpCorrectly() public { - gasZip = new GasZip(GAS_ZIP_ROUTER_MAINNET); - assertEq(address(gasZip.gasZipRouter()), GAS_ZIP_ROUTER_MAINNET); - } - - function test_canDepositNative() public { - // set up expected event - vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); - emit Deposit( - address(gasZip), - defaultDestinationChains, - defaultNativeAmount, - defaultRecipient - ); - - // deposit via GasZip periphery contract - gasZip.zip{ value: defaultNativeAmount }( - defaultNativeAmount, - defaultDestinationChains, - defaultRecipient - ); - } - - function test_canSwapERC20ToNativeAndDeposit() public { - ( - LibSwap.SwapData memory swapData, - uint256 amountOutMin - ) = _getUniswapCalldataForERC20ToNativeSwap( - ADDRESS_USDC, - defaultUSDCAmount - ); - - deal(ADDRESS_USDC, address(this), defaultUSDCAmount); - - ERC20(ADDRESS_USDC).approve(address(gasZip), defaultUSDCAmount); - - // set up expected event - vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); - emit Deposit( - address(gasZip), - defaultDestinationChains, - amountOutMin, - defaultRecipient - ); - - // deposit via GasZip periphery contract - gasZip.zipERC20( - swapData, - defaultDestinationChains, - defaultRecipient, - defaultRefundAddress - ); - } - - function _getUniswapCalldataForERC20ToNativeSwap( - address sendingAssetId, - uint256 fromAmount - ) - internal - view - returns (LibSwap.SwapData memory swapData, uint256 amountOutMin) - { - // prepare swap data - address[] memory path = new address[](2); - path[0] = sendingAssetId; - path[1] = ADDRESS_WETH; - - // Calculate USDC input amount - uint256[] memory amounts = uniswap.getAmountsOut(fromAmount, path); - amountOutMin = amounts[1]; - - swapData = LibSwap.SwapData( - address(uniswap), - address(uniswap), - sendingAssetId, - ADDRESS_WETH, - fromAmount, - abi.encodeWithSelector( - uniswap.swapExactTokensForETH.selector, - fromAmount, - amountOutMin, - path, - address(gasZip), - block.timestamp + 20 seconds - ), - true - ); - } -} From e96fc41d73fc9280843d661724066208be8f29cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 27 Jun 2024 09:09:22 +0700 Subject: [PATCH 024/100] removes coverage files --- lcov-filtered.info | 6037 -------------------------------------------- 1 file changed, 6037 deletions(-) delete mode 100644 lcov-filtered.info diff --git a/lcov-filtered.info b/lcov-filtered.info deleted file mode 100644 index 683b6eb1d..000000000 --- a/lcov-filtered.info +++ /dev/null @@ -1,6037 +0,0 @@ -TN: -SF:src/Facets/AccessManagerFacet.sol -FN:24,AccessManagerFacet.setCanExecute -FNDA:3,AccessManagerFacet.setCanExecute -DA:29,3 -DA:29,3 -DA:29,3 -BRDA:29,0,0,- -BRDA:29,0,1,3 -DA:30,0 -DA:30,0 -DA:32,3 -DA:32,3 -DA:33,3 -DA:33,3 -DA:36,3 -BRDA:36,1,0,2 -BRDA:36,1,1,1 -DA:37,2 -DA:37,2 -DA:39,1 -DA:39,1 -FN:46,AccessManagerFacet.addressCanExecuteMethod -FNDA:0,AccessManagerFacet.addressCanExecuteMethod -DA:50,0 -DA:50,0 -FNF:2 -FNH:1 -LF:8 -LH:6 -BRF:4 -BRH:3 -end_of_record -TN: -SF:src/Facets/AcrossFacet.sol -FN:44,AcrossFacet. -FNDA:0,AcrossFacet. -DA:45,0 -DA:45,0 -DA:46,0 -DA:46,0 -FN:54,AcrossFacet.startBridgeTokensViaAcross -FNDA:266,AcrossFacet.startBridgeTokensViaAcross -DA:66,261 -DA:66,261 -DA:70,259 -DA:70,259 -FN:77,AcrossFacet.swapAndStartBridgeTokensViaAcross -FNDA:6,AcrossFacet.swapAndStartBridgeTokensViaAcross -DA:90,3 -DA:90,3 -DA:96,2 -DA:96,2 -FN:104,AcrossFacet._startBridge -FNDA:261,AcrossFacet._startBridge -DA:108,261 -DA:108,261 -BRDA:108,0,0,- -BRDA:108,0,1,2 -DA:109,2 -DA:109,2 -DA:120,259 -DA:120,259 -DA:125,259 -DA:125,259 -DA:137,261 -DA:137,261 -FNF:4 -FNH:3 -LF:11 -LH:9 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/AcrossFacetPacked.sol -FN:47,AcrossFacetPacked. -FNDA:0,AcrossFacetPacked. -DA:52,0 -DA:52,0 -DA:53,0 -DA:53,0 -FN:61,AcrossFacetPacked.setApprovalForBridge -FNDA:19,AcrossFacetPacked.setApprovalForBridge -DA:64,19 -DA:64,19 -DA:64,57 -DA:64,38 -DA:66,38 -DA:66,38 -FN:76,AcrossFacetPacked.startBridgeTokensViaAcrossNativePacked -FNDA:2,AcrossFacetPacked.startBridgeTokensViaAcrossNativePacked -DA:78,2 -DA:78,2 -DA:78,2 -DA:81,2 -DA:81,2 -DA:92,2 -DA:92,2 -FN:103,AcrossFacetPacked.startBridgeTokensViaAcrossNativeMin -FNDA:2,AcrossFacetPacked.startBridgeTokensViaAcrossNativeMin -DA:113,2 -DA:113,2 -DA:124,2 -DA:124,2 -FN:129,AcrossFacetPacked.startBridgeTokensViaAcrossERC20Packed -FNDA:4,AcrossFacetPacked.startBridgeTokensViaAcrossERC20Packed -DA:130,4 -DA:130,4 -DA:130,4 -DA:131,4 -DA:131,4 -DA:131,4 -DA:134,4 -DA:134,4 -DA:141,4 -DA:141,4 -DA:141,4 -DA:144,4 -DA:144,4 -DA:155,4 -DA:155,4 -FN:168,AcrossFacetPacked.startBridgeTokensViaAcrossERC20Min -FNDA:4,AcrossFacetPacked.startBridgeTokensViaAcrossERC20Min -DA:180,4 -DA:180,4 -DA:187,4 -DA:187,4 -DA:198,4 -DA:198,4 -FN:209,AcrossFacetPacked.encode_startBridgeTokensViaAcrossNativePacked -FNDA:20,AcrossFacetPacked.encode_startBridgeTokensViaAcrossNativePacked -DA:220,20 -DA:220,20 -BRDA:220,0,0,1 -BRDA:220,0,1,19 -DA:225,19 -DA:225,19 -DA:226,19 -DA:226,19 -FN:250,AcrossFacetPacked.encode_startBridgeTokensViaAcrossERC20Packed -FNDA:40,AcrossFacetPacked.encode_startBridgeTokensViaAcrossERC20Packed -DA:263,40 -DA:263,40 -BRDA:263,1,0,1 -BRDA:263,1,1,39 -DA:268,39 -DA:268,39 -BRDA:268,2,0,1 -BRDA:268,2,1,38 -DA:273,38 -DA:273,38 -DA:274,38 -DA:274,38 -FN:292,AcrossFacetPacked.decode_startBridgeTokensViaAcrossNativePacked -FNDA:1,AcrossFacetPacked.decode_startBridgeTokensViaAcrossNativePacked -DA:302,1 -DA:302,1 -BRDA:302,3,0,- -BRDA:302,3,1,1 -DA:308,1 -DA:308,1 -DA:308,1 -DA:311,1 -DA:311,1 -DA:312,1 -DA:312,1 -DA:313,1 -DA:313,1 -DA:316,1 -DA:316,1 -DA:317,1 -DA:317,1 -DA:318,1 -DA:318,1 -DA:319,1 -DA:319,1 -DA:321,1 -DA:321,1 -FN:326,AcrossFacetPacked.decode_startBridgeTokensViaAcrossERC20Packed -FNDA:1,AcrossFacetPacked.decode_startBridgeTokensViaAcrossERC20Packed -DA:336,1 -DA:336,1 -BRDA:336,4,0,- -BRDA:336,4,1,1 -DA:342,1 -DA:342,1 -DA:342,1 -DA:344,1 -DA:344,1 -DA:345,1 -DA:345,1 -DA:346,1 -DA:346,1 -DA:347,1 -DA:347,1 -DA:348,1 -DA:348,1 -DA:351,1 -DA:351,1 -DA:352,1 -DA:352,1 -DA:353,1 -DA:353,1 -DA:354,1 -DA:354,1 -DA:356,1 -DA:356,1 -FN:365,AcrossFacetPacked.executeCallAndWithdraw -FNDA:2,AcrossFacetPacked.executeCallAndWithdraw -DA:374,1 -DA:374,1 -DA:374,1 -DA:377,1 -BRDA:377,5,0,1 -BRDA:377,5,1,- -DA:379,1 -DA:379,1 -DA:380,1 -DA:380,1 -DA:383,0 -DA:383,0 -FNF:11 -FNH:10 -LF:52 -LH:49 -BRF:12 -BRH:9 -end_of_record -TN: -SF:src/Facets/AllBridgeFacet.sol -FN:40,AllBridgeFacet. -FNDA:0,AllBridgeFacet. -DA:41,0 -DA:41,0 -FN:46,AllBridgeFacet.startBridgeTokensViaAllBridge -FNDA:8,AllBridgeFacet.startBridgeTokensViaAllBridge -DA:58,3 -DA:58,3 -DA:62,2 -DA:62,2 -FN:69,AllBridgeFacet.swapAndStartBridgeTokensViaAllBridge -FNDA:6,AllBridgeFacet.swapAndStartBridgeTokensViaAllBridge -DA:82,3 -DA:82,3 -DA:88,2 -DA:88,2 -FN:94,AllBridgeFacet._startBridge -FNDA:4,AllBridgeFacet._startBridge -DA:98,4 -DA:98,4 -DA:104,4 -BRDA:104,0,0,- -BRDA:104,0,1,2 -DA:105,2 -DA:105,2 -DA:116,2 -DA:116,2 -DA:128,4 -DA:128,4 -FNF:4 -FNH:3 -LF:10 -LH:9 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/AmarokFacet.sol -FN:43,AmarokFacet. -FNDA:0,AmarokFacet. -DA:44,0 -DA:44,0 -FN:52,AmarokFacet.startBridgeTokensViaAmarok -FNDA:265,AmarokFacet.startBridgeTokensViaAmarok -DA:64,261 -DA:64,261 -DA:66,260 -DA:66,260 -DA:71,259 -DA:71,259 -FN:78,AmarokFacet.swapAndStartBridgeTokensViaAmarok -FNDA:6,AmarokFacet.swapAndStartBridgeTokensViaAmarok -DA:91,3 -DA:91,3 -DA:93,3 -DA:93,3 -DA:101,2 -DA:101,2 -FN:109,AmarokFacet._startBridge -FNDA:261,AmarokFacet._startBridge -DA:114,261 -DA:114,261 -DA:121,261 -BRDA:121,0,0,- -BRDA:121,0,1,2 -DA:122,2 -DA:122,2 -DA:133,259 -DA:133,259 -DA:144,261 -DA:144,261 -FN:147,AmarokFacet.validateDestinationCallFlag -FNDA:264,AmarokFacet.validateDestinationCallFlag -DA:152,264 -DA:152,264 -BRDA:151,1,0,1 -BRDA:151,1,1,263 -DA:154,1 -DA:154,1 -FNF:5 -FNH:4 -LF:14 -LH:13 -BRF:4 -BRH:3 -end_of_record -TN: -SF:src/Facets/AmarokFacetPacked.sol -FN:33,AmarokFacetPacked. -FNDA:0,AmarokFacetPacked. -DA:37,0 -DA:37,0 -FN:45,AmarokFacetPacked.setApprovalForBridge -FNDA:13,AmarokFacetPacked.setApprovalForBridge -DA:48,13 -DA:48,13 -DA:50,13 -DA:50,13 -DA:50,39 -DA:50,26 -DA:52,26 -DA:52,26 -FN:62,AmarokFacetPacked.startBridgeTokensViaAmarokERC20PackedPayFeeWithAsset -FNDA:2,AmarokFacetPacked.startBridgeTokensViaAmarokERC20PackedPayFeeWithAsset -DA:64,2 -DA:64,2 -DA:64,2 -DA:65,2 -DA:65,2 -DA:65,2 -DA:66,2 -DA:66,2 -DA:66,2 -DA:67,2 -DA:67,2 -DA:67,2 -DA:70,2 -DA:70,2 -DA:77,2 -DA:77,2 -DA:88,2 -DA:88,2 -FN:91,AmarokFacetPacked.startBridgeTokensViaAmarokERC20PackedPayFeeWithNative -FNDA:2,AmarokFacetPacked.startBridgeTokensViaAmarokERC20PackedPayFeeWithNative -DA:96,2 -DA:96,2 -DA:96,2 -DA:97,2 -DA:97,2 -DA:97,2 -DA:98,2 -DA:98,2 -DA:98,2 -DA:101,2 -DA:101,2 -DA:108,2 -DA:108,2 -DA:118,2 -DA:118,2 -FN:129,AmarokFacetPacked.startBridgeTokensViaAmarokERC20MinPayFeeWithAsset -FNDA:2,AmarokFacetPacked.startBridgeTokensViaAmarokERC20MinPayFeeWithAsset -DA:139,2 -DA:139,2 -DA:146,2 -DA:146,2 -DA:157,2 -DA:157,2 -FN:167,AmarokFacetPacked.startBridgeTokensViaAmarokERC20MinPayFeeWithNative -FNDA:2,AmarokFacetPacked.startBridgeTokensViaAmarokERC20MinPayFeeWithNative -DA:176,2 -DA:176,2 -DA:183,2 -DA:183,2 -DA:193,2 -DA:193,2 -FN:204,AmarokFacetPacked.encode_startBridgeTokensViaAmarokERC20PackedPayFeeWithAsset -FNDA:16,AmarokFacetPacked.encode_startBridgeTokensViaAmarokERC20PackedPayFeeWithAsset -DA:213,16 -DA:213,16 -BRDA:213,0,0,1 -BRDA:213,0,1,15 -DA:217,15 -DA:217,15 -BRDA:217,1,0,1 -BRDA:217,1,1,14 -DA:221,14 -DA:221,14 -BRDA:221,2,0,1 -BRDA:221,2,1,13 -DA:226,13 -DA:226,13 -DA:227,13 -DA:227,13 -FN:248,AmarokFacetPacked.encode_startBridgeTokensViaAmarokERC20PackedPayFeeWithNative -FNDA:15,AmarokFacetPacked.encode_startBridgeTokensViaAmarokERC20PackedPayFeeWithNative -DA:256,15 -DA:256,15 -BRDA:256,3,0,1 -BRDA:256,3,1,14 -DA:260,14 -DA:260,14 -BRDA:260,4,0,1 -BRDA:260,4,1,13 -DA:265,13 -DA:265,13 -DA:266,13 -DA:266,13 -FN:281,AmarokFacetPacked.decode_startBridgeTokensViaAmarokERC20PackedPayFeeWithAsset -FNDA:1,AmarokFacetPacked.decode_startBridgeTokensViaAmarokERC20PackedPayFeeWithAsset -DA:288,1 -DA:288,1 -BRDA:288,5,0,- -BRDA:288,5,1,1 -DA:293,1 -DA:293,1 -DA:294,1 -DA:294,1 -DA:296,1 -DA:296,1 -DA:296,1 -DA:298,1 -DA:298,1 -DA:299,1 -DA:299,1 -DA:300,1 -DA:300,1 -DA:301,1 -DA:301,1 -DA:302,1 -DA:302,1 -DA:304,1 -DA:304,1 -DA:305,1 -DA:305,1 -DA:306,1 -DA:306,1 -DA:307,1 -DA:307,1 -DA:308,1 -DA:308,1 -DA:309,1 -DA:309,1 -DA:310,1 -DA:310,1 -DA:312,1 -DA:312,1 -FN:317,AmarokFacetPacked.decode_startBridgeTokensViaAmarokERC20PackedPayFeeWithNative -FNDA:1,AmarokFacetPacked.decode_startBridgeTokensViaAmarokERC20PackedPayFeeWithNative -DA:324,1 -DA:324,1 -BRDA:324,6,0,- -BRDA:324,6,1,1 -DA:329,1 -DA:329,1 -DA:330,1 -DA:330,1 -DA:332,1 -DA:332,1 -DA:332,1 -DA:334,1 -DA:334,1 -DA:335,1 -DA:335,1 -DA:336,1 -DA:336,1 -DA:337,1 -DA:337,1 -DA:338,1 -DA:338,1 -DA:340,1 -DA:340,1 -DA:341,1 -DA:341,1 -DA:342,1 -DA:342,1 -DA:343,1 -DA:343,1 -DA:346,1 -DA:346,1 -DA:347,1 -DA:347,1 -DA:349,1 -DA:349,1 -FN:352,AmarokFacetPacked.getChainIdForDomain -FNDA:0,AmarokFacetPacked.getChainIdForDomain -DA:355,2 -DA:355,2 -BRDA:355,7,0,- -BRDA:355,7,1,2 -DA:355,0 -DA:357,2 -DA:357,2 -BRDA:357,8,0,- -BRDA:357,8,1,2 -DA:357,0 -DA:359,2 -DA:359,2 -BRDA:359,9,0,2 -BRDA:359,9,1,- -DA:359,2 -DA:361,0 -DA:361,0 -BRDA:361,10,0,- -BRDA:361,10,1,- -DA:361,0 -DA:363,0 -DA:363,0 -BRDA:363,11,0,- -BRDA:363,11,1,- -DA:363,0 -DA:365,0 -DA:365,0 -BRDA:365,12,0,- -BRDA:365,12,1,- -DA:365,0 -DA:367,0 -DA:367,0 -BRDA:367,13,0,- -BRDA:367,13,1,- -DA:367,0 -FNF:11 -FNH:9 -LF:72 -LH:67 -BRF:28 -BRH:15 -end_of_record -TN: -SF:src/Facets/ArbitrumBridgeFacet.sol -FN:46,ArbitrumBridgeFacet. -FNDA:0,ArbitrumBridgeFacet. -DA:47,0 -DA:47,0 -DA:48,0 -DA:48,0 -FN:56,ArbitrumBridgeFacet.startBridgeTokensViaArbitrumBridge -FNDA:265,ArbitrumBridgeFacet.startBridgeTokensViaArbitrumBridge -DA:68,260 -DA:68,260 -DA:68,260 -DA:69,260 -DA:69,260 -DA:72,260 -DA:72,260 -DA:77,259 -DA:77,259 -FN:84,ArbitrumBridgeFacet.swapAndStartBridgeTokensViaArbitrumBridge -FNDA:6,ArbitrumBridgeFacet.swapAndStartBridgeTokensViaArbitrumBridge -DA:97,3 -DA:97,3 -DA:97,3 -DA:98,3 -DA:98,3 -DA:101,3 -DA:101,3 -DA:109,2 -DA:109,2 -FN:118,ArbitrumBridgeFacet._startBridge -FNDA:261,ArbitrumBridgeFacet._startBridge -DA:123,261 -DA:123,261 -BRDA:123,0,0,- -BRDA:123,0,1,2 -DA:124,2 -DA:124,2 -DA:137,259 -DA:137,259 -DA:142,259 -DA:142,259 -DA:152,261 -DA:152,261 -FNF:4 -FNH:3 -LF:15 -LH:13 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/CBridgeFacet.sol -FN:47,CBridgeFacet. -FNDA:0,CBridgeFacet. -DA:48,0 -DA:48,0 -FN:56,CBridgeFacet.startBridgeTokensViaCBridge -FNDA:268,CBridgeFacet.startBridgeTokensViaCBridge -DA:68,263 -DA:68,263 -DA:72,261 -DA:72,261 -FN:79,CBridgeFacet.swapAndStartBridgeTokensViaCBridge -FNDA:14,CBridgeFacet.swapAndStartBridgeTokensViaCBridge -DA:92,10 -DA:92,10 -DA:98,9 -DA:98,9 -FN:107,CBridgeFacet.triggerRefund -FNDA:0,CBridgeFacet.triggerRefund -DA:114,0 -DA:114,0 -DA:114,0 -BRDA:114,0,0,- -BRDA:114,0,1,- -DA:115,0 -DA:115,0 -DA:119,0 -DA:119,0 -DA:119,0 -BRDA:119,1,0,- -BRDA:119,1,1,- -DA:120,0 -DA:120,0 -DA:124,0 -DA:124,0 -DA:125,0 -DA:125,0 -DA:126,0 -DA:126,0 -BRDA:126,2,0,- -BRDA:126,2,1,- -DA:127,0 -DA:127,0 -DA:131,0 -DA:131,0 -DA:131,0 -DA:132,0 -DA:132,0 -DA:133,0 -DA:133,0 -FN:141,CBridgeFacet._startBridge -FNDA:270,CBridgeFacet._startBridge -DA:145,270 -DA:145,270 -BRDA:145,3,0,- -BRDA:145,3,1,4 -DA:146,4 -DA:146,4 -DA:155,266 -DA:155,266 -DA:161,266 -DA:161,266 -DA:171,270 -DA:171,270 -FNF:5 -FNH:3 -LF:21 -LH:9 -BRF:8 -BRH:1 -end_of_record -TN: -SF:src/Facets/CBridgeFacetPacked.sol -FN:40,CBridgeFacetPacked. -FNDA:0,CBridgeFacetPacked. -DA:44,0 -DA:44,0 -FN:52,CBridgeFacetPacked.setApprovalForBridge -FNDA:28,CBridgeFacetPacked.setApprovalForBridge -DA:55,28 -DA:55,28 -DA:55,70 -DA:55,42 -DA:57,42 -DA:57,42 -FN:74,CBridgeFacetPacked.triggerRefund -FNDA:1,CBridgeFacetPacked.triggerRefund -DA:82,1 -DA:82,1 -DA:82,1 -BRDA:82,0,0,- -BRDA:82,0,1,1 -DA:83,0 -DA:83,0 -DA:87,1 -DA:87,1 -DA:88,1 -DA:88,1 -DA:89,1 -DA:89,1 -BRDA:89,1,0,- -BRDA:89,1,1,1 -DA:90,0 -DA:90,0 -DA:94,1 -DA:94,1 -DA:94,1 -DA:95,1 -DA:95,1 -DA:96,1 -DA:96,1 -FN:101,CBridgeFacetPacked.startBridgeTokensViaCBridgeNativePacked -FNDA:6,CBridgeFacetPacked.startBridgeTokensViaCBridgeNativePacked -DA:102,6 -DA:102,6 -DA:110,6 -DA:110,6 -FN:119,CBridgeFacetPacked.startBridgeTokensViaCBridgeNativeMin -FNDA:3,CBridgeFacetPacked.startBridgeTokensViaCBridgeNativeMin -DA:126,3 -DA:126,3 -DA:134,3 -DA:134,3 -FN:139,CBridgeFacetPacked.startBridgeTokensViaCBridgeERC20Packed -FNDA:8,CBridgeFacetPacked.startBridgeTokensViaCBridgeERC20Packed -DA:140,8 -DA:140,8 -DA:140,8 -DA:141,8 -DA:141,8 -DA:141,8 -DA:144,8 -DA:144,8 -DA:152,8 -DA:152,8 -DA:161,8 -DA:161,8 -FN:172,CBridgeFacetPacked.startBridgeTokensViaCBridgeERC20Min -FNDA:4,CBridgeFacetPacked.startBridgeTokensViaCBridgeERC20Min -DA:182,4 -DA:182,4 -DA:190,4 -DA:190,4 -DA:199,4 -DA:199,4 -FN:210,CBridgeFacetPacked.encode_startBridgeTokensViaCBridgeNativePacked -FNDA:33,CBridgeFacetPacked.encode_startBridgeTokensViaCBridgeNativePacked -DA:217,33 -DA:217,33 -BRDA:217,2,0,1 -BRDA:217,2,1,32 -DA:221,32 -DA:221,32 -BRDA:221,3,0,1 -BRDA:221,3,1,31 -DA:226,31 -DA:226,31 -DA:227,31 -DA:227,31 -FN:241,CBridgeFacetPacked.decode_startBridgeTokensViaCBridgeNativePacked -FNDA:1,CBridgeFacetPacked.decode_startBridgeTokensViaCBridgeNativePacked -DA:248,1 -DA:248,1 -BRDA:248,4,0,- -BRDA:248,4,1,1 -DA:253,1 -DA:253,1 -DA:254,1 -DA:254,1 -DA:256,1 -DA:256,1 -DA:257,1 -DA:257,1 -DA:258,1 -DA:258,1 -DA:259,1 -DA:259,1 -DA:260,1 -DA:260,1 -DA:262,1 -DA:262,1 -FN:273,CBridgeFacetPacked.encode_startBridgeTokensViaCBridgeERC20Packed -FNDA:49,CBridgeFacetPacked.encode_startBridgeTokensViaCBridgeERC20Packed -DA:282,49 -DA:282,49 -BRDA:282,5,0,1 -BRDA:282,5,1,48 -DA:286,48 -DA:286,48 -BRDA:286,6,0,1 -BRDA:286,6,1,47 -DA:290,47 -DA:290,47 -BRDA:290,7,0,1 -BRDA:290,7,1,46 -DA:295,46 -DA:295,46 -DA:296,46 -DA:296,46 -FN:310,CBridgeFacetPacked.decode_startBridgeTokensViaCBridgeERC20Packed -FNDA:1,CBridgeFacetPacked.decode_startBridgeTokensViaCBridgeERC20Packed -DA:317,1 -DA:317,1 -BRDA:317,8,0,- -BRDA:317,8,1,1 -DA:319,1 -DA:319,1 -DA:320,1 -DA:320,1 -DA:322,1 -DA:322,1 -DA:323,1 -DA:323,1 -DA:324,1 -DA:324,1 -DA:325,1 -DA:325,1 -DA:326,1 -DA:326,1 -DA:327,1 -DA:327,1 -DA:328,1 -DA:328,1 -DA:330,1 -DA:330,1 -FNF:11 -FNH:10 -LF:53 -LH:50 -BRF:18 -BRH:14 -end_of_record -TN: -SF:src/Facets/CalldataVerificationFacet.sol -FN:22,CalldataVerificationFacet.extractBridgeData -FNDA:3,CalldataVerificationFacet.extractBridgeData -DA:25,3 -DA:25,3 -FN:31,CalldataVerificationFacet.extractSwapData -FNDA:4,CalldataVerificationFacet.extractSwapData -DA:34,4 -DA:34,4 -FN:41,CalldataVerificationFacet.extractData -FNDA:6,CalldataVerificationFacet.extractData -DA:51,6 -DA:51,6 -DA:52,6 -BRDA:52,0,0,2 -BRDA:52,0,1,6 -DA:53,2 -DA:53,2 -FN:66,CalldataVerificationFacet.extractMainParameters -FNDA:6,CalldataVerificationFacet.extractMainParameters -DA:81,10 -DA:81,10 -DA:81,10 -DA:83,10 -BRDA:83,1,0,- -BRDA:83,1,1,2 -DA:84,2 -DA:84,2 -DA:84,2 -DA:85,2 -DA:85,2 -DA:86,2 -DA:86,2 -DA:88,8 -DA:88,8 -DA:89,8 -DA:89,8 -DA:92,10 -DA:92,10 -FN:106,CalldataVerificationFacet.extractNonEVMAddress -FNDA:4,CalldataVerificationFacet.extractNonEVMAddress -DA:109,4 -DA:109,4 -DA:110,4 -DA:110,4 -DA:110,4 -DA:113,4 -DA:113,4 -DA:113,4 -BRDA:112,2,0,2 -BRDA:112,2,1,4 -DA:116,2 -DA:116,2 -DA:120,4 -BRDA:120,3,0,2 -BRDA:120,3,1,2 -DA:123,2 -DA:123,2 -DA:128,2 -DA:128,2 -FN:140,CalldataVerificationFacet.extractGenericSwapParameters -FNDA:2,CalldataVerificationFacet.extractGenericSwapParameters -DA:153,2 -DA:153,2 -DA:154,2 -DA:154,2 -DA:157,2 -DA:157,2 -DA:157,2 -BRDA:156,4,0,1 -BRDA:156,4,1,2 -DA:160,1 -DA:160,1 -DA:162,2 -DA:162,2 -DA:167,2 -DA:167,2 -DA:168,2 -DA:168,2 -DA:169,2 -DA:169,2 -DA:170,2 -DA:170,2 -FN:192,CalldataVerificationFacet.validateCalldata -FNDA:4,CalldataVerificationFacet.validateCalldata -DA:202,4 -DA:202,4 -DA:203,4 -DA:203,4 -DA:212,4 -DA:212,4 -DA:214,4 -DA:214,4 -DA:214,4 -DA:214,4 -DA:214,4 -DA:214,4 -DA:214,4 -DA:230,2 -DA:230,2 -DA:232,2 -DA:232,2 -FN:240,CalldataVerificationFacet.validateDestinationCalldata -FNDA:18,CalldataVerificationFacet.validateDestinationCalldata -DA:245,18 -DA:245,18 -DA:249,18 -DA:249,18 -DA:249,18 -BRDA:248,5,0,9 -BRDA:248,5,1,18 -DA:251,9 -DA:251,9 -DA:254,18 -DA:254,18 -DA:254,18 -DA:257,18 -DA:257,18 -BRDA:257,6,0,2 -BRDA:257,6,1,4 -DA:258,4 -DA:258,4 -DA:258,4 -DA:263,4 -DA:263,4 -DA:264,4 -DA:264,4 -DA:264,4 -DA:264,4 -DA:264,4 -DA:265,2 -DA:265,2 -DA:265,2 -DA:268,14 -DA:268,14 -BRDA:267,7,0,2 -BRDA:267,7,1,2 -DA:270,2 -DA:270,2 -DA:270,2 -DA:274,2 -DA:274,2 -DA:275,2 -DA:275,2 -DA:275,2 -DA:275,2 -DA:275,2 -DA:276,2 -DA:276,2 -DA:276,2 -DA:280,12 -DA:280,12 -BRDA:280,8,0,2 -BRDA:280,8,1,4 -DA:281,4 -DA:281,4 -DA:281,4 -DA:285,4 -DA:285,4 -DA:286,4 -DA:286,4 -DA:286,4 -DA:286,4 -DA:286,4 -DA:287,2 -DA:287,2 -DA:287,2 -DA:287,2 -DA:290,8 -DA:290,8 -BRDA:289,9,0,2 -BRDA:289,9,1,2 -DA:293,2 -DA:293,2 -DA:293,2 -DA:301,2 -DA:301,2 -DA:302,2 -DA:302,2 -DA:302,2 -DA:302,2 -DA:302,2 -DA:303,2 -DA:303,2 -DA:303,2 -DA:303,2 -DA:307,6 -DA:307,6 -BRDA:306,10,0,3 -BRDA:306,10,1,5 -DA:309,5 -DA:309,5 -DA:309,5 -DA:313,5 -DA:313,5 -DA:314,5 -DA:314,5 -DA:314,5 -DA:314,5 -DA:314,5 -DA:315,3 -DA:315,3 -DA:315,3 -DA:315,3 -DA:318,1 -DA:318,1 -BRDA:317,11,0,1 -BRDA:317,11,1,1 -DA:321,1 -DA:321,1 -DA:321,1 -DA:325,1 -DA:325,1 -DA:326,1 -DA:326,1 -DA:326,1 -DA:326,1 -DA:326,1 -DA:327,1 -DA:327,1 -DA:327,1 -DA:327,1 -DA:331,0 -DA:331,0 -FN:339,CalldataVerificationFacet._extractBridgeData -FNDA:23,CalldataVerificationFacet._extractBridgeData -DA:343,23 -DA:343,23 -DA:343,23 -BRDA:342,12,0,11 -BRDA:342,12,1,12 -DA:346,11 -DA:346,11 -DA:346,11 -DA:347,11 -DA:347,11 -DA:351,11 -DA:351,11 -DA:354,12 -DA:354,12 -FN:360,CalldataVerificationFacet._extractSwapData -FNDA:8,CalldataVerificationFacet._extractSwapData -DA:364,8 -DA:364,8 -DA:364,8 -BRDA:363,13,0,4 -BRDA:363,13,1,4 -DA:367,4 -DA:367,4 -DA:367,4 -DA:368,4 -DA:368,4 -DA:372,4 -DA:372,4 -DA:375,4 -DA:375,4 -FNF:10 -FNH:10 -LF:80 -LH:79 -BRF:28 -BRH:27 -end_of_record -TN: -SF:src/Facets/CelerCircleBridgeFacet.sol -FN:34,CelerCircleBridgeFacet. -FNDA:0,CelerCircleBridgeFacet. -DA:35,0 -DA:35,0 -DA:36,0 -DA:36,0 -FN:43,CelerCircleBridgeFacet.startBridgeTokensViaCelerCircleBridge -FNDA:265,CelerCircleBridgeFacet.startBridgeTokensViaCelerCircleBridge -DA:53,260 -DA:53,260 -DA:54,259 -DA:54,259 -FN:60,CelerCircleBridgeFacet.swapAndStartBridgeTokensViaCelerCircleBridge -FNDA:5,CelerCircleBridgeFacet.swapAndStartBridgeTokensViaCelerCircleBridge -DA:73,2 -DA:73,2 -DA:79,1 -DA:79,1 -FN:86,CelerCircleBridgeFacet._startBridge -FNDA:260,CelerCircleBridgeFacet._startBridge -DA:87,260 -DA:87,260 -BRDA:87,0,0,1 -BRDA:87,0,1,259 -DA:93,259 -DA:93,259 -DA:100,259 -DA:100,259 -DA:107,259 -DA:107,259 -FNF:4 -FNH:3 -LF:10 -LH:8 -BRF:2 -BRH:2 -end_of_record -TN: -SF:src/Facets/CelerIMFacetImmutable.sol -FN:19,CelerIMFacetImmutable. -FNDA:0,CelerIMFacetImmutable. -FNF:1 -FNH:0 -LF:0 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/CelerIMFacetMutable.sol -FN:19,CelerIMFacetMutable. -FNDA:0,CelerIMFacetMutable. -FNF:1 -FNH:0 -LF:0 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/CircleBridgeFacet.sol -FN:34,CircleBridgeFacet. -FNDA:0,CircleBridgeFacet. -DA:35,0 -DA:35,0 -DA:36,0 -DA:36,0 -FN:44,CircleBridgeFacet.startBridgeTokensViaCircleBridge -FNDA:264,CircleBridgeFacet.startBridgeTokensViaCircleBridge -DA:55,259 -DA:55,259 -DA:56,258 -DA:56,258 -FN:63,CircleBridgeFacet.swapAndStartBridgeTokensViaCircleBridge -FNDA:5,CircleBridgeFacet.swapAndStartBridgeTokensViaCircleBridge -DA:77,2 -DA:77,2 -DA:83,1 -DA:83,1 -FN:91,CircleBridgeFacet._startBridge -FNDA:259,CircleBridgeFacet._startBridge -DA:96,259 -DA:96,259 -DA:103,259 -DA:103,259 -DA:110,259 -DA:110,259 -FNF:4 -FNH:3 -LF:9 -LH:7 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/DeBridgeDlnFacet.sol -FN:49,DeBridgeDlnFacet. -FNDA:0,DeBridgeDlnFacet. -DA:50,0 -DA:50,0 -FN:58,DeBridgeDlnFacet.startBridgeTokensViaDeBridgeDln -FNDA:265,DeBridgeDlnFacet.startBridgeTokensViaDeBridgeDln -DA:70,260 -DA:70,260 -DA:74,259 -DA:74,259 -FN:85,DeBridgeDlnFacet.swapAndStartBridgeTokensViaDeBridgeDln -FNDA:7,DeBridgeDlnFacet.swapAndStartBridgeTokensViaDeBridgeDln -DA:98,4 -DA:98,4 -DA:98,4 -DA:99,4 -DA:99,4 -DA:100,4 -DA:100,4 -DA:107,3 -DA:107,3 -FN:115,DeBridgeDlnFacet._startBridge -FNDA:262,DeBridgeDlnFacet._startBridge -DA:120,262 -DA:120,262 -DA:120,262 -DA:135,262 -DA:135,262 -DA:136,262 -DA:136,262 -BRDA:136,0,0,- -BRDA:136,0,1,260 -DA:138,260 -DA:138,260 -DA:144,260 -DA:144,260 -DA:151,2 -DA:151,2 -DA:152,2 -DA:152,2 -DA:160,262 -DA:160,262 -DA:162,262 -DA:162,262 -BRDA:162,1,0,- -BRDA:162,1,1,262 -DA:163,0 -DA:163,0 -DA:170,262 -DA:170,262 -FNF:4 -FNH:3 -LF:18 -LH:16 -BRF:4 -BRH:2 -end_of_record -TN: -SF:src/Facets/DeBridgeFacet.sol -FN:51,DeBridgeFacet. -FNDA:0,DeBridgeFacet. -DA:52,0 -DA:52,0 -FN:60,DeBridgeFacet.startBridgeTokensViaDeBridge -FNDA:266,DeBridgeFacet.startBridgeTokensViaDeBridge -DA:71,262 -DA:71,262 -DA:73,261 -DA:73,261 -DA:77,259 -DA:77,259 -FN:84,DeBridgeFacet.swapAndStartBridgeTokensViaDeBridge -FNDA:6,DeBridgeFacet.swapAndStartBridgeTokensViaDeBridge -DA:96,3 -DA:96,3 -DA:98,3 -DA:98,3 -DA:106,2 -DA:106,2 -FN:114,DeBridgeFacet._startBridge -FNDA:261,DeBridgeFacet._startBridge -DA:118,261 -DA:118,261 -DA:118,261 -DA:120,261 -DA:120,261 -DA:120,261 -DA:124,261 -DA:124,261 -BRDA:124,0,0,- -BRDA:124,0,1,261 -DA:125,0 -DA:125,0 -DA:128,261 -DA:128,261 -DA:128,261 -DA:129,261 -DA:129,261 -DA:131,261 -BRDA:131,1,0,2 -BRDA:131,1,1,259 -DA:132,2 -DA:132,2 -DA:134,259 -DA:134,259 -DA:142,261 -DA:142,261 -DA:153,261 -DA:153,261 -FN:156,DeBridgeFacet.validateDestinationCallFlag -FNDA:265,DeBridgeFacet.validateDestinationCallFlag -DA:161,265 -DA:161,265 -BRDA:160,2,0,1 -BRDA:160,2,1,264 -DA:164,1 -DA:164,1 -FNF:5 -FNH:4 -LF:20 -LH:18 -BRF:6 -BRH:5 -end_of_record -TN: -SF:src/Facets/DexManagerFacet.sol -FN:27,DexManagerFacet.addDex -FNDA:4,DexManagerFacet.addDex -DA:28,4 -DA:28,4 -DA:28,4 -BRDA:28,0,0,- -BRDA:28,0,1,4 -DA:29,0 -DA:29,0 -DA:32,4 -DA:32,4 -DA:32,4 -BRDA:32,1,0,- -BRDA:32,1,1,4 -DA:33,0 -DA:33,0 -DA:36,4 -DA:36,4 -DA:38,2 -DA:38,2 -FN:43,DexManagerFacet.batchAddDex -FNDA:4,DexManagerFacet.batchAddDex -DA:44,4 -DA:44,4 -DA:44,4 -BRDA:44,2,0,- -BRDA:44,2,1,4 -DA:45,0 -DA:45,0 -DA:47,4 -DA:47,4 -DA:49,4 -DA:49,4 -DA:49,14 -DA:50,12 -DA:50,12 -DA:51,12 -DA:51,12 -DA:51,12 -BRDA:51,3,0,- -BRDA:51,3,1,12 -DA:52,0 -DA:52,0 -DA:54,12 -DA:54,12 -BRDA:54,4,0,- -BRDA:54,4,1,12 -DA:54,0 -DA:55,12 -DA:55,12 -DA:56,10 -DA:56,10 -DA:58,10 -DA:58,10 -FN:65,DexManagerFacet.removeDex -FNDA:1,DexManagerFacet.removeDex -DA:66,1 -DA:66,1 -DA:66,1 -BRDA:66,5,0,- -BRDA:66,5,1,1 -DA:67,0 -DA:67,0 -DA:69,1 -DA:69,1 -DA:70,1 -DA:70,1 -FN:75,DexManagerFacet.batchRemoveDex -FNDA:1,DexManagerFacet.batchRemoveDex -DA:76,1 -DA:76,1 -DA:76,1 -BRDA:76,6,0,- -BRDA:76,6,1,1 -DA:77,0 -DA:77,0 -DA:79,1 -DA:79,1 -DA:80,1 -DA:80,1 -DA:80,3 -DA:81,2 -DA:81,2 -DA:82,2 -DA:82,2 -DA:84,2 -DA:84,2 -FN:92,DexManagerFacet.setFunctionApprovalBySignature -FNDA:1,DexManagerFacet.setFunctionApprovalBySignature -DA:96,1 -DA:96,1 -DA:96,1 -BRDA:96,7,0,- -BRDA:96,7,1,1 -DA:97,0 -DA:97,0 -DA:100,1 -BRDA:100,8,0,1 -BRDA:100,8,1,- -DA:101,1 -DA:101,1 -DA:103,0 -DA:103,0 -DA:106,1 -DA:106,1 -FN:112,DexManagerFacet.batchSetFunctionApprovalBySignature -FNDA:1,DexManagerFacet.batchSetFunctionApprovalBySignature -DA:116,1 -DA:116,1 -DA:116,1 -BRDA:116,9,0,- -BRDA:116,9,1,1 -DA:117,0 -DA:117,0 -DA:119,1 -DA:119,1 -DA:120,1 -DA:120,1 -DA:120,6 -DA:121,5 -DA:121,5 -DA:122,5 -BRDA:122,10,0,5 -BRDA:122,10,1,- -DA:123,5 -DA:123,5 -DA:125,0 -DA:125,0 -DA:127,5 -DA:127,5 -DA:129,5 -DA:129,5 -FN:137,DexManagerFacet.isFunctionApproved -FNDA:6,DexManagerFacet.isFunctionApproved -DA:140,6 -DA:140,6 -DA:140,6 -FN:145,DexManagerFacet.approvedDexs -FNDA:4,DexManagerFacet.approvedDexs -DA:150,4 -DA:150,4 -DA:150,4 -FNF:8 -FNH:8 -LF:46 -LH:36 -BRF:22 -BRH:11 -end_of_record -TN: -SF:src/Facets/DiamondCutFacet.sol -FN:18,DiamondCutFacet.diamondCut -FNDA:1508,DiamondCutFacet.diamondCut -DA:23,1508 -DA:23,1508 -DA:24,1508 -DA:24,1508 -FNF:1 -FNH:1 -LF:2 -LH:2 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/DiamondLoupeFacet.sol -FN:24,DiamondLoupeFacet.facets -FNDA:0,DiamondLoupeFacet.facets -DA:25,0 -DA:25,0 -DA:25,0 -DA:26,0 -DA:26,0 -DA:27,0 -DA:27,0 -DA:28,0 -DA:28,0 -DA:28,0 -DA:29,0 -DA:29,0 -DA:30,0 -DA:30,0 -DA:31,0 -DA:31,0 -DA:35,0 -DA:35,0 -FN:43,DiamondLoupeFacet.facetFunctionSelectors -FNDA:0,DiamondLoupeFacet.facetFunctionSelectors -DA:51,0 -DA:51,0 -DA:51,0 -DA:52,0 -DA:52,0 -FN:59,DiamondLoupeFacet.facetAddresses -FNDA:0,DiamondLoupeFacet.facetAddresses -DA:65,0 -DA:65,0 -DA:65,0 -DA:66,0 -DA:66,0 -FN:73,DiamondLoupeFacet.facetAddress -FNDA:0,DiamondLoupeFacet.facetAddress -DA:76,0 -DA:76,0 -DA:76,0 -DA:77,0 -DA:77,0 -FN:83,DiamondLoupeFacet.supportsInterface -FNDA:0,DiamondLoupeFacet.supportsInterface -DA:86,0 -DA:86,0 -DA:86,0 -DA:87,0 -DA:87,0 -FNF:5 -FNH:0 -LF:16 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/GenericSwapFacet.sol -FN:27,GenericSwapFacet.swapTokensGeneric -FNDA:19,GenericSwapFacet.swapTokensGeneric -DA:35,19 -DA:35,19 -BRDA:35,0,0,- -BRDA:35,0,1,19 -DA:36,0 -DA:36,0 -DA:39,19 -DA:39,19 -DA:39,19 -DA:45,19 -DA:45,19 -DA:47,19 -DA:47,19 -DA:49,19 -DA:49,19 -FNF:1 -FNH:1 -LF:6 -LH:5 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/GenericSwapFacetV3.sol -FN:32,GenericSwapFacetV3.swapTokensSingleV3ERC20ToERC20 -FNDA:7,GenericSwapFacetV3.swapTokensSingleV3ERC20ToERC20 -DA:40,7 -DA:40,7 -DA:42,6 -DA:42,6 -DA:43,6 -DA:43,6 -DA:46,6 -DA:46,6 -DA:46,6 -DA:51,6 -DA:51,6 -BRDA:51,0,0,1 -BRDA:51,0,1,5 -DA:52,1 -DA:52,1 -DA:55,5 -DA:55,5 -DA:58,5 -DA:58,5 -DA:59,5 -DA:59,5 -DA:69,5 -DA:69,5 -FN:88,GenericSwapFacetV3.swapTokensSingleV3ERC20ToNative -FNDA:6,GenericSwapFacetV3.swapTokensSingleV3ERC20ToNative -DA:96,6 -DA:96,6 -DA:99,4 -DA:99,4 -DA:102,4 -DA:102,4 -BRDA:102,1,0,1 -BRDA:102,1,1,3 -DA:103,1 -DA:103,1 -DA:107,3 -DA:107,3 -DA:107,3 -DA:108,3 -DA:108,3 -BRDA:108,2,0,1 -BRDA:108,2,1,2 -DA:108,1 -DA:111,2 -DA:111,2 -DA:112,2 -DA:112,2 -DA:113,2 -DA:113,2 -DA:123,2 -DA:123,2 -FN:142,GenericSwapFacetV3.swapTokensSingleV3NativeToERC20 -FNDA:7,GenericSwapFacetV3.swapTokensSingleV3NativeToERC20 -DA:150,7 -DA:150,7 -DA:153,7 -DA:153,7 -BRDA:152,3,0,1 -BRDA:152,3,1,6 -DA:155,1 -DA:155,1 -DA:159,6 -DA:159,6 -DA:159,6 -DA:162,6 -DA:162,6 -BRDA:162,4,0,1 -BRDA:162,4,1,5 -DA:163,1 -DA:163,1 -DA:166,5 -DA:166,5 -DA:169,4 -DA:169,4 -DA:170,4 -DA:170,4 -DA:170,4 -DA:175,4 -DA:175,4 -BRDA:175,5,0,1 -BRDA:175,5,1,3 -DA:176,1 -DA:176,1 -DA:179,3 -DA:179,3 -DA:182,3 -DA:182,3 -DA:183,3 -DA:183,3 -DA:193,3 -DA:193,3 -FN:214,GenericSwapFacetV3.swapTokensMultipleV3ERC20ToNative -FNDA:5,GenericSwapFacetV3.swapTokensMultipleV3ERC20ToNative -DA:222,5 -DA:222,5 -DA:223,5 -DA:223,5 -DA:224,5 -DA:224,5 -FN:241,GenericSwapFacetV3.swapTokensMultipleV3ERC20ToERC20 -FNDA:10,GenericSwapFacetV3.swapTokensMultipleV3ERC20ToERC20 -DA:249,10 -DA:249,10 -DA:250,10 -DA:250,10 -DA:251,6 -DA:251,6 -FN:268,GenericSwapFacetV3.swapTokensMultipleV3NativeToERC20 -FNDA:5,GenericSwapFacetV3.swapTokensMultipleV3NativeToERC20 -DA:276,5 -DA:276,5 -DA:277,4 -DA:277,4 -FN:288,GenericSwapFacetV3._depositMultipleERC20Tokens -FNDA:15,GenericSwapFacetV3._depositMultipleERC20Tokens -DA:292,15 -DA:292,15 -DA:293,15 -DA:293,15 -DA:296,15 -DA:296,15 -DA:296,45 -DA:297,30 -DA:297,30 -DA:298,30 -BRDA:298,6,0,15 -BRDA:298,6,1,30 -DA:301,15 -DA:301,15 -DA:308,30 -DA:308,30 -FN:313,GenericSwapFacetV3._depositAndSwapERC20Single -FNDA:13,GenericSwapFacetV3._depositAndSwapERC20Single -DA:317,13 -DA:317,13 -DA:317,13 -DA:318,13 -DA:318,13 -DA:320,13 -DA:320,13 -DA:323,13 -DA:323,13 -DA:324,13 -DA:324,13 -DA:325,13 -DA:325,13 -DA:327,13 -DA:327,13 -BRDA:326,7,0,1 -BRDA:326,7,1,12 -DA:329,1 -DA:329,1 -DA:332,12 -DA:332,12 -DA:332,12 -DA:332,1 -BRDA:332,8,0,1 -BRDA:332,8,1,11 -DA:333,1 -DA:333,1 -DA:336,11 -DA:336,11 -DA:336,11 -DA:342,11 -DA:342,11 -BRDA:342,9,0,1 -BRDA:342,9,1,8 -DA:344,8 -DA:344,8 -BRDA:344,10,0,1 -BRDA:344,10,1,8 -DA:344,1 -DA:346,8 -DA:346,8 -DA:351,11 -DA:351,11 -DA:351,11 -DA:352,11 -DA:352,11 -BRDA:352,11,0,1 -BRDA:352,11,1,10 -DA:353,1 -DA:353,1 -DA:356,10 -DA:356,10 -FN:363,GenericSwapFacetV3._executeSwaps -FNDA:20,GenericSwapFacetV3._executeSwaps -DA:369,20 -DA:369,20 -DA:370,20 -DA:370,20 -DA:371,20 -DA:371,20 -DA:372,20 -DA:372,20 -DA:373,20 -DA:373,20 -DA:374,20 -DA:374,20 -DA:375,20 -DA:375,20 -DA:376,20 -DA:376,20 -DA:379,20 -DA:379,20 -DA:379,52 -DA:380,37 -DA:380,37 -DA:381,37 -DA:381,37 -DA:382,37 -DA:382,37 -DA:383,37 -DA:383,37 -DA:387,37 -DA:387,37 -DA:387,37 -DA:388,35 -DA:388,35 -BRDA:386,12,0,2 -BRDA:386,12,1,35 -DA:392,2 -DA:392,2 -DA:397,35 -DA:397,35 -DA:397,35 -DA:398,1 -DA:398,1 -BRDA:396,13,0,1 -BRDA:396,13,1,34 -DA:400,1 -DA:400,1 -DA:403,34 -DA:403,34 -BRDA:403,14,0,4 -BRDA:403,14,1,6 -DA:406,7 -DA:406,7 -DA:409,7 -DA:409,7 -BRDA:409,15,0,1 -BRDA:409,15,1,6 -DA:410,1 -DA:410,1 -DA:415,6 -DA:415,6 -BRDA:415,16,0,4 -BRDA:415,16,1,6 -DA:416,4 -DA:416,4 -DA:420,27 -DA:420,27 -DA:424,27 -DA:424,27 -BRDA:424,17,0,25 -BRDA:424,17,1,27 -DA:425,25 -DA:425,25 -DA:426,25 -DA:426,25 -DA:433,27 -DA:433,27 -DA:436,27 -DA:436,27 -BRDA:436,18,0,1 -BRDA:436,18,1,26 -DA:437,1 -DA:437,1 -DA:442,26 -DA:442,26 -BRDA:442,19,0,19 -BRDA:442,19,1,26 -DA:443,19 -DA:443,19 -DA:450,32 -DA:450,32 -DA:463,32 -DA:463,32 -FN:468,GenericSwapFacetV3._transferERC20TokensAndEmitEvent -FNDA:10,GenericSwapFacetV3._transferERC20TokensAndEmitEvent -DA:477,10 -DA:477,10 -DA:479,10 -DA:479,10 -DA:479,10 -DA:482,10 -DA:482,10 -BRDA:482,20,0,1 -BRDA:482,20,1,9 -DA:483,1 -DA:483,1 -DA:486,9 -DA:486,9 -DA:489,9 -DA:489,9 -FN:501,GenericSwapFacetV3._transferNativeTokensAndEmitEvent -FNDA:5,GenericSwapFacetV3._transferNativeTokensAndEmitEvent -DA:509,5 -DA:509,5 -DA:510,5 -DA:510,5 -DA:513,5 -DA:513,5 -BRDA:513,21,0,1 -BRDA:513,21,1,4 -DA:514,1 -DA:514,1 -DA:518,4 -DA:518,4 -DA:518,4 -DA:519,4 -DA:519,4 -BRDA:519,22,0,1 -BRDA:519,22,1,3 -DA:520,1 -DA:520,1 -DA:521,1 -DA:521,1 -DA:525,3 -DA:525,3 -FN:538,GenericSwapFacetV3._returnPositiveSlippageERC20 -FNDA:29,GenericSwapFacetV3._returnPositiveSlippageERC20 -DA:543,29 -DA:543,29 -DA:543,29 -DA:543,29 -BRDA:543,23,0,5 -BRDA:543,23,1,29 -DA:544,29 -DA:544,29 -DA:544,29 -DA:548,29 -DA:548,29 -BRDA:548,24,0,5 -BRDA:548,24,1,29 -DA:549,5 -DA:549,5 -FN:555,GenericSwapFacetV3._returnPositiveSlippageNative -FNDA:9,GenericSwapFacetV3._returnPositiveSlippageNative -DA:557,9 -DA:557,9 -DA:559,9 -DA:559,9 -BRDA:559,25,0,1 -BRDA:559,25,1,1 -DA:561,2 -DA:561,2 -DA:561,2 -DA:562,2 -DA:562,2 -BRDA:562,26,0,1 -BRDA:562,26,1,1 -DA:562,1 -FNF:13 -FNH:13 -LF:127 -LH:127 -BRF:54 -BRH:54 -end_of_record -TN: -SF:src/Facets/GnosisBridgeFacet.sol -FN:32,GnosisBridgeFacet. -FNDA:0,GnosisBridgeFacet. -DA:33,0 -DA:33,0 -FN:40,GnosisBridgeFacet.startBridgeTokensViaXDaiBridge -FNDA:265,GnosisBridgeFacet.startBridgeTokensViaXDaiBridge -DA:51,260 -DA:51,260 -DA:52,259 -DA:52,259 -FN:58,GnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge -FNDA:5,GnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge -DA:72,2 -DA:72,2 -DA:79,1 -DA:79,1 -FN:86,GnosisBridgeFacet._startBridge -FNDA:260,GnosisBridgeFacet._startBridge -DA:87,260 -DA:87,260 -DA:92,260 -DA:92,260 -DA:93,259 -DA:93,259 -FNF:4 -FNH:3 -LF:8 -LH:7 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/GnosisBridgeL2Facet.sol -FN:37,GnosisBridgeL2Facet. -FNDA:0,GnosisBridgeL2Facet. -DA:38,0 -DA:38,0 -FN:45,GnosisBridgeL2Facet.startBridgeTokensViaXDaiBridge -FNDA:6,GnosisBridgeL2Facet.startBridgeTokensViaXDaiBridge -DA:58,1 -DA:58,1 -FN:64,GnosisBridgeL2Facet.swapAndStartBridgeTokensViaXDaiBridge -FNDA:5,GnosisBridgeL2Facet.swapAndStartBridgeTokensViaXDaiBridge -DA:78,2 -DA:78,2 -DA:85,1 -DA:85,1 -FN:92,GnosisBridgeL2Facet._startBridge -FNDA:2,GnosisBridgeL2Facet._startBridge -DA:93,2 -DA:93,2 -DA:96,2 -DA:96,2 -FNF:4 -FNH:3 -LF:6 -LH:5 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/HopFacet.sol -FN:54,HopFacet.initHop -FNDA:27,HopFacet.initHop -DA:55,27 -DA:55,27 -DA:57,27 -DA:57,27 -DA:57,27 -DA:59,27 -DA:59,27 -DA:59,102 -DA:59,75 -DA:60,75 -DA:60,75 -DA:60,75 -BRDA:60,0,0,- -BRDA:60,0,1,75 -DA:61,0 -DA:61,0 -DA:63,75 -DA:63,75 -DA:66,27 -DA:66,27 -FN:74,HopFacet.registerBridge -FNDA:3,HopFacet.registerBridge -DA:75,3 -DA:75,3 -DA:77,2 -DA:77,2 -DA:77,2 -DA:79,2 -DA:79,2 -DA:79,2 -BRDA:79,1,0,1 -BRDA:79,1,1,1 -DA:80,1 -DA:80,1 -DA:83,1 -DA:83,1 -DA:85,1 -DA:85,1 -FN:91,HopFacet.startBridgeTokensViaHop -FNDA:270,HopFacet.startBridgeTokensViaHop -DA:103,264 -DA:103,264 -DA:107,262 -DA:107,262 -FN:114,HopFacet.swapAndStartBridgeTokensViaHop -FNDA:8,HopFacet.swapAndStartBridgeTokensViaHop -DA:127,4 -DA:127,4 -DA:134,3 -DA:134,3 -FN:142,HopFacet._startBridge -FNDA:265,HopFacet._startBridge -DA:146,265 -DA:146,265 -DA:147,265 -DA:147,265 -DA:147,265 -DA:148,265 -DA:148,265 -DA:151,265 -DA:151,265 -DA:157,265 -DA:157,265 -DA:157,265 -DA:161,265 -DA:161,265 -DA:161,265 -DA:161,1 -BRDA:161,2,0,- -BRDA:161,2,1,264 -DA:163,264 -DA:163,264 -DA:175,1 -DA:175,1 -DA:186,265 -DA:186,265 -FN:190,HopFacet.getStorage -FNDA:294,HopFacet.getStorage -DA:191,294 -DA:191,294 -DA:194,294 -DA:194,294 -FNF:6 -FNH:6 -LF:28 -LH:27 -BRF:6 -BRH:4 -end_of_record -TN: -SF:src/Facets/HopFacetOptimized.sol -FN:34,HopFacetOptimized.setApprovalForBridges -FNDA:72,HopFacetOptimized.setApprovalForBridges -DA:38,72 -DA:38,72 -DA:39,72 -DA:39,72 -DA:39,304 -DA:39,232 -DA:41,232 -DA:41,232 -FN:52,HopFacetOptimized.startBridgeTokensViaHopL1ERC20 -FNDA:261,HopFacetOptimized.startBridgeTokensViaHopL1ERC20 -DA:57,261 -DA:57,261 -DA:64,260 -DA:64,260 -DA:73,258 -DA:73,258 -FN:79,HopFacetOptimized.startBridgeTokensViaHopL1Native -FNDA:1,HopFacetOptimized.startBridgeTokensViaHopL1Native -DA:84,1 -DA:84,1 -DA:95,1 -DA:95,1 -FN:102,HopFacetOptimized.swapAndStartBridgeTokensViaHopL1ERC20 -FNDA:4,HopFacetOptimized.swapAndStartBridgeTokensViaHopL1ERC20 -DA:108,4 -DA:108,4 -DA:117,3 -DA:117,3 -DA:126,2 -DA:126,2 -FN:133,HopFacetOptimized.swapAndStartBridgeTokensViaHopL1Native -FNDA:1,HopFacetOptimized.swapAndStartBridgeTokensViaHopL1Native -DA:139,1 -DA:139,1 -DA:148,1 -DA:148,1 -DA:160,1 -DA:160,1 -FN:166,HopFacetOptimized.startBridgeTokensViaHopL2ERC20 -FNDA:261,HopFacetOptimized.startBridgeTokensViaHopL2ERC20 -DA:171,261 -DA:171,261 -DA:178,260 -DA:178,260 -DA:188,258 -DA:188,258 -FN:194,HopFacetOptimized.startBridgeTokensViaHopL2Native -FNDA:1,HopFacetOptimized.startBridgeTokensViaHopL2Native -DA:199,1 -DA:199,1 -DA:209,1 -DA:209,1 -FN:216,HopFacetOptimized.swapAndStartBridgeTokensViaHopL2ERC20 -FNDA:5,HopFacetOptimized.swapAndStartBridgeTokensViaHopL2ERC20 -DA:222,5 -DA:222,5 -DA:229,4 -DA:229,4 -DA:239,2 -DA:239,2 -FN:246,HopFacetOptimized.swapAndStartBridgeTokensViaHopL2Native -FNDA:1,HopFacetOptimized.swapAndStartBridgeTokensViaHopL2Native -DA:252,1 -DA:252,1 -DA:259,1 -DA:259,1 -DA:269,1 -DA:269,1 -FNF:9 -FNH:9 -LF:25 -LH:25 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/HopFacetPacked.sol -FN:39,HopFacetPacked. -FNDA:0,HopFacetPacked. -DA:43,0 -DA:43,0 -DA:43,0 -DA:43,0 -DA:45,0 -DA:45,0 -DA:45,0 -BRDA:45,0,0,- -BRDA:45,0,1,- -DA:46,0 -DA:46,0 -DA:49,0 -DA:49,0 -DA:52,0 -DA:52,0 -DA:55,0 -DA:55,0 -DA:58,0 -DA:58,0 -FN:69,HopFacetPacked.setApprovalForHopBridges -FNDA:32,HopFacetPacked.setApprovalForHopBridges -DA:73,32 -DA:73,32 -DA:75,32 -DA:75,32 -DA:75,192 -DA:75,160 -DA:77,160 -DA:77,160 -FN:87,HopFacetPacked.startBridgeTokensViaHopL2NativePacked -FNDA:3,HopFacetPacked.startBridgeTokensViaHopL2NativePacked -DA:96,3 -DA:96,3 -DA:96,3 -DA:97,3 -DA:97,3 -DA:97,3 -DA:98,3 -DA:98,3 -DA:98,3 -DA:101,3 -DA:101,3 -DA:104,3 -DA:104,3 -DA:104,3 -DA:114,3 -DA:114,3 -DA:123,3 -DA:123,3 -FN:137,HopFacetPacked.startBridgeTokensViaHopL2NativeMin -FNDA:2,HopFacetPacked.startBridgeTokensViaHopL2NativeMin -DA:148,2 -DA:148,2 -DA:159,2 -DA:159,2 -FN:168,HopFacetPacked.encode_startBridgeTokensViaHopL2NativePacked -FNDA:16,HopFacetPacked.encode_startBridgeTokensViaHopL2NativePacked -DA:175,16 -DA:175,16 -BRDA:175,1,0,- -BRDA:175,1,1,16 -DA:179,16 -DA:179,16 -BRDA:179,2,0,- -BRDA:179,2,1,16 -DA:183,16 -DA:183,16 -BRDA:183,3,0,- -BRDA:183,3,1,16 -DA:188,16 -DA:188,16 -DA:189,16 -DA:189,16 -FN:201,HopFacetPacked.decode_startBridgeTokensViaHopL2NativePacked -FNDA:1,HopFacetPacked.decode_startBridgeTokensViaHopL2NativePacked -DA:208,1 -DA:208,1 -BRDA:208,4,0,- -BRDA:208,4,1,1 -DA:213,1 -DA:213,1 -DA:214,1 -DA:214,1 -DA:216,1 -DA:216,1 -DA:217,1 -DA:217,1 -DA:218,1 -DA:218,1 -DA:219,1 -DA:219,1 -DA:220,1 -DA:220,1 -DA:222,1 -DA:222,1 -FN:227,HopFacetPacked.startBridgeTokensViaHopL2ERC20Packed -FNDA:4,HopFacetPacked.startBridgeTokensViaHopL2ERC20Packed -DA:241,4 -DA:241,4 -DA:241,4 -DA:242,4 -DA:242,4 -DA:242,4 -DA:243,4 -DA:243,4 -DA:243,4 -DA:244,4 -DA:244,4 -DA:244,4 -DA:246,4 -DA:246,4 -DA:246,4 -DA:251,4 -DA:251,4 -DA:258,4 -DA:258,4 -DA:258,4 -DA:268,4 -DA:268,4 -DA:277,4 -DA:277,4 -FN:291,HopFacetPacked.startBridgeTokensViaHopL2ERC20Min -FNDA:4,HopFacetPacked.startBridgeTokensViaHopL2ERC20Min -DA:304,4 -DA:304,4 -DA:311,4 -DA:311,4 -DA:322,4 -DA:322,4 -FN:336,HopFacetPacked.encode_startBridgeTokensViaHopL2ERC20Packed -FNDA:32,HopFacetPacked.encode_startBridgeTokensViaHopL2ERC20Packed -DA:348,32 -DA:348,32 -BRDA:348,5,0,- -BRDA:348,5,1,32 -DA:352,32 -DA:352,32 -BRDA:352,6,0,- -BRDA:352,6,1,32 -DA:356,32 -DA:356,32 -BRDA:356,7,0,- -BRDA:356,7,1,32 -DA:360,32 -DA:360,32 -BRDA:360,8,0,- -BRDA:360,8,1,32 -DA:364,32 -DA:364,32 -BRDA:364,9,0,- -BRDA:364,9,1,32 -DA:368,32 -DA:368,32 -BRDA:368,10,0,- -BRDA:368,10,1,32 -DA:373,32 -DA:373,32 -DA:374,32 -DA:374,32 -FN:391,HopFacetPacked.decode_startBridgeTokensViaHopL2ERC20Packed -FNDA:2,HopFacetPacked.decode_startBridgeTokensViaHopL2ERC20Packed -DA:398,2 -DA:398,2 -BRDA:398,11,0,- -BRDA:398,11,1,2 -DA:403,2 -DA:403,2 -DA:404,2 -DA:404,2 -DA:406,2 -DA:406,2 -DA:407,2 -DA:407,2 -DA:408,2 -DA:408,2 -DA:409,2 -DA:409,2 -DA:410,2 -DA:410,2 -DA:411,2 -DA:411,2 -DA:412,2 -DA:412,2 -DA:413,2 -DA:413,2 -DA:416,2 -DA:416,2 -DA:417,2 -DA:417,2 -DA:419,2 -DA:419,2 -FN:424,HopFacetPacked.startBridgeTokensViaHopL1NativePacked -FNDA:3,HopFacetPacked.startBridgeTokensViaHopL1NativePacked -DA:436,3 -DA:436,3 -DA:448,3 -DA:448,3 -FN:459,HopFacetPacked.startBridgeTokensViaHopL1NativeMin -FNDA:2,HopFacetPacked.startBridgeTokensViaHopL1NativeMin -DA:469,2 -DA:469,2 -DA:479,2 -DA:479,2 -FN:490,HopFacetPacked.encode_startBridgeTokensViaHopL1NativePacked -FNDA:16,HopFacetPacked.encode_startBridgeTokensViaHopL1NativePacked -DA:499,16 -DA:499,16 -BRDA:499,12,0,- -BRDA:499,12,1,16 -DA:503,16 -DA:503,16 -BRDA:503,13,0,- -BRDA:503,13,1,16 -DA:507,16 -DA:507,16 -BRDA:507,14,0,- -BRDA:507,14,1,16 -DA:512,16 -DA:512,16 -DA:513,16 -DA:513,16 -FN:527,HopFacetPacked.decode_startBridgeTokensViaHopL1NativePacked -FNDA:1,HopFacetPacked.decode_startBridgeTokensViaHopL1NativePacked -DA:534,1 -DA:534,1 -BRDA:534,15,0,- -BRDA:534,15,1,1 -DA:539,1 -DA:539,1 -DA:540,1 -DA:540,1 -DA:542,1 -DA:542,1 -DA:543,1 -DA:543,1 -DA:544,1 -DA:544,1 -DA:545,1 -DA:545,1 -DA:550,1 -DA:550,1 -DA:552,1 -DA:552,1 -FN:557,HopFacetPacked.startBridgeTokensViaHopL1ERC20Packed -FNDA:4,HopFacetPacked.startBridgeTokensViaHopL1ERC20Packed -DA:570,4 -DA:570,4 -DA:570,4 -DA:573,4 -DA:573,4 -DA:580,4 -DA:580,4 -DA:590,4 -DA:590,4 -FN:603,HopFacetPacked.startBridgeTokensViaHopL1ERC20Min -FNDA:4,HopFacetPacked.startBridgeTokensViaHopL1ERC20Min -DA:615,4 -DA:615,4 -DA:622,4 -DA:622,4 -DA:632,4 -DA:632,4 -FN:645,HopFacetPacked.encode_startBridgeTokensViaHopL1ERC20Packed -FNDA:32,HopFacetPacked.encode_startBridgeTokensViaHopL1ERC20Packed -DA:656,32 -DA:656,32 -BRDA:656,16,0,- -BRDA:656,16,1,32 -DA:660,32 -DA:660,32 -BRDA:660,17,0,- -BRDA:660,17,1,32 -DA:664,32 -DA:664,32 -BRDA:664,18,0,- -BRDA:664,18,1,32 -DA:668,32 -DA:668,32 -BRDA:668,19,0,- -BRDA:668,19,1,32 -DA:673,32 -DA:673,32 -DA:674,32 -DA:674,32 -FN:690,HopFacetPacked.decode_startBridgeTokensViaHopL1ERC20Packed -FNDA:2,HopFacetPacked.decode_startBridgeTokensViaHopL1ERC20Packed -DA:697,2 -DA:697,2 -BRDA:697,20,0,- -BRDA:697,20,1,2 -DA:702,2 -DA:702,2 -DA:703,2 -DA:703,2 -DA:705,2 -DA:705,2 -DA:706,2 -DA:706,2 -DA:707,2 -DA:707,2 -DA:708,2 -DA:708,2 -DA:709,2 -DA:709,2 -DA:710,2 -DA:710,2 -DA:715,2 -DA:715,2 -DA:717,2 -DA:717,2 -FNF:18 -FNH:17 -LF:109 -LH:102 -BRF:42 -BRH:20 -end_of_record -TN: -SF:src/Facets/HyphenFacet.sol -FN:25,HyphenFacet. -FNDA:0,HyphenFacet. -DA:26,0 -DA:26,0 -FN:33,HyphenFacet.startBridgeTokensViaHyphen -FNDA:265,HyphenFacet.startBridgeTokensViaHyphen -DA:44,260 -DA:44,260 -DA:48,259 -DA:48,259 -FN:54,HyphenFacet.swapAndStartBridgeTokensViaHyphen -FNDA:6,HyphenFacet.swapAndStartBridgeTokensViaHyphen -DA:66,3 -DA:66,3 -DA:72,2 -DA:72,2 -FN:79,HyphenFacet._startBridge -FNDA:261,HyphenFacet._startBridge -DA:80,261 -DA:80,261 -BRDA:80,0,0,- -BRDA:80,0,1,259 -DA:82,259 -DA:82,259 -DA:88,259 -DA:88,259 -DA:96,2 -DA:96,2 -DA:103,261 -DA:103,261 -FNF:4 -FNH:3 -LF:10 -LH:9 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/LIFuelFacet.sol -FN:32,LIFuelFacet.startBridgeTokensViaLIFuel -FNDA:264,LIFuelFacet.startBridgeTokensViaLIFuel -DA:43,260 -DA:43,260 -DA:47,259 -DA:47,259 -FN:53,LIFuelFacet.swapAndStartBridgeTokensViaLIFuel -FNDA:6,LIFuelFacet.swapAndStartBridgeTokensViaLIFuel -DA:65,3 -DA:65,3 -DA:72,2 -DA:72,2 -FN:79,LIFuelFacet._startBridge -FNDA:261,LIFuelFacet._startBridge -DA:80,261 -DA:80,261 -DA:80,261 -DA:84,261 -DA:84,261 -BRDA:84,0,0,- -BRDA:84,0,1,2 -DA:85,2 -DA:85,2 -DA:93,259 -DA:93,259 -DA:99,259 -DA:99,259 -DA:107,261 -DA:107,261 -FN:111,LIFuelFacet.getStorage -FNDA:261,LIFuelFacet.getStorage -DA:112,261 -DA:112,261 -DA:115,261 -DA:115,261 -FNF:4 -FNH:4 -LF:12 -LH:12 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/MakerTeleportFacet.sol -FN:43,MakerTeleportFacet. -FNDA:0,MakerTeleportFacet. -DA:49,0 -DA:49,0 -DA:50,0 -DA:50,0 -DA:51,0 -DA:51,0 -DA:52,0 -DA:52,0 -FN:59,MakerTeleportFacet.startBridgeTokensViaMakerTeleport -FNDA:264,MakerTeleportFacet.startBridgeTokensViaMakerTeleport -DA:70,259 -DA:70,259 -DA:71,258 -DA:71,258 -FN:77,MakerTeleportFacet.swapAndStartBridgeTokensViaMakerTeleport -FNDA:5,MakerTeleportFacet.swapAndStartBridgeTokensViaMakerTeleport -DA:91,2 -DA:91,2 -DA:98,1 -DA:98,1 -FN:105,MakerTeleportFacet._startBridge -FNDA:259,MakerTeleportFacet._startBridge -DA:106,259 -DA:106,259 -DA:112,259 -DA:112,259 -DA:118,259 -DA:118,259 -FNF:4 -FNH:3 -LF:11 -LH:7 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/MayanFacet.sol -FN:53,MayanFacet. -FNDA:0,MayanFacet. -DA:54,0 -DA:54,0 -FN:62,MayanFacet.startBridgeTokensViaMayan -FNDA:266,MayanFacet.startBridgeTokensViaMayan -DA:74,261 -DA:74,261 -DA:78,260 -DA:78,260 -FN:85,MayanFacet.swapAndStartBridgeTokensViaMayan -FNDA:7,MayanFacet.swapAndStartBridgeTokensViaMayan -DA:98,4 -DA:98,4 -DA:104,3 -DA:104,3 -FN:112,MayanFacet._startBridge -FNDA:263,MayanFacet._startBridge -DA:117,263 -DA:117,263 -BRDA:117,0,0,1 -BRDA:117,0,1,- -DA:118,1 -DA:118,1 -DA:118,1 -BRDA:118,1,0,- -BRDA:118,1,1,1 -DA:119,0 -DA:119,0 -DA:124,1 -DA:124,1 -DA:124,1 -DA:125,1 -DA:125,1 -BRDA:125,2,0,1 -BRDA:125,2,1,- -DA:126,1 -DA:126,1 -DA:132,262 -DA:132,262 -DA:132,262 -DA:135,262 -DA:135,262 -BRDA:135,3,0,- -BRDA:135,3,1,262 -DA:136,0 -DA:136,0 -DA:140,262 -DA:140,262 -DA:142,262 -DA:142,262 -BRDA:142,4,0,- -BRDA:142,4,1,260 -DA:143,260 -DA:143,260 -DA:149,260 -DA:149,260 -DA:157,2 -DA:157,2 -DA:163,262 -DA:163,262 -BRDA:163,5,0,- -BRDA:163,5,1,262 -DA:164,0 -DA:164,0 -DA:171,262 -DA:171,262 -FN:177,MayanFacet._parseReceiver -FNDA:263,MayanFacet._parseReceiver -DA:180,263 -DA:180,263 -DA:183,263 -DA:183,263 -FNF:5 -FNH:4 -LF:24 -LH:20 -BRF:12 -BRH:6 -end_of_record -TN: -SF:src/Facets/MultichainFacet.sol -FN:60,MultichainFacet.initMultichain -FNDA:26,MultichainFacet.initMultichain -DA:64,26 -DA:64,26 -DA:66,26 -DA:66,26 -DA:66,26 -DA:68,26 -DA:68,26 -DA:70,26 -DA:70,26 -DA:71,26 -DA:71,26 -DA:71,102 -DA:72,76 -DA:72,76 -DA:72,76 -BRDA:72,0,0,- -BRDA:72,0,1,76 -DA:73,0 -DA:73,0 -DA:75,76 -DA:75,76 -DA:77,76 -DA:77,76 -DA:81,26 -DA:81,26 -FN:88,MultichainFacet.updateAddressMappings -FNDA:25,MultichainFacet.updateAddressMappings -DA:89,25 -DA:89,25 -DA:91,24 -DA:91,24 -DA:91,24 -DA:93,24 -DA:93,24 -DA:93,72 -DA:94,48 -DA:94,48 -DA:97,48 -DA:97,48 -DA:101,24 -DA:101,24 -FN:107,MultichainFacet.registerRouters -FNDA:2,MultichainFacet.registerRouters -DA:111,2 -DA:111,2 -DA:113,1 -DA:113,1 -DA:113,1 -DA:115,1 -DA:115,1 -DA:116,1 -DA:116,1 -DA:116,4 -DA:117,3 -DA:117,3 -DA:117,3 -BRDA:117,1,0,- -BRDA:117,1,1,3 -DA:118,0 -DA:118,0 -DA:120,3 -DA:120,3 -DA:123,3 -DA:123,3 -DA:126,1 -DA:126,1 -FN:132,MultichainFacet.startBridgeTokensViaMultichain -FNDA:268,MultichainFacet.startBridgeTokensViaMultichain -DA:144,263 -DA:144,263 -DA:144,263 -DA:145,263 -DA:145,263 -BRDA:145,2,0,2 -BRDA:145,2,1,261 -DA:146,2 -DA:146,2 -DA:148,261 -DA:148,261 -BRDA:148,3,0,260 -BRDA:148,3,1,260 -DA:149,260 -DA:149,260 -DA:154,260 -DA:154,260 -FN:161,MultichainFacet.swapAndStartBridgeTokensViaMultichain -FNDA:6,MultichainFacet.swapAndStartBridgeTokensViaMultichain -DA:174,3 -DA:174,3 -DA:174,3 -DA:176,3 -DA:176,3 -BRDA:176,4,0,- -BRDA:176,4,1,3 -DA:177,0 -DA:177,0 -DA:180,3 -DA:180,3 -DA:186,2 -DA:186,2 -FN:194,MultichainFacet._startBridge -FNDA:262,MultichainFacet._startBridge -DA:199,262 -DA:199,262 -BRDA:199,5,0,- -BRDA:199,5,1,1 -DA:200,1 -DA:200,1 -DA:205,261 -DA:205,261 -DA:205,261 -DA:206,261 -DA:206,261 -BRDA:206,6,0,- -BRDA:206,6,1,2 -DA:208,2 -DA:208,2 -DA:217,259 -DA:217,259 -DA:223,259 -DA:223,259 -DA:228,259 -DA:228,259 -DA:239,262 -DA:239,262 -FN:243,MultichainFacet.getStorage -FNDA:578,MultichainFacet.getStorage -DA:244,578 -DA:244,578 -DA:247,578 -DA:247,578 -FNF:7 -FNH:7 -LF:47 -LH:44 -BRF:14 -BRH:9 -end_of_record -TN: -SF:src/Facets/NonStandardSelectorsRegistryFacet.sol -FN:23,NonStandardSelectorsRegistryFacet.setNonStandardSelector -FNDA:2,NonStandardSelectorsRegistryFacet.setNonStandardSelector -DA:27,2 -DA:27,2 -DA:28,1 -DA:28,1 -DA:28,1 -DA:29,1 -DA:29,1 -FN:35,NonStandardSelectorsRegistryFacet.batchSetNonStandardSelectors -FNDA:2,NonStandardSelectorsRegistryFacet.batchSetNonStandardSelectors -DA:39,2 -DA:39,2 -DA:40,1 -DA:40,1 -DA:40,1 -DA:41,1 -DA:41,1 -BRDA:41,0,0,- -BRDA:41,0,1,1 -DA:45,1 -DA:45,1 -DA:45,3 -DA:45,2 -DA:46,2 -DA:46,2 -FN:53,NonStandardSelectorsRegistryFacet.isNonStandardSelector -FNDA:3,NonStandardSelectorsRegistryFacet.isNonStandardSelector -DA:56,3 -DA:56,3 -FN:62,NonStandardSelectorsRegistryFacet.getStorage -FNDA:5,NonStandardSelectorsRegistryFacet.getStorage -DA:63,5 -DA:63,5 -DA:65,5 -DA:65,5 -FNF:4 -FNH:4 -LF:11 -LH:11 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/OmniBridgeFacet.sol -FN:29,OmniBridgeFacet. -FNDA:0,OmniBridgeFacet. -DA:30,0 -DA:30,0 -DA:31,0 -DA:31,0 -FN:38,OmniBridgeFacet.startBridgeTokensViaOmniBridge -FNDA:529,OmniBridgeFacet.startBridgeTokensViaOmniBridge -DA:49,519 -DA:49,519 -DA:53,517 -DA:53,517 -FN:59,OmniBridgeFacet.swapAndStartBridgeTokensViaOmniBridge -FNDA:11,OmniBridgeFacet.swapAndStartBridgeTokensViaOmniBridge -DA:71,5 -DA:71,5 -DA:77,3 -DA:77,3 -FN:84,OmniBridgeFacet._startBridge -FNDA:520,OmniBridgeFacet._startBridge -DA:85,520 -DA:85,520 -BRDA:85,0,0,- -BRDA:85,0,1,2 -DA:86,2 -DA:86,2 -DA:90,518 -DA:90,518 -DA:95,518 -DA:95,518 -DA:102,520 -DA:102,520 -FNF:4 -FNH:3 -LF:11 -LH:9 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/OptimismBridgeFacet.sol -FN:57,OptimismBridgeFacet.initOptimism -FNDA:7,OptimismBridgeFacet.initOptimism -DA:61,7 -DA:61,7 -DA:63,7 -DA:63,7 -DA:63,7 -DA:65,7 -BRDA:65,0,0,- -BRDA:65,0,1,7 -DA:66,0 -DA:66,0 -DA:69,7 -DA:69,7 -DA:69,14 -DA:69,7 -DA:70,7 -DA:70,7 -DA:70,7 -BRDA:70,1,0,- -BRDA:70,1,1,7 -DA:71,0 -DA:71,0 -DA:73,7 -DA:73,7 -DA:78,7 -DA:78,7 -DA:79,7 -DA:79,7 -DA:81,7 -DA:81,7 -FN:89,OptimismBridgeFacet.registerOptimismBridge -FNDA:0,OptimismBridgeFacet.registerOptimismBridge -DA:90,0 -DA:90,0 -DA:92,0 -DA:92,0 -DA:92,0 -DA:94,0 -DA:94,0 -BRDA:94,2,0,- -BRDA:94,2,1,- -DA:94,0 -DA:96,0 -DA:96,0 -DA:96,0 -BRDA:96,3,0,- -BRDA:96,3,1,- -DA:97,0 -DA:97,0 -DA:100,0 -DA:100,0 -DA:102,0 -DA:102,0 -FN:108,OptimismBridgeFacet.startBridgeTokensViaOptimismBridge -FNDA:6,OptimismBridgeFacet.startBridgeTokensViaOptimismBridge -DA:120,3 -DA:120,3 -DA:124,1 -DA:124,1 -FN:131,OptimismBridgeFacet.swapAndStartBridgeTokensViaOptimismBridge -FNDA:1,OptimismBridgeFacet.swapAndStartBridgeTokensViaOptimismBridge -DA:144,1 -DA:144,1 -DA:150,1 -DA:150,1 -FN:158,OptimismBridgeFacet._startBridge -FNDA:2,OptimismBridgeFacet._startBridge -DA:162,2 -DA:162,2 -DA:162,2 -DA:163,2 -DA:163,2 -DA:166,2 -DA:166,2 -DA:166,2 -DA:172,2 -DA:172,2 -BRDA:172,4,0,- -BRDA:172,4,1,- -DA:173,0 -DA:173,0 -DA:179,2 -DA:179,2 -DA:185,2 -BRDA:185,5,0,- -BRDA:185,5,1,- -DA:186,0 -DA:186,0 -DA:188,2 -DA:188,2 -DA:199,2 -DA:199,2 -FN:203,OptimismBridgeFacet.getStorage -FNDA:9,OptimismBridgeFacet.getStorage -DA:204,9 -DA:204,9 -DA:207,9 -DA:207,9 -FNF:6 -FNH:5 -LF:34 -LH:23 -BRF:12 -BRH:2 -end_of_record -TN: -SF:src/Facets/OwnershipFacet.sol -FN:43,OwnershipFacet.transferOwnership -FNDA:5,OwnershipFacet.transferOwnership -DA:44,5 -DA:44,5 -DA:45,4 -DA:45,4 -DA:45,4 -DA:47,4 -DA:47,4 -BRDA:47,0,0,1 -BRDA:47,0,1,3 -DA:47,1 -DA:49,3 -DA:49,3 -DA:49,3 -BRDA:49,1,0,1 -BRDA:49,1,1,2 -DA:50,1 -DA:50,1 -DA:52,2 -DA:52,2 -DA:53,2 -DA:53,2 -FN:57,OwnershipFacet.cancelOwnershipTransfer -FNDA:0,OwnershipFacet.cancelOwnershipTransfer -DA:58,0 -DA:58,0 -DA:59,0 -DA:59,0 -DA:59,0 -DA:61,0 -DA:61,0 -BRDA:61,2,0,- -BRDA:61,2,1,- -DA:62,0 -DA:62,0 -DA:63,0 -DA:63,0 -FN:67,OwnershipFacet.confirmOwnershipTransfer -FNDA:2,OwnershipFacet.confirmOwnershipTransfer -DA:68,2 -DA:68,2 -DA:68,2 -DA:69,2 -DA:69,2 -DA:70,2 -DA:70,2 -BRDA:70,3,0,1 -BRDA:70,3,1,1 -DA:70,1 -DA:71,1 -DA:71,1 -DA:72,1 -DA:72,1 -DA:73,1 -DA:73,1 -FN:78,OwnershipFacet.owner -FNDA:3,OwnershipFacet.owner -DA:79,3 -DA:79,3 -FN:85,OwnershipFacet.getStorage -FNDA:6,OwnershipFacet.getStorage -DA:86,6 -DA:86,6 -DA:89,6 -DA:89,6 -FNF:5 -FNH:4 -LF:21 -LH:16 -BRF:8 -BRH:6 -end_of_record -TN: -SF:src/Facets/PeripheryRegistryFacet.sol -FN:31,PeripheryRegistryFacet.registerPeripheryContract -FNDA:22,PeripheryRegistryFacet.registerPeripheryContract -DA:35,22 -DA:35,22 -DA:36,22 -DA:36,22 -DA:36,22 -DA:37,22 -DA:37,22 -DA:38,22 -DA:38,22 -FN:43,PeripheryRegistryFacet.getPeripheryContract -FNDA:2,PeripheryRegistryFacet.getPeripheryContract -DA:46,2 -DA:46,2 -FN:50,PeripheryRegistryFacet.getStorage -FNDA:24,PeripheryRegistryFacet.getStorage -DA:51,24 -DA:51,24 -DA:54,24 -DA:54,24 -FNF:3 -FNH:3 -LF:7 -LH:7 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Facets/PolygonBridgeFacet.sol -FN:29,PolygonBridgeFacet. -FNDA:0,PolygonBridgeFacet. -DA:30,0 -DA:30,0 -DA:31,0 -DA:31,0 -FN:38,PolygonBridgeFacet.startBridgeTokensViaPolygonBridge -FNDA:265,PolygonBridgeFacet.startBridgeTokensViaPolygonBridge -DA:49,260 -DA:49,260 -DA:53,259 -DA:53,259 -FN:59,PolygonBridgeFacet.swapAndStartBridgeTokensViaPolygonBridge -FNDA:6,PolygonBridgeFacet.swapAndStartBridgeTokensViaPolygonBridge -DA:71,3 -DA:71,3 -DA:77,2 -DA:77,2 -FN:84,PolygonBridgeFacet._startBridge -FNDA:261,PolygonBridgeFacet._startBridge -DA:85,261 -DA:85,261 -DA:87,261 -DA:87,261 -BRDA:87,0,0,- -BRDA:87,0,1,2 -DA:88,2 -DA:88,2 -DA:92,259 -DA:92,259 -DA:96,259 -DA:96,259 -DA:102,259 -DA:102,259 -DA:102,259 -DA:103,259 -DA:103,259 -DA:110,261 -DA:110,261 -FNF:4 -FNH:3 -LF:14 -LH:12 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Facets/SquidFacet.sol -FN:67,SquidFacet. -FNDA:0,SquidFacet. -DA:68,0 -DA:68,0 -FN:76,SquidFacet.startBridgeTokensViaSquid -FNDA:7,SquidFacet.startBridgeTokensViaSquid -DA:87,3 -DA:87,3 -DA:92,3 -DA:92,3 -FN:99,SquidFacet.swapAndStartBridgeTokensViaSquid -FNDA:5,SquidFacet.swapAndStartBridgeTokensViaSquid -DA:112,2 -DA:112,2 -DA:120,1 -DA:120,1 -FN:128,SquidFacet._startBridge -FNDA:4,SquidFacet._startBridge -DA:132,4 -DA:132,4 -DA:132,4 -DA:139,4 -DA:139,4 -BRDA:139,0,0,2 -BRDA:139,0,1,4 -DA:140,2 -DA:140,2 -DA:148,4 -DA:148,4 -BRDA:148,1,0,1 -BRDA:148,1,1,3 -DA:149,1 -DA:149,1 -DA:150,3 -DA:150,3 -BRDA:150,2,0,- -BRDA:150,2,1,3 -DA:151,0 -DA:151,0 -DA:152,3 -DA:152,3 -BRDA:152,3,0,3 -BRDA:152,3,1,- -DA:153,3 -DA:153,3 -DA:155,0 -DA:155,0 -DA:158,4 -DA:158,4 -FN:161,SquidFacet._bridgeCall -FNDA:1,SquidFacet._bridgeCall -DA:162,1 -DA:162,1 -FN:173,SquidFacet._callBridge -FNDA:0,SquidFacet._callBridge -DA:174,0 -DA:174,0 -FN:186,SquidFacet._callBridgeCall -FNDA:3,SquidFacet._callBridgeCall -DA:187,3 -DA:187,3 -FN:202,SquidFacet._calculateMsgValue -FNDA:4,SquidFacet._calculateMsgValue -DA:206,4 -DA:206,4 -DA:207,4 -DA:207,4 -BRDA:207,4,0,2 -BRDA:207,4,1,4 -DA:208,2 -DA:208,2 -DA:210,4 -DA:210,4 -FNF:8 -FNH:6 -LF:23 -LH:19 -BRF:10 -BRH:8 -end_of_record -TN: -SF:src/Facets/StandardizedCallFacet.sol -FN:15,StandardizedCallFacet.standardizedCall -FNDA:2,StandardizedCallFacet.standardizedCall -DA:16,2 -DA:16,2 -FN:21,StandardizedCallFacet.standardizedSwapCall -FNDA:2,StandardizedCallFacet.standardizedSwapCall -DA:22,2 -DA:22,2 -FN:27,StandardizedCallFacet.standardizedBridgeCall -FNDA:2,StandardizedCallFacet.standardizedBridgeCall -DA:28,2 -DA:28,2 -FN:33,StandardizedCallFacet.standardizedSwapAndBridgeCall -FNDA:2,StandardizedCallFacet.standardizedSwapAndBridgeCall -DA:36,2 -DA:36,2 -FN:39,StandardizedCallFacet.execute -FNDA:8,StandardizedCallFacet.execute -DA:42,8 -DA:42,8 -DA:42,8 -DA:43,8 -DA:43,8 -DA:47,8 -DA:47,8 -DA:47,8 -BRDA:47,0,0,4 -BRDA:47,0,1,4 -DA:48,4 -DA:48,4 -FNF:5 -FNH:5 -LF:8 -LH:8 -BRF:2 -BRH:2 -end_of_record -TN: -SF:src/Facets/StargateFacet.sol -FN:80,StargateFacet. -FNDA:0,StargateFacet. -DA:81,0 -DA:81,0 -FN:88,StargateFacet.initStargate -FNDA:23,StargateFacet.initStargate -DA:89,23 -DA:89,23 -DA:91,22 -DA:91,22 -DA:91,22 -DA:93,22 -DA:93,22 -DA:93,88 -DA:93,66 -DA:94,66 -DA:94,66 -DA:98,22 -DA:98,22 -DA:100,22 -DA:100,22 -FN:108,StargateFacet.startBridgeTokensViaStargate -FNDA:269,StargateFacet.startBridgeTokensViaStargate -DA:119,265 -DA:119,265 -DA:120,264 -DA:120,264 -DA:124,263 -DA:124,263 -FN:131,StargateFacet.swapAndStartBridgeTokensViaStargate -FNDA:6,StargateFacet.swapAndStartBridgeTokensViaStargate -DA:143,3 -DA:143,3 -DA:144,3 -DA:144,3 -DA:152,2 -DA:152,2 -FN:155,StargateFacet.quoteLayerZeroFee -FNDA:46,StargateFacet.quoteLayerZeroFee -DA:159,46 -DA:159,46 -DA:160,46 -DA:160,46 -FN:178,StargateFacet._startBridge -FNDA:265,StargateFacet._startBridge -DA:182,265 -DA:182,265 -BRDA:182,0,0,- -BRDA:182,0,1,3 -DA:183,3 -DA:183,3 -DA:201,262 -DA:201,262 -DA:207,262 -DA:207,262 -DA:224,263 -DA:224,263 -DA:226,263 -DA:226,263 -FN:229,StargateFacet.validateDestinationCallFlag -FNDA:268,StargateFacet.validateDestinationCallFlag -DA:234,268 -DA:234,268 -BRDA:233,1,0,1 -BRDA:233,1,1,267 -DA:237,1 -DA:237,1 -FN:247,StargateFacet.setLayerZeroChainId -FNDA:2,StargateFacet.setLayerZeroChainId -DA:251,2 -DA:251,2 -DA:252,1 -DA:252,1 -DA:252,1 -DA:254,1 -DA:254,1 -BRDA:254,2,0,- -BRDA:254,2,1,1 -DA:255,0 -DA:255,0 -DA:258,1 -DA:258,1 -DA:259,1 -DA:259,1 -FN:265,StargateFacet.getLayerZeroChainId -FNDA:311,StargateFacet.getLayerZeroChainId -DA:268,311 -DA:268,311 -DA:268,311 -DA:269,311 -DA:269,311 -DA:270,311 -DA:270,311 -BRDA:270,3,0,- -BRDA:270,3,1,311 -DA:270,0 -DA:271,311 -DA:271,311 -FN:274,StargateFacet.toBytes -FNDA:311,StargateFacet.toBytes -DA:275,311 -DA:275,311 -DA:275,311 -FN:279,StargateFacet.getStorage -FNDA:334,StargateFacet.getStorage -DA:280,334 -DA:280,334 -DA:283,334 -DA:283,334 -FNF:11 -FNH:10 -LF:36 -LH:34 -BRF:8 -BRH:5 -end_of_record -TN: -SF:src/Facets/SymbiosisFacet.sol -FN:49,SymbiosisFacet. -FNDA:0,SymbiosisFacet. -DA:53,0 -DA:53,0 -DA:54,0 -DA:54,0 -FN:62,SymbiosisFacet.startBridgeTokensViaSymbiosis -FNDA:265,SymbiosisFacet.startBridgeTokensViaSymbiosis -DA:74,260 -DA:74,260 -DA:79,259 -DA:79,259 -FN:88,SymbiosisFacet.swapAndStartBridgeTokensViaSymbiosis -FNDA:6,SymbiosisFacet.swapAndStartBridgeTokensViaSymbiosis -DA:100,3 -DA:100,3 -DA:107,2 -DA:107,2 -FN:113,SymbiosisFacet._startBridge -FNDA:261,SymbiosisFacet._startBridge -DA:117,261 -DA:117,261 -DA:117,261 -DA:118,261 -DA:118,261 -DA:120,261 -BRDA:120,0,0,2 -BRDA:120,0,1,259 -DA:121,2 -DA:121,2 -DA:123,259 -DA:123,259 -DA:130,261 -DA:130,261 -DA:144,261 -DA:144,261 -FNF:4 -FNH:3 -LF:13 -LH:11 -BRF:2 -BRH:2 -end_of_record -TN: -SF:src/Facets/SynapseBridgeFacet.sol -FN:37,SynapseBridgeFacet. -FNDA:0,SynapseBridgeFacet. -DA:38,0 -DA:38,0 -FN:46,SynapseBridgeFacet.startBridgeTokensViaSynapseBridge -FNDA:265,SynapseBridgeFacet.startBridgeTokensViaSynapseBridge -DA:58,260 -DA:58,260 -DA:63,259 -DA:63,259 -FN:70,SynapseBridgeFacet.swapAndStartBridgeTokensViaSynapseBridge -FNDA:6,SynapseBridgeFacet.swapAndStartBridgeTokensViaSynapseBridge -DA:83,3 -DA:83,3 -DA:90,2 -DA:90,2 -FN:98,SynapseBridgeFacet._startBridge -FNDA:261,SynapseBridgeFacet._startBridge -DA:102,261 -DA:102,261 -DA:103,261 -DA:103,261 -DA:105,261 -DA:105,261 -BRDA:105,0,0,2 -BRDA:105,0,1,259 -DA:106,2 -DA:106,2 -DA:107,2 -DA:107,2 -DA:109,259 -DA:109,259 -DA:116,261 -DA:116,261 -DA:125,261 -DA:125,261 -FNF:4 -FNH:3 -LF:13 -LH:12 -BRF:2 -BRH:2 -end_of_record -TN: -SF:src/Facets/ThorSwapFacet.sol -FN:31,ThorSwapFacet. -FNDA:0,ThorSwapFacet. -DA:32,0 -DA:32,0 -FN:38,ThorSwapFacet.startBridgeTokensViaThorSwap -FNDA:265,ThorSwapFacet.startBridgeTokensViaThorSwap -DA:50,260 -DA:50,260 -DA:54,259 -DA:54,259 -FN:61,ThorSwapFacet.swapAndStartBridgeTokensViaThorSwap -FNDA:6,ThorSwapFacet.swapAndStartBridgeTokensViaThorSwap -DA:74,3 -DA:74,3 -DA:80,2 -DA:80,2 -FN:86,ThorSwapFacet._startBridge -FNDA:261,ThorSwapFacet._startBridge -DA:92,261 -DA:92,261 -BRDA:92,0,0,- -BRDA:92,0,1,261 -DA:92,0 -DA:94,261 -DA:94,261 -DA:94,261 -DA:95,261 -DA:95,261 -DA:95,261 -DA:97,261 -DA:97,261 -BRDA:97,1,0,259 -BRDA:97,1,1,261 -DA:98,259 -DA:98,259 -DA:104,261 -DA:104,261 -DA:114,261 -DA:114,261 -FNF:4 -FNH:3 -LF:12 -LH:11 -BRF:4 -BRH:3 -end_of_record -TN: -SF:src/Facets/WithdrawFacet.sol -FN:35,WithdrawFacet.executeCallAndWithdraw -FNDA:5,WithdrawFacet.executeCallAndWithdraw -DA:42,5 -DA:42,5 -DA:42,5 -BRDA:42,0,0,2 -BRDA:42,0,1,3 -DA:43,2 -DA:43,2 -DA:47,3 -DA:47,3 -DA:48,3 -DA:48,3 -DA:48,3 -DA:49,3 -DA:49,3 -BRDA:49,1,0,- -BRDA:49,1,1,3 -DA:49,0 -DA:52,3 -DA:52,3 -DA:54,3 -BRDA:54,2,0,2 -BRDA:54,2,1,1 -DA:55,2 -DA:55,2 -DA:57,1 -DA:57,1 -FN:65,WithdrawFacet.withdraw -FNDA:0,WithdrawFacet.withdraw -DA:70,0 -DA:70,0 -DA:70,0 -BRDA:70,3,0,- -BRDA:70,3,1,- -DA:71,0 -DA:71,0 -DA:73,0 -DA:73,0 -FN:82,WithdrawFacet._withdrawAsset -FNDA:2,WithdrawFacet._withdrawAsset -DA:87,2 -DA:87,2 -DA:87,2 -DA:88,2 -DA:88,2 -DA:89,2 -DA:89,2 -FNF:3 -FNH:2 -LF:15 -LH:12 -BRF:8 -BRH:5 -end_of_record -TN: -SF:src/Helpers/CelerIMFacetBase.sol -FN:69,CelerIMFacetBase. -FNDA:0,CelerIMFacetBase. -DA:76,0 -DA:76,0 -DA:83,0 -DA:83,0 -DA:84,0 -DA:84,0 -FN:92,CelerIMFacetBase.startBridgeTokensViaCelerIM -FNDA:272,CelerIMFacetBase.startBridgeTokensViaCelerIM -DA:103,268 -DA:103,268 -DA:104,267 -DA:104,267 -BRDA:104,0,0,- -BRDA:104,0,1,262 -DA:106,263 -DA:106,263 -DA:106,263 -DA:109,263 -DA:109,263 -DA:109,263 -DA:110,263 -DA:110,263 -DA:118,262 -DA:118,262 -DA:118,262 -DA:118,262 -BRDA:117,1,0,- -BRDA:117,1,1,262 -DA:121,0 -DA:121,0 -DA:125,266 -DA:125,266 -FN:132,CelerIMFacetBase.swapAndStartBridgeTokensViaCelerIM -FNDA:9,CelerIMFacetBase.swapAndStartBridgeTokensViaCelerIM -DA:144,5 -DA:144,5 -DA:146,5 -DA:146,5 -DA:154,4 -DA:154,4 -BRDA:154,2,0,- -BRDA:154,2,1,2 -DA:156,2 -DA:156,2 -DA:156,2 -DA:159,2 -DA:159,2 -DA:159,2 -DA:160,2 -DA:160,2 -DA:167,2 -DA:167,2 -DA:167,2 -DA:167,2 -BRDA:166,3,0,- -BRDA:166,3,1,2 -DA:170,0 -DA:170,0 -DA:174,4 -DA:174,4 -FN:182,CelerIMFacetBase._startBridge -FNDA:270,CelerIMFacetBase._startBridge -DA:188,270 -DA:188,270 -DA:188,270 -DA:193,270 -DA:193,270 -BRDA:193,4,0,1 -BRDA:193,4,1,267 -DA:195,268 -DA:195,268 -DA:203,2 -DA:203,2 -DA:206,2 -DA:206,2 -DA:209,2 -DA:209,2 -DA:209,2 -DA:216,2 -DA:216,2 -DA:227,2 -DA:227,2 -DA:231,269 -DA:231,269 -FN:237,CelerIMFacetBase._getRightAsset -FNDA:265,CelerIMFacetBase._getRightAsset -DA:240,265 -DA:240,265 -BRDA:240,5,0,- -BRDA:240,5,1,- -DA:242,0 -DA:242,0 -DA:245,265 -DA:245,265 -FN:249,CelerIMFacetBase.validateDestinationCallFlag -FNDA:273,CelerIMFacetBase.validateDestinationCallFlag -DA:254,273 -DA:254,273 -BRDA:253,6,0,1 -BRDA:253,6,1,272 -DA:257,1 -DA:257,1 -FNF:6 -FNH:5 -LF:34 -LH:28 -BRF:14 -BRH:8 -end_of_record -TN: -SF:src/Helpers/ExcessivelySafeCall.sol -FN:28,ExcessivelySafeCall.excessivelySafeCall -FNDA:0,ExcessivelySafeCall.excessivelySafeCall -DA:36,0 -DA:36,0 -DA:37,0 -DA:37,0 -DA:38,0 -DA:38,0 -DA:38,0 -DA:44,0 -DA:44,0 -DA:54,0 -DA:54,0 -DA:55,0 -BRDA:55,0,0,- -DA:56,0 -DA:56,0 -DA:63,0 -DA:63,0 -FN:81,ExcessivelySafeCall.excessivelySafeStaticCall -FNDA:0,ExcessivelySafeCall.excessivelySafeStaticCall -DA:88,0 -DA:88,0 -DA:89,0 -DA:89,0 -DA:90,0 -DA:90,0 -DA:90,0 -DA:96,0 -DA:96,0 -DA:105,0 -DA:105,0 -DA:106,0 -BRDA:106,1,0,- -DA:107,0 -DA:107,0 -DA:114,0 -DA:114,0 -FN:126,ExcessivelySafeCall.swapSelector -FNDA:0,ExcessivelySafeCall.swapSelector -DA:130,0 -DA:130,0 -BRDA:130,2,0,- -BRDA:130,2,1,- -DA:131,0 -DA:131,0 -DA:133,0 -DA:133,0 -DA:139,0 -DA:139,0 -DA:140,0 -DA:140,0 -FNF:3 -FNH:0 -LF:21 -LH:0 -BRF:4 -BRH:0 -end_of_record -TN: -SF:src/Helpers/ReentrancyGuard.sol -FN:29,ReentrancyGuard.nonReentrant -FNDA:3314,ReentrancyGuard.nonReentrant -DA:30,3314 -DA:30,3314 -DA:30,3314 -DA:31,3314 -DA:31,3314 -BRDA:31,0,0,2 -BRDA:31,0,1,3043 -DA:31,2 -DA:32,3312 -DA:32,3312 -DA:34,3173 -DA:34,3173 -FN:40,ReentrancyGuard.reentrancyStorage -FNDA:6342,ReentrancyGuard.reentrancyStorage -DA:45,6342 -DA:45,6342 -DA:48,6342 -DA:48,6342 -FNF:2 -FNH:2 -LF:6 -LH:6 -BRF:2 -BRH:2 -end_of_record -TN: -SF:src/Helpers/SwapperV2.sol -FN:30,SwapperV2.noLeftovers -FNDA:66,SwapperV2.noLeftovers -DA:35,66 -DA:35,66 -DA:36,66 -DA:36,66 -BRDA:36,0,0,4 -BRDA:36,0,1,15 -DA:37,18 -DA:37,18 -DA:38,18 -DA:38,18 -DA:42,18 -DA:42,18 -DA:42,36 -DA:42,36 -DA:43,18 -DA:43,18 -DA:45,18 -DA:45,18 -BRDA:45,1,0,4 -BRDA:45,1,1,15 -DA:46,15 -DA:46,15 -DA:49,15 -DA:49,15 -BRDA:49,2,0,4 -BRDA:49,2,1,15 -DA:50,4 -DA:50,4 -DA:58,18 -DA:58,18 -FN:71,SwapperV2.noLeftoversReserve -FNDA:23,SwapperV2.noLeftoversReserve -DA:77,23 -DA:77,23 -DA:78,23 -DA:78,23 -BRDA:78,3,0,- -BRDA:78,3,1,- -DA:79,0 -DA:79,0 -DA:80,0 -DA:80,0 -DA:84,0 -DA:84,0 -DA:84,0 -DA:84,0 -DA:85,0 -DA:85,0 -DA:87,0 -DA:87,0 -BRDA:87,4,0,- -BRDA:87,4,1,- -DA:88,0 -DA:88,0 -DA:91,0 -DA:91,0 -DA:91,0 -DA:94,0 -DA:94,0 -BRDA:94,5,0,- -BRDA:94,5,1,- -DA:95,0 -DA:95,0 -DA:103,0 -DA:103,0 -FN:114,SwapperV2.refundExcessNative -FNDA:2784,SwapperV2.refundExcessNative -DA:115,2784 -DA:115,2784 -DA:115,2784 -DA:117,2654 -DA:117,2654 -DA:119,2654 -DA:119,2654 -BRDA:119,6,0,5 -BRDA:119,6,1,2385 -DA:120,5 -DA:120,5 -FN:136,SwapperV2._depositAndSwap -FNDA:85,SwapperV2._depositAndSwap -DA:142,85 -DA:142,85 -DA:144,85 -DA:144,85 -BRDA:144,7,0,19 -BRDA:144,7,1,66 -DA:145,19 -DA:145,19 -DA:148,66 -DA:148,66 -DA:149,66 -DA:149,66 -DA:149,66 -DA:151,66 -DA:151,66 -BRDA:151,8,0,19 -BRDA:151,8,1,66 -DA:152,19 -DA:152,19 -DA:155,66 -DA:155,66 -DA:155,66 -DA:157,66 -DA:157,66 -DA:158,66 -DA:158,66 -DA:165,66 -DA:165,66 -DA:165,66 -DA:165,66 -DA:168,66 -DA:168,66 -BRDA:168,9,0,- -BRDA:168,9,1,66 -DA:169,0 -DA:169,0 -DA:172,66 -DA:172,66 -FN:181,SwapperV2._depositAndSwap -FNDA:32,SwapperV2._depositAndSwap -DA:188,32 -DA:188,32 -DA:190,32 -DA:190,32 -BRDA:190,10,0,9 -BRDA:190,10,1,23 -DA:191,9 -DA:191,9 -DA:194,23 -DA:194,23 -DA:195,23 -DA:195,23 -DA:195,23 -DA:197,23 -DA:197,23 -BRDA:197,11,0,9 -BRDA:197,11,1,23 -DA:198,9 -DA:198,9 -DA:201,23 -DA:201,23 -DA:201,23 -DA:203,23 -DA:203,23 -DA:204,23 -DA:204,23 -DA:204,23 -DA:209,23 -DA:209,23 -DA:211,23 -DA:211,23 -DA:211,23 -DA:211,23 -DA:214,23 -DA:214,23 -BRDA:214,12,0,9 -BRDA:214,12,1,23 -DA:215,9 -DA:215,9 -DA:218,23 -DA:218,23 -BRDA:218,13,0,- -BRDA:218,13,1,23 -DA:219,0 -DA:219,0 -DA:222,23 -DA:222,23 -FN:232,SwapperV2._executeSwaps -FNDA:66,SwapperV2._executeSwaps -DA:238,18 -DA:238,18 -DA:239,18 -DA:239,18 -DA:239,54 -DA:240,36 -DA:240,36 -DA:243,36 -DA:243,36 -BRDA:242,14,0,- -BRDA:242,14,1,48 -DA:249,0 -DA:249,0 -DA:251,36 -DA:251,36 -DA:254,36 -DA:254,36 -FN:262,SwapperV2._executeSwaps -FNDA:23,SwapperV2._executeSwaps -DA:275,0 -DA:275,0 -DA:276,0 -DA:276,0 -DA:276,0 -DA:277,0 -DA:277,0 -DA:280,0 -DA:280,0 -BRDA:279,15,0,- -BRDA:279,15,1,23 -DA:286,0 -DA:286,0 -DA:288,0 -DA:288,0 -DA:291,0 -DA:291,0 -FN:299,SwapperV2._fetchBalances -FNDA:89,SwapperV2._fetchBalances -DA:302,89 -DA:302,89 -DA:303,89 -DA:303,89 -DA:303,89 -DA:304,89 -DA:304,89 -DA:305,89 -DA:305,89 -DA:305,196 -DA:306,107 -DA:306,107 -DA:307,107 -DA:307,107 -DA:309,107 -DA:309,107 -BRDA:309,16,0,- -BRDA:309,16,1,31 -DA:310,31 -DA:310,31 -DA:314,107 -DA:314,107 -DA:318,89 -DA:318,89 -FNF:8 -FNH:8 -LF:82 -LH:62 -BRF:34 -BRH:23 -end_of_record -TN: -SF:src/Helpers/TransferrableOwnership.sol -FN:24,TransferrableOwnership. -FNDA:84,TransferrableOwnership. -DA:25,84 -DA:25,84 -FN:28,TransferrableOwnership.onlyOwner -FNDA:8,TransferrableOwnership.onlyOwner -DA:29,8 -DA:29,8 -BRDA:29,0,0,4 -BRDA:29,0,1,16 -DA:29,3 -FN:35,TransferrableOwnership.transferOwnership -FNDA:20,TransferrableOwnership.transferOwnership -DA:36,16 -DA:36,16 -BRDA:36,1,0,4 -BRDA:36,1,1,12 -DA:36,4 -DA:37,12 -DA:37,12 -BRDA:37,2,0,4 -BRDA:37,2,1,8 -DA:37,4 -DA:38,8 -DA:38,8 -DA:39,8 -DA:39,8 -FN:43,TransferrableOwnership.cancelOwnershipTransfer -FNDA:0,TransferrableOwnership.cancelOwnershipTransfer -DA:44,0 -DA:44,0 -BRDA:44,3,0,- -BRDA:44,3,1,- -DA:45,0 -DA:45,0 -DA:46,0 -DA:46,0 -FN:50,TransferrableOwnership.confirmOwnershipTransfer -FNDA:8,TransferrableOwnership.confirmOwnershipTransfer -DA:51,8 -DA:51,8 -DA:52,8 -DA:52,8 -BRDA:52,4,0,4 -BRDA:52,4,1,4 -DA:52,4 -DA:53,4 -DA:53,4 -DA:54,4 -DA:54,4 -DA:55,4 -DA:55,4 -FNF:5 -FNH:4 -LF:14 -LH:11 -BRF:10 -BRH:8 -end_of_record -TN: -SF:src/Helpers/Validatable.sol -FN:11,Validatable.validateBridgeData -FNDA:3267,Validatable.validateBridgeData -DA:12,3267 -DA:12,3267 -BRDA:12,0,0,27 -BRDA:12,0,1,2980 -DA:13,26 -DA:13,26 -DA:15,3241 -DA:15,3241 -BRDA:15,1,0,27 -BRDA:15,1,1,2953 -DA:16,26 -DA:16,26 -DA:18,3215 -DA:18,3215 -BRDA:18,2,0,26 -BRDA:18,2,1,2927 -DA:19,26 -DA:19,26 -FN:24,Validatable.noNativeAsset -FNDA:3,Validatable.noNativeAsset -DA:25,3 -DA:25,3 -BRDA:25,3,0,- -BRDA:25,3,1,261 -DA:26,0 -DA:26,0 -FN:31,Validatable.onlyAllowSourceToken -FNDA:524,Validatable.onlyAllowSourceToken -DA:35,524 -DA:35,524 -BRDA:35,4,0,- -BRDA:35,4,1,525 -DA:36,0 -DA:36,0 -FN:41,Validatable.onlyAllowDestinationChain -FNDA:263,Validatable.onlyAllowDestinationChain -DA:45,263 -DA:45,263 -BRDA:45,5,0,- -BRDA:45,5,1,263 -DA:46,0 -DA:46,0 -FN:51,Validatable.containsSourceSwaps -FNDA:159,Validatable.containsSourceSwaps -DA:52,159 -DA:52,159 -BRDA:52,6,0,- -BRDA:52,6,1,159 -DA:53,0 -DA:53,0 -FN:58,Validatable.doesNotContainSourceSwaps -FNDA:6115,Validatable.doesNotContainSourceSwaps -DA:59,6115 -BRDA:59,7,0,27 -BRDA:59,7,1,6088 -DA:60,27 -DA:60,27 -FN:65,Validatable.doesNotContainDestinationCalls -FNDA:2697,Validatable.doesNotContainDestinationCalls -DA:68,2697 -BRDA:68,8,0,10 -BRDA:68,8,1,2693 -DA:69,11 -DA:69,11 -FNF:7 -FNH:7 -LF:18 -LH:14 -BRF:18 -BRH:14 -end_of_record -TN: -SF:src/LiFiDiamond.sol -FN:13,LiFiDiamond. -FNDA:0,LiFiDiamond. -DA:14,0 -DA:14,0 -DA:17,0 -DA:17,0 -DA:17,0 -DA:18,0 -DA:18,0 -DA:18,0 -DA:19,0 -DA:19,0 -DA:20,0 -DA:20,0 -DA:25,0 -DA:25,0 -FN:31,LiFiDiamond. -FNDA:12110,LiFiDiamond. -DA:32,12110 -DA:32,12110 -DA:33,12110 -DA:33,12110 -DA:38,12110 -DA:38,12110 -DA:42,12110 -DA:42,12110 -DA:44,12110 -DA:44,12110 -DA:44,12110 -BRDA:44,0,0,- -BRDA:44,0,1,12110 -DA:45,0 -DA:45,0 -FNF:2 -FNH:1 -LF:12 -LH:5 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/LiFiDiamondImmutable.sol -FN:13,LiFiDiamondImmutable. -FNDA:0,LiFiDiamondImmutable. -DA:14,0 -DA:14,0 -DA:17,0 -DA:17,0 -DA:17,0 -DA:18,0 -DA:18,0 -DA:18,0 -DA:19,0 -DA:19,0 -DA:20,0 -DA:20,0 -DA:25,0 -DA:25,0 -FN:31,LiFiDiamondImmutable. -FNDA:0,LiFiDiamondImmutable. -DA:32,0 -DA:32,0 -DA:33,0 -DA:33,0 -DA:38,0 -DA:38,0 -DA:42,0 -DA:42,0 -DA:44,0 -DA:44,0 -DA:44,0 -BRDA:44,0,0,- -BRDA:44,0,1,- -DA:45,0 -DA:45,0 -FNF:2 -FNH:0 -LF:12 -LH:0 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/Libraries/LibAccess.sol -FN:24,LibAccess.accessStorage -FNDA:8,LibAccess.accessStorage -DA:29,8 -DA:29,8 -DA:32,8 -DA:32,8 -FN:39,LibAccess.addAccess -FNDA:2,LibAccess.addAccess -DA:40,2 -DA:40,2 -DA:40,2 -BRDA:40,0,0,- -BRDA:40,0,1,2 -DA:41,0 -DA:41,0 -DA:43,2 -DA:43,2 -DA:43,2 -DA:44,2 -DA:44,2 -DA:45,2 -DA:45,2 -FN:51,LibAccess.removeAccess -FNDA:1,LibAccess.removeAccess -DA:52,1 -DA:52,1 -DA:52,1 -DA:53,1 -DA:53,1 -DA:54,1 -DA:54,1 -FN:59,LibAccess.enforceAccessControl -FNDA:5,LibAccess.enforceAccessControl -DA:60,5 -DA:60,5 -DA:60,5 -DA:61,5 -DA:61,5 -BRDA:61,1,0,4 -BRDA:61,1,1,1 -DA:62,4 -DA:62,4 -FNF:4 -FNH:4 -LF:13 -LH:12 -BRF:4 -BRH:3 -end_of_record -TN: -SF:src/Libraries/LibAllowList.sol -FN:22,LibAllowList.addAllowedContract -FNDA:776,LibAllowList.addAllowedContract -DA:23,776 -DA:23,776 -DA:25,772 -DA:25,772 -DA:25,772 -DA:27,772 -BRDA:27,0,0,114 -BRDA:27,0,1,658 -DA:27,114 -DA:29,658 -DA:29,658 -DA:30,658 -DA:30,658 -FN:35,LibAllowList.contractIsAllowed -FNDA:266,LibAllowList.contractIsAllowed -DA:38,266 -DA:38,266 -FN:43,LibAllowList.removeAllowedContract -FNDA:6,LibAllowList.removeAllowedContract -DA:44,6 -DA:44,6 -DA:44,6 -DA:46,6 -DA:46,6 -BRDA:46,1,0,- -BRDA:46,1,1,6 -DA:47,0 -DA:47,0 -DA:50,6 -DA:50,6 -DA:52,6 -DA:52,6 -DA:54,6 -DA:54,6 -DA:54,7 -DA:54,1 -DA:55,7 -DA:55,7 -BRDA:55,2,0,- -BRDA:55,2,1,6 -DA:57,6 -DA:57,6 -DA:59,6 -DA:59,6 -DA:60,6 -DA:60,6 -FN:66,LibAllowList.getAllowedContracts -FNDA:4,LibAllowList.getAllowedContracts -DA:67,4 -DA:67,4 -FN:72,LibAllowList.addAllowedSelector -FNDA:2317,LibAllowList.addAllowedSelector -DA:73,2317 -DA:73,2317 -FN:78,LibAllowList.removeAllowedSelector -FNDA:0,LibAllowList.removeAllowedSelector -DA:79,0 -DA:79,0 -FN:84,LibAllowList.selectorIsAllowed -FNDA:166,LibAllowList.selectorIsAllowed -DA:85,166 -DA:85,166 -FN:89,LibAllowList._getStorage -FNDA:3531,LibAllowList._getStorage -DA:94,3531 -DA:94,3531 -DA:97,3531 -DA:97,3531 -FN:103,LibAllowList._checkAddress -FNDA:776,LibAllowList._checkAddress -DA:104,776 -DA:104,776 -DA:104,776 -BRDA:104,3,0,2 -BRDA:104,3,1,774 -DA:104,2 -DA:106,774 -DA:106,774 -BRDA:106,4,0,2 -BRDA:106,4,1,772 -DA:106,2 -FNF:9 -FNH:8 -LF:24 -LH:22 -BRF:10 -BRH:8 -end_of_record -TN: -SF:src/Libraries/LibAsset.sol -FN:25,LibAsset.getOwnBalance -FNDA:770,LibAsset.getOwnBalance -DA:26,770 -DA:26,770 -DA:27,770 -DA:27,770 -FN:36,LibAsset.transferNativeAsset -FNDA:22,LibAsset.transferNativeAsset -DA:40,22 -DA:40,22 -BRDA:40,0,0,- -BRDA:40,0,1,22 -DA:40,0 -DA:41,22 -DA:41,22 -BRDA:41,1,0,- -BRDA:41,1,1,22 -DA:42,0 -DA:42,0 -DA:44,22 -DA:44,22 -DA:44,22 -DA:45,22 -DA:45,22 -BRDA:45,2,0,4 -BRDA:45,2,1,18 -DA:45,4 -FN:53,LibAsset.maxApproveERC20 -FNDA:6633,LibAsset.maxApproveERC20 -DA:58,6633 -DA:58,6633 -BRDA:58,3,0,6630 -BRDA:58,3,1,6633 -DA:59,6633 -DA:59,6633 -DA:61,6630 -DA:61,6630 -BRDA:61,4,0,- -BRDA:61,4,1,6630 -DA:62,0 -DA:62,0 -DA:65,6630 -DA:65,6630 -DA:65,6630 -BRDA:65,5,0,6625 -BRDA:65,5,1,6630 -DA:66,6625 -DA:66,6625 -DA:67,6625 -DA:67,6625 -FN:76,LibAsset.transferERC20 -FNDA:46,LibAsset.transferERC20 -DA:81,46 -DA:81,46 -BRDA:81,6,0,- -BRDA:81,6,1,46 -DA:82,0 -DA:82,0 -DA:84,46 -DA:84,46 -BRDA:84,7,0,- -BRDA:84,7,1,46 -DA:85,0 -DA:85,0 -DA:88,46 -DA:88,46 -DA:88,46 -DA:89,46 -DA:89,46 -BRDA:89,8,0,- -BRDA:89,8,1,46 -DA:90,0 -DA:90,0 -DA:92,46 -DA:92,46 -FN:100,LibAsset.transferFromERC20 -FNDA:6577,LibAsset.transferFromERC20 -DA:106,6577 -DA:106,6577 -BRDA:106,9,0,- -BRDA:106,9,1,6577 -DA:107,0 -DA:107,0 -DA:109,6577 -DA:109,6577 -BRDA:109,10,0,- -BRDA:109,10,1,6577 -DA:110,0 -DA:110,0 -DA:113,6577 -DA:113,6577 -DA:113,6577 -DA:114,6577 -DA:114,6577 -DA:114,6577 -DA:115,6577 -DA:115,6577 -DA:116,6574 -DA:116,6574 -DA:116,6574 -DA:116,6574 -BRDA:116,11,0,- -BRDA:116,11,1,6574 -DA:117,0 -DA:117,0 -FN:121,LibAsset.depositAsset -FNDA:6116,LibAsset.depositAsset -DA:122,6116 -DA:122,6116 -BRDA:122,12,0,- -BRDA:122,12,1,6116 -DA:122,0 -DA:123,6116 -DA:123,6116 -BRDA:123,13,0,3 -BRDA:123,13,1,35 -DA:124,38 -DA:124,38 -BRDA:124,14,0,3 -BRDA:124,14,1,35 -DA:124,3 -DA:126,6078 -DA:126,6078 -DA:126,6078 -DA:127,6078 -DA:127,6078 -BRDA:127,15,0,25 -BRDA:127,15,1,6053 -DA:127,25 -DA:128,6053 -DA:128,6053 -FN:132,LibAsset.depositAssets -FNDA:89,LibAsset.depositAssets -DA:133,89 -DA:133,89 -DA:133,196 -DA:134,107 -DA:134,107 -DA:135,107 -BRDA:135,16,0,90 -BRDA:135,16,1,107 -DA:136,90 -DA:136,90 -DA:139,107 -DA:139,107 -FN:147,LibAsset.isNativeAsset -FNDA:26593,LibAsset.isNativeAsset -DA:148,26593 -DA:148,26593 -DA:148,26593 -FN:158,LibAsset.transferAsset -FNDA:68,LibAsset.transferAsset -DA:163,68 -DA:163,68 -FN:169,LibAsset.isContract -FNDA:398,LibAsset.isContract -DA:170,398 -DA:170,398 -DA:173,398 -DA:173,398 -DA:175,398 -DA:175,398 -DA:175,398 -FNF:10 -FNH:10 -LF:47 -LH:39 -BRF:34 -BRH:24 -end_of_record -TN: -SF:src/Libraries/LibBytes.sol -FN:16,LibBytes.slice -FNDA:35,LibBytes.slice -DA:21,35 -DA:21,35 -DA:21,35 -BRDA:21,0,0,- -BRDA:21,0,1,35 -DA:21,0 -DA:22,35 -DA:22,35 -DA:22,35 -BRDA:22,1,0,- -BRDA:22,1,1,35 -DA:22,0 -DA:24,35 -DA:24,35 -DA:87,35 -DA:87,35 -FN:90,LibBytes.toAddress -FNDA:0,LibBytes.toAddress -DA:94,0 -DA:94,0 -DA:94,0 -BRDA:94,2,0,- -BRDA:94,2,1,- -DA:95,0 -DA:95,0 -DA:97,0 -DA:97,0 -DA:100,0 -DA:100,0 -DA:106,0 -DA:106,0 -FN:111,LibBytes.toHexString -FNDA:12,LibBytes.toHexString -DA:115,12 -DA:115,12 -DA:115,12 -DA:116,12 -DA:116,12 -DA:117,12 -DA:117,12 -DA:118,12 -DA:118,12 -DA:118,12 -DA:118,12 -DA:118,492 -DA:118,480 -DA:119,480 -DA:119,480 -DA:120,480 -DA:120,480 -DA:122,12 -DA:122,12 -BRDA:122,3,0,- -BRDA:122,3,1,12 -DA:123,12 -DA:123,12 -DA:123,12 -FNF:3 -FNH:2 -LF:17 -LH:12 -BRF:8 -BRH:3 -end_of_record -TN: -SF:src/Libraries/LibDiamond.sol -FN:53,LibDiamond.diamondStorage -FNDA:4712,LibDiamond.diamondStorage -DA:58,4712 -DA:58,4712 -DA:61,4712 -DA:61,4712 -FN:70,LibDiamond.setContractOwner -FNDA:1,LibDiamond.setContractOwner -DA:71,1 -DA:71,1 -DA:71,1 -DA:72,1 -DA:72,1 -DA:73,1 -DA:73,1 -DA:74,1 -DA:74,1 -FN:77,LibDiamond.contractOwner -FNDA:24,LibDiamond.contractOwner -DA:78,24 -DA:78,24 -FN:81,LibDiamond.enforceIsContractOwner -FNDA:1729,LibDiamond.enforceIsContractOwner -DA:82,1729 -DA:82,1729 -BRDA:82,0,0,8 -BRDA:82,0,1,1721 -DA:83,8 -DA:83,8 -FN:93,LibDiamond.diamondCut -FNDA:1508,LibDiamond.diamondCut -DA:98,1508 -DA:98,1508 -DA:98,4458 -DA:99,2950 -DA:99,2950 -DA:100,2950 -DA:100,2950 -BRDA:100,1,0,- -BRDA:100,1,1,2950 -DA:101,2950 -DA:101,2950 -DA:105,0 -DA:105,0 -BRDA:105,2,0,- -BRDA:105,2,1,- -DA:106,0 -DA:106,0 -DA:110,0 -DA:110,0 -BRDA:110,3,0,- -BRDA:110,3,1,- -DA:111,0 -DA:111,0 -DA:116,0 -DA:116,0 -DA:119,2950 -DA:119,2950 -DA:122,1508 -DA:122,1508 -DA:123,1508 -DA:123,1508 -FN:126,LibDiamond.addFunctions -FNDA:2950,LibDiamond.addFunctions -DA:130,2950 -DA:130,2950 -BRDA:130,4,0,- -BRDA:130,4,1,2950 -DA:131,0 -DA:131,0 -DA:133,2950 -DA:133,2950 -DA:133,2950 -DA:134,2950 -DA:134,2950 -BRDA:134,5,0,- -BRDA:134,5,1,2950 -DA:135,0 -DA:135,0 -DA:137,2950 -DA:137,2950 -DA:137,2950 -DA:141,2950 -DA:141,2950 -BRDA:141,6,0,2950 -BRDA:141,6,1,2950 -DA:142,2950 -DA:142,2950 -DA:145,2950 -DA:145,2950 -DA:146,14815 -DA:146,14815 -DA:149,11865 -DA:149,11865 -DA:150,11865 -DA:150,11865 -DA:153,11865 -DA:153,11865 -BRDA:153,7,0,- -BRDA:153,7,1,11865 -DA:154,0 -DA:154,0 -DA:156,11865 -DA:156,11865 -DA:158,11865 -DA:158,11865 -DA:159,11865 -DA:159,11865 -FN:164,LibDiamond.replaceFunctions -FNDA:0,LibDiamond.replaceFunctions -DA:168,0 -DA:168,0 -BRDA:168,8,0,- -BRDA:168,8,1,- -DA:169,0 -DA:169,0 -DA:171,0 -DA:171,0 -DA:171,0 -DA:172,0 -DA:172,0 -BRDA:172,9,0,- -BRDA:172,9,1,- -DA:173,0 -DA:173,0 -DA:175,0 -DA:175,0 -DA:175,0 -DA:179,0 -DA:179,0 -BRDA:179,10,0,- -BRDA:179,10,1,- -DA:180,0 -DA:180,0 -DA:183,0 -DA:183,0 -DA:184,0 -DA:184,0 -DA:187,0 -DA:187,0 -DA:188,0 -DA:188,0 -DA:191,0 -DA:191,0 -BRDA:191,11,0,- -BRDA:191,11,1,- -DA:192,0 -DA:192,0 -DA:194,0 -DA:194,0 -DA:195,0 -DA:195,0 -DA:197,0 -DA:197,0 -DA:198,0 -DA:198,0 -FN:203,LibDiamond.removeFunctions -FNDA:0,LibDiamond.removeFunctions -DA:207,0 -DA:207,0 -BRDA:207,12,0,- -BRDA:207,12,1,- -DA:208,0 -DA:208,0 -DA:210,0 -DA:210,0 -DA:210,0 -DA:212,0 -DA:212,0 -BRDA:212,13,0,- -BRDA:212,13,1,- -DA:213,0 -DA:213,0 -DA:216,0 -DA:216,0 -DA:217,0 -DA:217,0 -DA:220,0 -DA:220,0 -DA:221,0 -DA:221,0 -DA:224,0 -DA:224,0 -DA:226,0 -DA:226,0 -FN:231,LibDiamond.addFacet -FNDA:2950,LibDiamond.addFacet -DA:235,2950 -DA:235,2950 -DA:236,2950 -DA:236,2950 -DA:239,2950 -DA:239,2950 -FN:242,LibDiamond.addFunction -FNDA:11865,LibDiamond.addFunction -DA:248,11865 -DA:248,11865 -DA:251,11865 -DA:251,11865 -DA:254,11865 -DA:254,11865 -FN:257,LibDiamond.removeFunction -FNDA:0,LibDiamond.removeFunction -DA:262,0 -DA:262,0 -BRDA:262,14,0,- -BRDA:262,14,1,- -DA:263,0 -DA:263,0 -DA:266,0 -DA:266,0 -DA:266,0 -BRDA:266,15,0,- -BRDA:266,15,1,- -DA:267,0 -DA:267,0 -DA:270,0 -DA:270,0 -DA:273,0 -DA:273,0 -DA:273,0 -DA:278,0 -DA:278,0 -BRDA:278,16,0,- -BRDA:278,16,1,- -DA:279,0 -DA:279,0 -DA:282,0 -DA:282,0 -DA:285,0 -DA:285,0 -DA:290,0 -DA:290,0 -DA:291,0 -DA:291,0 -DA:294,0 -DA:294,0 -BRDA:294,17,0,- -BRDA:294,17,1,- -DA:296,0 -DA:296,0 -DA:296,0 -DA:297,0 -DA:297,0 -DA:300,0 -DA:300,0 -BRDA:300,18,0,- -BRDA:300,18,1,- -DA:301,0 -DA:301,0 -DA:304,0 -DA:304,0 -DA:305,0 -DA:305,0 -DA:309,0 -DA:309,0 -DA:310,0 -DA:310,0 -FN:316,LibDiamond.initializeDiamondCut -FNDA:1508,LibDiamond.initializeDiamondCut -DA:320,1508 -DA:320,1508 -BRDA:320,19,0,- -BRDA:320,19,1,1500 -DA:321,1500 -DA:321,1500 -BRDA:321,20,0,- -BRDA:321,20,1,1500 -DA:322,0 -DA:322,0 -DA:325,8 -DA:325,8 -BRDA:325,21,0,- -BRDA:325,21,1,8 -DA:326,0 -DA:326,0 -DA:328,8 -DA:328,8 -DA:328,8 -BRDA:328,22,0,8 -BRDA:328,22,1,8 -DA:329,8 -DA:329,8 -DA:332,8 -DA:332,8 -DA:332,8 -DA:333,8 -DA:333,8 -BRDA:333,23,0,- -BRDA:333,23,1,- -DA:334,0 -DA:334,0 -BRDA:334,24,0,- -BRDA:334,24,1,- -DA:336,0 -DA:336,0 -DA:338,0 -DA:338,0 -FN:344,LibDiamond.enforceHasContractCode -FNDA:2958,LibDiamond.enforceHasContractCode -DA:345,2958 -DA:345,2958 -DA:348,2958 -DA:348,2958 -DA:350,2958 -DA:350,2958 -BRDA:350,25,0,- -BRDA:350,25,1,2958 -DA:351,0 -DA:351,0 -FNF:13 -FNH:10 -LF:110 -LH:46 -BRF:52 -BRH:14 -end_of_record -TN: -SF:src/Libraries/LibSwap.sol -FN:30,LibSwap.swap -FNDA:134,LibSwap.swap -DA:31,134 -DA:31,134 -BRDA:31,0,0,- -BRDA:31,0,1,134 -DA:31,0 -DA:32,134 -DA:32,134 -DA:33,134 -DA:33,134 -BRDA:33,1,0,- -BRDA:33,1,1,134 -DA:33,0 -DA:34,134 -DA:34,134 -DA:34,134 -DA:37,134 -DA:37,134 -DA:37,134 -DA:40,134 -DA:40,134 -DA:40,134 -DA:44,134 -DA:44,134 -BRDA:44,2,0,112 -BRDA:44,2,1,134 -DA:45,112 -DA:45,112 -DA:52,134 -DA:52,134 -BRDA:52,3,0,2 -BRDA:52,3,1,132 -DA:53,2 -DA:53,2 -DA:60,132 -DA:60,132 -DA:60,132 -DA:63,132 -DA:63,132 -BRDA:63,4,0,1 -BRDA:63,4,1,131 -DA:64,1 -DA:64,1 -DA:67,131 -DA:67,131 -DA:67,131 -DA:69,131 -DA:69,131 -FNF:1 -FNH:1 -LF:15 -LH:15 -BRF:10 -BRH:8 -end_of_record -TN: -SF:src/Libraries/LibUtil.sol -FN:9,LibUtil.getRevertMsg -FNDA:0,LibUtil.getRevertMsg -DA:13,0 -DA:13,0 -BRDA:13,0,0,- -BRDA:13,0,1,- -DA:13,0 -DA:14,0 -DA:14,0 -DA:14,0 -DA:15,0 -DA:15,0 -DA:15,0 -FN:21,LibUtil.isZeroAddress -FNDA:22626,LibUtil.isZeroAddress -DA:22,22626 -DA:22,22626 -DA:22,22626 -DA:22,22626 -FN:25,LibUtil.revertWith -FNDA:9,LibUtil.revertWith -FNF:3 -FNH:2 -LF:4 -LH:1 -BRF:2 -BRH:0 -end_of_record -TN: -SF:src/Periphery/ERC20Proxy.sol -FN:22,ERC20Proxy. -FNDA:0,ERC20Proxy. -DA:23,0 -DA:23,0 -FN:29,ERC20Proxy.setAuthorizedCaller -FNDA:6,ERC20Proxy.setAuthorizedCaller -DA:33,6 -DA:33,6 -DA:34,6 -DA:34,6 -FN:42,ERC20Proxy.transferFrom -FNDA:2,ERC20Proxy.transferFrom -DA:48,2 -DA:48,2 -BRDA:48,0,0,- -BRDA:48,0,1,2 -DA:48,0 -DA:50,2 -DA:50,2 -FNF:3 -FNH:2 -LF:5 -LH:4 -BRF:2 -BRH:1 -end_of_record -TN: -SF:src/Periphery/Executor.sol -FN:30,Executor.noLeftovers -FNDA:11,Executor.noLeftovers -DA:34,11 -DA:34,11 -DA:35,11 -DA:35,11 -BRDA:35,0,0,- -BRDA:35,0,1,9 -DA:36,3 -DA:36,3 -DA:36,3 -DA:37,3 -DA:37,3 -DA:38,3 -DA:38,3 -DA:42,3 -DA:42,3 -DA:42,18 -DA:42,18 -DA:43,15 -DA:43,15 -DA:45,15 -DA:45,15 -BRDA:45,1,0,- -BRDA:45,1,1,9 -DA:46,9 -DA:46,9 -DA:47,9 -DA:47,9 -BRDA:47,2,0,- -BRDA:47,2,1,9 -DA:48,9 -DA:48,9 -DA:56,15 -DA:56,15 -FN:67,Executor. -FNDA:0,Executor. -DA:68,0 -DA:68,0 -DA:69,0 -DA:69,0 -FN:79,Executor.swapAndCompleteBridgeTokens -FNDA:9,Executor.swapAndCompleteBridgeTokens -DA:85,9 -DA:85,9 -FN:101,Executor.swapAndExecute -FNDA:2,Executor.swapAndExecute -DA:108,2 -DA:108,2 -FN:127,Executor._processSwaps -FNDA:11,Executor._processSwaps -DA:135,11 -DA:135,11 -DA:136,11 -DA:136,11 -DA:137,11 -DA:137,11 -DA:139,11 -DA:139,11 -BRDA:139,3,0,8 -BRDA:139,3,1,3 -DA:140,8 -DA:140,8 -DA:142,3 -DA:142,3 -DA:147,11 -DA:147,11 -BRDA:147,4,0,- -BRDA:147,4,1,2 -DA:148,10 -DA:148,10 -DA:149,10 -BRDA:149,5,0,- -BRDA:149,5,1,8 -DA:150,8 -DA:150,8 -DA:150,8 -DA:154,8 -DA:154,8 -DA:156,2 -DA:156,2 -DA:164,1 -DA:164,1 -DA:169,11 -DA:169,11 -DA:171,8 -DA:171,8 -DA:171,8 -DA:172,8 -DA:172,8 -BRDA:172,6,0,4 -BRDA:172,6,1,8 -DA:173,4 -DA:173,4 -DA:180,8 -DA:180,8 -DA:180,8 -DA:184,8 -DA:184,8 -BRDA:184,7,0,4 -BRDA:184,7,1,8 -DA:185,4 -DA:185,4 -DA:192,8 -DA:192,8 -FN:205,Executor._executeSwaps -FNDA:11,Executor._executeSwaps -DA:210,3 -DA:210,3 -DA:211,3 -DA:211,3 -DA:211,21 -DA:212,18 -DA:212,18 -DA:212,18 -BRDA:212,8,0,- -BRDA:212,8,1,8 -DA:213,0 -DA:213,0 -DA:216,18 -DA:216,18 -DA:217,18 -DA:217,18 -DA:219,18 -DA:219,18 -FN:227,Executor._fetchBalances -FNDA:3,Executor._fetchBalances -DA:230,3 -DA:230,3 -DA:231,3 -DA:231,3 -DA:231,3 -DA:232,3 -DA:232,3 -DA:233,3 -DA:233,3 -DA:233,21 -DA:234,18 -DA:234,18 -DA:235,18 -DA:235,18 -DA:237,18 -DA:237,18 -BRDA:237,9,0,- -BRDA:237,9,1,9 -DA:238,9 -DA:238,9 -DA:242,18 -DA:242,18 -DA:246,3 -DA:246,3 -FNF:7 -FNH:6 -LF:54 -LH:51 -BRF:20 -BRH:13 -end_of_record -TN: -SF:src/Periphery/FeeCollector.sol -FN:44,FeeCollector. -FNDA:26,FeeCollector. -FN:53,FeeCollector.collectTokenFees -FNDA:11,FeeCollector.collectTokenFees -DA:59,11 -DA:59,11 -DA:60,11 -DA:60,11 -DA:61,11 -DA:61,11 -DA:62,11 -DA:62,11 -FN:74,FeeCollector.collectNativeFees -FNDA:6,FeeCollector.collectNativeFees -DA:79,6 -DA:79,6 -DA:79,6 -BRDA:79,0,0,- -BRDA:79,0,1,6 -DA:80,0 -DA:80,0 -DA:81,6 -DA:81,6 -DA:82,6 -DA:82,6 -DA:83,6 -DA:83,6 -DA:83,6 -DA:85,6 -DA:85,6 -BRDA:85,1,0,- -BRDA:85,1,1,1 -DA:87,1 -DA:87,1 -DA:87,1 -DA:90,1 -DA:90,1 -BRDA:90,2,0,- -BRDA:90,2,1,1 -DA:91,0 -DA:91,0 -DA:94,6 -DA:94,6 -FN:104,FeeCollector.withdrawIntegratorFees -FNDA:2,FeeCollector.withdrawIntegratorFees -DA:105,2 -DA:105,2 -DA:106,2 -DA:106,2 -BRDA:106,3,0,1 -BRDA:106,3,1,1 -DA:107,1 -DA:107,1 -DA:109,1 -DA:109,1 -DA:110,1 -DA:110,1 -DA:111,1 -DA:111,1 -FN:116,FeeCollector.batchWithdrawIntegratorFees -FNDA:1,FeeCollector.batchWithdrawIntegratorFees -DA:119,1 -DA:119,1 -DA:120,1 -DA:120,1 -DA:121,1 -DA:121,1 -DA:121,3 -DA:122,2 -DA:122,2 -DA:123,2 -DA:123,2 -BRDA:123,4,0,- -BRDA:123,4,1,2 -DA:124,2 -DA:124,2 -DA:125,2 -DA:125,2 -DA:130,2 -DA:130,2 -DA:133,2 -DA:133,2 -FN:140,FeeCollector.withdrawLifiFees -FNDA:2,FeeCollector.withdrawLifiFees -DA:141,1 -DA:141,1 -DA:142,1 -DA:142,1 -BRDA:142,5,0,- -BRDA:142,5,1,1 -DA:143,0 -DA:143,0 -DA:145,1 -DA:145,1 -DA:146,1 -DA:146,1 -DA:147,1 -DA:147,1 -FN:152,FeeCollector.batchWithdrawLifiFees -FNDA:2,FeeCollector.batchWithdrawLifiFees -DA:155,1 -DA:155,1 -DA:156,1 -DA:156,1 -DA:157,1 -DA:157,1 -DA:157,3 -DA:158,2 -DA:158,2 -DA:159,2 -DA:159,2 -DA:160,2 -DA:160,2 -DA:165,2 -DA:165,2 -DA:167,2 -DA:167,2 -FN:175,FeeCollector.getTokenBalance -FNDA:8,FeeCollector.getTokenBalance -DA:179,8 -DA:179,8 -FN:184,FeeCollector.getLifiTokenBalance -FNDA:8,FeeCollector.getLifiTokenBalance -DA:187,8 -DA:187,8 -FNF:9 -FNH:9 -LF:45 -LH:42 -BRF:12 -BRH:7 -end_of_record -TN: -SF:src/Periphery/GasRebateDistributor.sol -FN:39,GasRebateDistributor. -FNDA:13,GasRebateDistributor. -DA:45,13 -DA:45,13 -DA:46,13 -DA:46,13 -DA:47,13 -DA:47,13 -DA:48,13 -DA:48,13 -FN:56,GasRebateDistributor.claim -FNDA:10,GasRebateDistributor.claim -DA:61,9 -DA:61,9 -BRDA:61,0,0,1 -BRDA:61,0,1,8 -DA:62,1 -DA:62,1 -DA:65,8 -DA:65,8 -BRDA:65,1,0,1 -BRDA:65,1,1,7 -DA:65,1 -DA:68,7 -DA:68,7 -DA:68,7 -DA:69,7 -DA:69,7 -BRDA:69,2,0,3 -BRDA:69,2,1,4 -DA:70,3 -DA:70,3 -DA:73,4 -DA:73,4 -DA:76,4 -DA:76,4 -DA:78,4 -DA:78,4 -FN:85,GasRebateDistributor.withdrawUnclaimed -FNDA:3,GasRebateDistributor.withdrawUnclaimed -DA:89,1 -DA:89,1 -DA:89,2 -DA:91,1 -DA:91,1 -DA:91,1 -DA:96,1 -DA:96,1 -DA:100,1 -DA:100,1 -FN:109,GasRebateDistributor.updateMerkleRoot -FNDA:3,GasRebateDistributor.updateMerkleRoot -DA:115,2 -DA:115,2 -DA:118,2 -DA:118,2 -DA:121,2 -DA:121,2 -DA:124,2 -DA:124,2 -FN:128,GasRebateDistributor.pauseContract -FNDA:3,GasRebateDistributor.pauseContract -DA:129,3 -DA:129,3 -FN:133,GasRebateDistributor.unpauseContract -FNDA:1,GasRebateDistributor.unpauseContract -DA:134,1 -DA:134,1 -FNF:6 -FNH:6 -LF:23 -LH:23 -BRF:6 -BRH:6 -end_of_record -TN: -SF:src/Periphery/GasZip.sol -FN:35,GasZip. -FNDA:4,GasZip. -DA:36,4 -DA:36,4 -FN:43,GasZip.zipERC20 -FNDA:1,GasZip.zipERC20 -DA:49,1 -DA:49,1 -DA:56,1 -DA:56,1 -DA:59,1 -DA:59,1 -DA:65,1 -DA:65,1 -DA:65,1 -DA:70,1 -DA:70,1 -BRDA:70,0,0,- -BRDA:70,0,1,1 -DA:71,0 -DA:71,0 -FN:82,GasZip.zip -FNDA:1,GasZip.zip -DA:87,1 -DA:87,1 -DA:88,1 -DA:88,1 -DA:89,1 -DA:89,1 -DA:91,1 -DA:91,1 -DA:96,1 -DA:96,1 -DA:98,1 -DA:98,1 -DA:102,1 -DA:102,1 -BRDA:102,1,0,- -BRDA:102,1,1,- -DA:104,0 -DA:104,0 -DA:104,0 -DA:105,0 -DA:105,0 -BRDA:105,2,0,- -BRDA:105,2,1,- -DA:105,0 -FNF:3 -FNH:3 -LF:16 -LH:13 -BRF:6 -BRH:1 -end_of_record -TN: -SF:src/Periphery/LiFuelFeeCollector.sol -FN:33,LiFuelFeeCollector. -FNDA:20,LiFuelFeeCollector. -FN:42,LiFuelFeeCollector.collectTokenGasFees -FNDA:263,LiFuelFeeCollector.collectTokenGasFees -DA:48,263 -DA:48,263 -DA:49,263 -DA:49,263 -FN:55,LiFuelFeeCollector.collectNativeGasFees -FNDA:4,LiFuelFeeCollector.collectNativeGasFees -DA:60,4 -DA:60,4 -DA:66,4 -DA:66,4 -DA:66,4 -DA:67,4 -DA:67,4 -BRDA:67,0,0,- -BRDA:67,0,1,- -DA:68,0 -DA:68,0 -DA:68,0 -DA:69,0 -DA:69,0 -BRDA:69,1,0,- -BRDA:69,1,1,- -DA:70,0 -DA:70,0 -FN:77,LiFuelFeeCollector.withdrawFees -FNDA:2,LiFuelFeeCollector.withdrawFees -DA:78,1 -DA:78,1 -DA:78,1 -DA:79,1 -DA:79,1 -DA:80,1 -DA:80,1 -FN:85,LiFuelFeeCollector.batchWithdrawFees -FNDA:1,LiFuelFeeCollector.batchWithdrawFees -DA:88,1 -DA:88,1 -DA:89,1 -DA:89,1 -DA:90,1 -DA:90,1 -DA:90,3 -DA:91,2 -DA:91,2 -DA:92,2 -DA:92,2 -DA:97,2 -DA:97,2 -DA:99,2 -DA:99,2 -FNF:5 -FNH:5 -LF:18 -LH:15 -BRF:4 -BRH:0 -end_of_record -TN: -SF:src/Periphery/Receiver.sol -FN:33,Receiver.onlySGRouter -FNDA:2,Receiver.onlySGRouter -DA:34,2 -DA:34,2 -BRDA:34,0,0,- -BRDA:34,0,1,2 -DA:35,0 -DA:35,0 -FN:39,Receiver.onlyAmarokRouter -FNDA:2,Receiver.onlyAmarokRouter -DA:40,2 -DA:40,2 -BRDA:40,1,0,- -BRDA:40,1,1,2 -DA:41,0 -DA:41,0 -FN:47,Receiver. -FNDA:0,Receiver. -DA:54,0 -DA:54,0 -DA:55,0 -DA:55,0 -DA:56,0 -DA:56,0 -DA:57,0 -DA:57,0 -DA:58,0 -DA:58,0 -DA:59,0 -DA:59,0 -DA:60,0 -DA:60,0 -DA:61,0 -DA:61,0 -FN:74,Receiver.xReceive -FNDA:2,Receiver.xReceive -DA:82,2 -DA:82,2 -DA:82,2 -DA:87,2 -DA:87,2 -FN:105,Receiver.sgReceive -FNDA:2,Receiver.sgReceive -DA:113,2 -DA:113,2 -DA:118,2 -DA:118,2 -DA:123,2 -DA:123,2 -FN:138,Receiver.swapAndCompleteBridgeTokens -FNDA:0,Receiver.swapAndCompleteBridgeTokens -DA:144,0 -DA:144,0 -BRDA:144,2,0,- -BRDA:144,2,1,- -DA:145,0 -DA:145,0 -DA:154,0 -DA:154,0 -DA:154,0 -DA:158,0 -DA:158,0 -DA:159,0 -DA:159,0 -FN:174,Receiver.pullToken -FNDA:2,Receiver.pullToken -DA:179,1 -DA:179,1 -BRDA:179,3,0,- -BRDA:179,3,1,- -DA:181,0 -DA:181,0 -DA:181,0 -DA:182,0 -DA:182,0 -BRDA:182,4,0,- -BRDA:182,4,1,- -DA:182,0 -DA:184,1 -DA:184,1 -FN:197,Receiver._swapAndCompleteBridgeTokens -FNDA:4,Receiver._swapAndCompleteBridgeTokens -DA:205,4 -DA:205,4 -DA:205,4 -DA:207,4 -DA:207,4 -BRDA:207,5,0,- -BRDA:207,5,1,- -DA:209,0 -DA:209,0 -DA:209,0 -DA:210,0 -DA:210,0 -DA:210,0 -BRDA:210,6,0,- -BRDA:210,6,1,- -DA:213,0 -DA:213,0 -DA:213,0 -DA:214,0 -DA:214,0 -BRDA:214,7,0,- -BRDA:214,7,1,- -DA:214,0 -DA:216,0 -DA:216,0 -DA:223,0 -DA:223,0 -DA:229,0 -DA:229,0 -DA:248,4 -DA:248,4 -DA:248,4 -DA:249,4 -DA:249,4 -DA:249,4 -DA:250,4 -DA:250,4 -DA:252,4 -DA:252,4 -DA:252,2 -BRDA:252,8,0,1 -BRDA:252,8,1,3 -DA:254,1 -DA:254,1 -DA:256,1 -DA:256,1 -DA:263,1 -DA:263,1 -DA:267,3 -DA:267,3 -DA:269,3 -DA:269,3 -DA:283,3 -DA:283,3 -FNF:8 -FNH:6 -LF:45 -LH:21 -BRF:18 -BRH:4 -end_of_record -TN: -SF:src/Periphery/RelayerCelerIM.sol -FN:40,RelayerCelerIM.onlyCBridgeMessageBus -FNDA:2,RelayerCelerIM.onlyCBridgeMessageBus -DA:41,2 -DA:41,2 -DA:41,2 -BRDA:41,0,0,1 -BRDA:41,0,1,2 -DA:41,1 -FN:44,RelayerCelerIM.onlyDiamond -FNDA:269,RelayerCelerIM.onlyDiamond -DA:45,269 -DA:45,269 -BRDA:45,1,0,- -BRDA:45,1,1,2 -DA:45,0 -FN:51,RelayerCelerIM. -FNDA:0,RelayerCelerIM. -DA:56,0 -DA:56,0 -DA:57,0 -DA:57,0 -DA:58,0 -DA:58,0 -FN:73,RelayerCelerIM.executeMessageWithTransfer -FNDA:3,RelayerCelerIM.executeMessageWithTransfer -DA:87,2 -DA:87,2 -DA:92,2 -DA:92,2 -DA:97,2 -DA:97,2 -DA:106,2 -DA:106,2 -FN:117,RelayerCelerIM.executeMessageWithTransferRefund -FNDA:2,RelayerCelerIM.executeMessageWithTransferRefund -DA:128,1 -DA:128,1 -DA:128,1 -DA:134,1 -DA:134,1 -DA:136,1 -DA:136,1 -DA:144,1 -DA:144,1 -FN:153,RelayerCelerIM.sendTokenTransfer -FNDA:269,RelayerCelerIM.sendTokenTransfer -DA:164,269 -DA:164,269 -BRDA:164,2,0,- -BRDA:164,2,1,260 -DA:165,264 -DA:165,264 -DA:166,264 -DA:166,264 -BRDA:166,3,0,- -BRDA:166,3,1,4 -DA:168,4 -DA:168,4 -DA:179,260 -DA:179,260 -DA:185,260 -DA:185,260 -DA:194,264 -DA:194,264 -DA:202,5 -DA:202,5 -BRDA:201,4,0,- -BRDA:201,4,1,1 -DA:204,1 -DA:204,1 -DA:205,1 -DA:205,1 -DA:210,1 -DA:210,1 -DA:217,1 -DA:217,1 -DA:225,4 -DA:225,4 -BRDA:224,5,0,- -BRDA:224,5,1,1 -DA:227,1 -DA:227,1 -DA:228,1 -DA:228,1 -DA:233,1 -DA:233,1 -DA:239,1 -DA:239,1 -DA:246,3 -DA:246,3 -BRDA:245,6,0,- -BRDA:245,6,1,1 -DA:248,2 -DA:248,2 -DA:249,2 -DA:249,2 -BRDA:249,7,0,- -BRDA:249,7,1,1 -DA:251,1 -DA:251,1 -DA:260,1 -DA:260,1 -DA:265,1 -DA:265,1 -DA:274,1 -DA:274,1 -BRDA:273,8,0,- -BRDA:273,8,1,1 -DA:276,1 -DA:276,1 -DA:277,1 -DA:277,1 -DA:282,1 -DA:282,1 -DA:290,0 -DA:290,0 -BRDA:289,9,0,- -BRDA:289,9,1,- -DA:293,0 -DA:293,0 -DA:294,0 -DA:294,0 -DA:299,0 -DA:299,0 -DA:307,0 -DA:307,0 -FN:320,RelayerCelerIM.forwardSendMessageWithTransfer -FNDA:2,RelayerCelerIM.forwardSendMessageWithTransfer -DA:327,2 -DA:327,2 -FN:346,RelayerCelerIM._swapAndCompleteBridgeTokens -FNDA:2,RelayerCelerIM._swapAndCompleteBridgeTokens -DA:354,2 -DA:354,2 -DA:355,2 -DA:355,2 -DA:355,2 -DA:360,2 -DA:360,2 -BRDA:360,10,0,- -BRDA:360,10,1,- -DA:362,0 -DA:362,0 -DA:378,2 -DA:378,2 -DA:378,2 -DA:379,2 -DA:379,2 -DA:380,2 -DA:380,2 -DA:383,2 -DA:383,2 -DA:394,2 -DA:394,2 -DA:397,2 -DA:397,2 -BRDA:397,11,0,1 -BRDA:397,11,1,2 -DA:398,1 -DA:398,1 -FN:412,RelayerCelerIM.withdraw -FNDA:0,RelayerCelerIM.withdraw -DA:417,0 -DA:417,0 -BRDA:417,12,0,- -BRDA:417,12,1,- -DA:419,0 -DA:419,0 -DA:419,0 -DA:420,0 -DA:420,0 -BRDA:420,13,0,- -BRDA:420,13,1,- -DA:421,0 -DA:421,0 -DA:424,0 -DA:424,0 -DA:426,0 -DA:426,0 -FN:435,RelayerCelerIM.triggerRefund -FNDA:1,RelayerCelerIM.triggerRefund -DA:442,1 -DA:442,1 -DA:446,1 -DA:446,1 -DA:446,1 -DA:446,1 -DA:446,1 -DA:446,1 -DA:446,1 -DA:447,0 -DA:447,0 -DA:447,0 -DA:448,0 -DA:448,0 -DA:448,0 -DA:449,0 -DA:449,0 -DA:449,0 -DA:450,0 -DA:450,0 -DA:450,0 -BRDA:445,14,0,- -BRDA:445,14,1,1 -DA:452,0 -DA:452,0 -DA:457,1 -DA:457,1 -DA:460,1 -BRDA:460,15,0,1 -BRDA:460,15,1,- -DA:461,1 -DA:461,1 -DA:461,1 -DA:462,1 -DA:462,1 -DA:463,1 -DA:463,1 -DA:465,0 -DA:465,0 -FNF:10 -FNH:8 -LF:76 -LH:55 -BRF:32 -BRH:14 -end_of_record -TN: -SF:src/Periphery/ServiceFeeCollector.sol -FN:39,ServiceFeeCollector. -FNDA:20,ServiceFeeCollector. -FN:47,ServiceFeeCollector.collectTokenInsuranceFees -FNDA:4,ServiceFeeCollector.collectTokenInsuranceFees -DA:52,4 -DA:52,4 -DA:53,4 -DA:53,4 -FN:58,ServiceFeeCollector.collectNativeInsuranceFees -FNDA:2,ServiceFeeCollector.collectNativeInsuranceFees -DA:59,2 -DA:59,2 -FN:68,ServiceFeeCollector.withdrawFees -FNDA:2,ServiceFeeCollector.withdrawFees -DA:69,1 -DA:69,1 -DA:69,1 -DA:70,1 -DA:70,1 -DA:71,1 -DA:71,1 -FN:76,ServiceFeeCollector.batchWithdrawFees -FNDA:1,ServiceFeeCollector.batchWithdrawFees -DA:79,1 -DA:79,1 -DA:80,1 -DA:80,1 -DA:81,1 -DA:81,1 -DA:81,3 -DA:82,2 -DA:82,2 -DA:83,2 -DA:83,2 -DA:88,2 -DA:88,2 -DA:90,2 -DA:90,2 -FNF:5 -FNH:5 -LF:13 -LH:13 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/Periphery/TokenWrapper.sol -FN:27,TokenWrapper. -FNDA:0,TokenWrapper. -DA:28,0 -DA:28,0 -DA:29,0 -DA:29,0 -FN:35,TokenWrapper.deposit -FNDA:1,TokenWrapper.deposit -DA:36,1 -DA:36,1 -DA:37,1 -DA:37,1 -FN:41,TokenWrapper.withdraw -FNDA:1,TokenWrapper.withdraw -DA:46,1 -DA:46,1 -DA:46,1 -DA:47,1 -DA:47,1 -DA:48,1 -DA:48,1 -DA:49,1 -DA:49,1 -DA:49,1 -DA:50,1 -DA:50,1 -BRDA:50,0,0,- -BRDA:50,0,1,1 -DA:51,0 -DA:51,0 -FNF:3 -FNH:2 -LF:10 -LH:7 -BRF:2 -BRH:1 -end_of_record -TN: From 46cc685788640e7c5ec0432f69b376be8dc60ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 27 Jun 2024 09:12:15 +0700 Subject: [PATCH 025/100] cleanup --- .gitignore | 1 + script/deploy/_targetState.json | 5 ----- tmpfile | 0 3 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 tmpfile diff --git a/.gitignore b/.gitignore index b4121c5bc..78985120a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ yarn-error.log .DS_Store* lcov.info +lcov-filtered.info test/logs diff --git a/script/deploy/_targetState.json b/script/deploy/_targetState.json index e22094a73..8a4ca8408 100644 --- a/script/deploy/_targetState.json +++ b/script/deploy/_targetState.json @@ -346,11 +346,6 @@ } }, "bsc": { - "staging": { - "LiFiDiamond": { - "GasZip": "1.0.0" - } - }, "production": { "LiFiDiamond": { "DiamondCutFacet": "1.0.0", diff --git a/tmpfile b/tmpfile deleted file mode 100644 index e69de29bb..000000000 From d3333bd2178c4b4c1293296fb871fae5583e1d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 27 Jun 2024 09:18:14 +0700 Subject: [PATCH 026/100] update docs --- docs/GasZip.md | 38 -------------------------------------- docs/GasZipFacet.md | 37 +++++++++++++++++++++++++++++++++++++ src/Facets/GasZipFacet.sol | 2 +- 3 files changed, 38 insertions(+), 39 deletions(-) delete mode 100644 docs/GasZip.md create mode 100644 docs/GasZipFacet.md diff --git a/docs/GasZip.md b/docs/GasZip.md deleted file mode 100644 index 2f7f40465..000000000 --- a/docs/GasZip.md +++ /dev/null @@ -1,38 +0,0 @@ -# GasZip - -## Description - -Periphery contract used for sending gas on requested chain. - -## How To Use - -The contract is meant to be used to call the GasZip routers which will execute the refueling part. - -There are two methods. -One for ERC20 tokens - -```solidity -/// @notice Refuel the gas on the requested chain from ERC20 token -/// @param _swap data needed for swap before the router call -/// @param destinationChain native token will be received on this chain -/// @param recipient address that will receive tokens in the end -function zipERC20( - SwapData calldata _swap, - uint256 destinationChain, - address recipient -) -``` - -and another for Native tokens (here no swap before the router call will be performed) - -```solidity -/// @notice Refuel the gas on the requested chain from native token -/// @param amountToZip amount of sending token -/// @param destinationChain native token will be received on this chain -/// @param recipient address that will receive tokens in the end -function zipERC20( - uint256 amountToZip, - uint256 destinationChain, - address recipient -) -``` diff --git a/docs/GasZipFacet.md b/docs/GasZipFacet.md new file mode 100644 index 000000000..dcbe9fbbc --- /dev/null +++ b/docs/GasZipFacet.md @@ -0,0 +1,37 @@ +# GasZipFacet + +## Description + +The GasZipFacet provides function to deposit ERC20 and native tokens to the Gas.zip protocol (https://www.gas.zip/) + +## How To Use + +The contract provides two public methods: + +One for ERC20 tokens (these will be swapped into native before depositing to gas.zip) + +```solidity +/// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract +/// @param _swapData The swap data struct +/// @param _destinationChainId the id of the chain where gas should be made available +/// @param _recipient the address to receive the gas on dst chain +function depositToGasZipERC20( + LibSwap.SwapData calldata _swapData, + uint256 _destinationChainId, + address _recipient +) +``` + +and another for Native tokens (these will be directly deposited) + +```solidity +/// @notice Deposits native tokens in the GasZip router contract +/// @param _amountToZip The swap data struct +/// @param _destinationChainId the id of the chain where gas should be made available +/// @param _recipient the address to receive the gas on dst chain +function depositToGasZipNative( + uint256 _amountToZip, + uint256 _destinationChainId, + address _recipient +) +``` diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 9bea8f27b..94f96facc 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -54,7 +54,7 @@ contract GasZipFacet { ); } - /// @notice Deposits native tokens in the GasZip router contract and returns any unused + /// @notice Deposits native tokens in the GasZip router contract /// @param _amountToZip The swap data struct /// @param _destinationChainId the id of the chain where gas should be made available /// @param _recipient the address to receive the gas on dst chain From cbf2fcc9b8bdbef17aa83bdc37e9e43e46ac6c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 27 Jun 2024 09:43:42 +0700 Subject: [PATCH 027/100] adds complex native test case --- src/Facets/GasZipFacet.sol | 10 --- test/solidity/Facets/GasZipFacet.t.sol | 96 +++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 12 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 94f96facc..df00a76b6 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -68,15 +68,5 @@ contract GasZipFacet { _destinationChainId, _recipient ); - - // Send back any remaining native balance to the msg.sender (i.e. LI.FI diamond) - uint256 nativeBalance = address(this).balance; - if (nativeBalance > 0) { - // solhint-disable-next-line avoid-low-level-calls - (bool success, ) = msg.sender.call{ value: nativeBalance }(""); - if (!success) revert NativeAssetTransferFailed(); - } } - - receive() external payable {} } diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 0da49d11d..43b0c6a04 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -94,11 +94,13 @@ contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { gasZipFacet = TestGasZipFacet(payable(address(diamond))); // whitelist uniswap dex with function selectors - // v1 gasZipFacet.addDex(address(uniswap)); gasZipFacet.setFunctionApprovalBySignature( uniswap.swapExactTokensForTokens.selector ); + gasZipFacet.setFunctionApprovalBySignature( + uniswap.swapExactETHForTokens.selector + ); vm.label(address(gasZipFacet), "LiFiDiamond"); vm.label(ADDRESS_WETH, "WETH_TOKEN"); @@ -124,7 +126,9 @@ contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { ); } - function test_canCollectFeesThenSwapThenDepositERC20ThenBridge() public { + function test_canCollectERC20FeesThenSwapToERC20ThenDepositThenBridge() + public + { // Testcase: // 1. pay 1 USDC fee to FeeCollector in USDC // 2. swap remaining (9) USDC to DAI @@ -245,6 +249,94 @@ contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { ); } + function test_canDepositNativeThenSwapThenBridge() public { + // Testcase: + // 1. deposit small native amount to gasZip + // 2. swap remaining native to DAI + // 3. bridge remaining DAI to Gnosis using GnosisBridgeFacet + + uint256 nativeFromAmount = 1 ether; + + vm.deal(address(this), nativeFromAmount); + + uint256 nativeZipAmount = 1e14; + + // get swapData for gas zip + LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](2); + swapData[0] = LibSwap.SwapData( + address(gasZipFacet), + address(gasZipFacet), + address(0), + address(0), + nativeZipAmount, + abi.encodeWithSelector( + gasZipFacet.depositToGasZipNative.selector, + nativeZipAmount, + defaultDestinationChains, + defaultRecipientAddress + ), + false + ); + + // get swapData for swap + uint256 swapInputAmount = nativeFromAmount - nativeZipAmount; + + // prepare swap data + address[] memory path = new address[](2); + path[0] = ADDRESS_WETH; + path[1] = ADDRESS_DAI; + + // Calculate expected amountOut + uint256[] memory amounts = uniswap.getAmountsOut( + swapInputAmount, + path + ); + uint256 swapOutputAmount = amounts[1]; + + swapData[1] = LibSwap.SwapData( + address(uniswap), + address(uniswap), + address(0), + ADDRESS_DAI, + swapInputAmount, + abi.encodeWithSelector( + uniswap.swapExactETHForTokens.selector, + swapOutputAmount, + path, + address(diamond), + block.timestamp + 20 minutes + ), + false // not required since tokens are already in diamond + ); + + // get BridgeData + ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({ + transactionId: "", + bridge: "GnosisBridge", + integrator: "", + referrer: address(0), + sendingAssetId: ADDRESS_DAI, + receiver: defaultRecipientAddress, + minAmount: swapOutputAmount, + destinationChainId: 100, + hasSourceSwaps: true, + hasDestinationCall: false + }); + + // whitelist gasZipFacet and FeeCollector + gasZipFacet.addDex(address(gasZipFacet)); + gasZipFacet.setFunctionApprovalBySignature( + gasZipFacet.depositToGasZipNative.selector + ); + + // bridge using (standalone) GnosisBridgeFacet + TestGnosisBridgeFacet gnosisBridgeFacet = _getGnosisBridgeFacet(); + + gnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge{ + value: nativeFromAmount + }(bridgeData, swapData); + } + function _getGnosisBridgeFacet() internal returns (TestGnosisBridgeFacet gnosisBridgeFacet) From 7255dfabf09211198440e2b43ad69c76c2907b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 27 Jun 2024 09:51:15 +0700 Subject: [PATCH 028/100] deploys GasZipFacet to BSC staging --- deployments/bsc.diamond.staging.json | 41 ++++++++++--------- deployments/bsc.staging.json | 3 +- script/deploy/facets/UpdateGasZipFacet.s.sol | 13 ++++++ test/solidity/Facets/GenericSwapFacetV3.t.sol | 2 +- tmpfile | 0 5 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 script/deploy/facets/UpdateGasZipFacet.s.sol create mode 100644 tmpfile diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index fe1b3889c..ff21b73f1 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -3,35 +3,35 @@ "Facets": { "0x06045F5FA6EA7c6AcEb104b55BcD6C3dE3a08831": { "Name": "DiamondCutFacet", - "Version": "1.0.0" + "Version": "" }, "0x8938CEa23C3c5eAABb895765f5B0b2b07D680402": { "Name": "DiamondLoupeFacet", - "Version": "1.0.0" + "Version": "" }, "0x53d4Bcd5BEa4e863376b7eA43D7465351a4d71B0": { "Name": "OwnershipFacet", - "Version": "1.0.0" + "Version": "" }, "0x83be9a6642c41f7ef78A0B60a355B5D7f3C9A62f": { "Name": "WithdrawFacet", - "Version": "1.0.0" + "Version": "" }, "0xB94Fd26F6b138E1bE6CfEa5Ec4F67C5573F5d6AD": { "Name": "DexManagerFacet", - "Version": "1.0.0" + "Version": "" }, "0x1A841931913806FB7570B43bcD64A487A8E7A50c": { "Name": "AccessManagerFacet", - "Version": "1.0.0" + "Version": "" }, "0x27A0B9Dd7ee2762e15CCF36DF2F54A4A7B7a9304": { "Name": "PeripheryRegistryFacet", - "Version": "1.0.0" + "Version": "" }, "0x9E39c6906F8FBC16D7CC996aB81bcBeD0F6E021d": { "Name": "AllBridgeFacet", - "Version": "2.0.0" + "Version": "" }, "0xd6aba485e836d1C717734fFD00A25d16Cf738b49": { "Name": "", @@ -39,35 +39,39 @@ }, "0x9a077b9dD746237d8854848BDB01521B47edC8DF": { "Name": "LIFuelFacet", - "Version": "1.0.0" + "Version": "" }, "0x04BD4b6430483cFdD0450D0aFb08633d33C93275": { "Name": "", "Version": "" }, - "0xcED4B608A468ce334c75c6652e18E2Ba7f3F44dA": { - "Name": "MayanFacet", - "Version": "1.0.0" + "0xb179bfa49d8B5d272288C59b26ac93f036735790": { + "Name": "", + "Version": "" }, "0xAfcC5c55d5Ec3082675D51331E7Ed9AdE195db48": { "Name": "StargateFacet", - "Version": "2.2.0" + "Version": "" }, "0xa6aAe470E7B8E8916e692882A5db25bB40C398A7": { "Name": "ThorSwapFacet", - "Version": "0.0.3" + "Version": "" }, "0x8BFC9f022ACb65dfa0Eb6CCbA45c04C2a6cb9A34": { "Name": "SquidFacet", - "Version": "0.0.8" + "Version": "" }, "0x7ac3EB2D191EBAb9E925CAbFD4F8155be066b3aa": { "Name": "AmarokFacetPacked", - "Version": "1.0.0" + "Version": "" }, "0xA08EcCb4aDd1556CC42ABD5d8dFbEe8a56012359": { "Name": "GenericSwapFacet", - "Version": "1.0.0" + "Version": "" + }, + "0xf92D157a857d55B5DA987E35488e746EA85f1ca3": { + "Name": "GasZipFacet", + "Version": "" } }, "Periphery": { @@ -79,8 +83,7 @@ "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", "RelayerCelerIM": "", "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index a2ecd34f7..cd6866a8c 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -27,5 +27,6 @@ "AmarokFacetPacked": "0x7ac3EB2D191EBAb9E925CAbFD4F8155be066b3aa", "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A", "MayanBridgeFacet": "0x5Ba4FeD1DAd2fD057A9f687B399B8e4cF2368214", - "MayanFacet": "0xcED4B608A468ce334c75c6652e18E2Ba7f3F44dA" + "MayanFacet": "0xcED4B608A468ce334c75c6652e18E2Ba7f3F44dA", + "GasZipFacet": "0xf92D157a857d55B5DA987E35488e746EA85f1ca3" } \ No newline at end of file diff --git a/script/deploy/facets/UpdateGasZipFacet.s.sol b/script/deploy/facets/UpdateGasZipFacet.s.sol new file mode 100644 index 000000000..8433024ee --- /dev/null +++ b/script/deploy/facets/UpdateGasZipFacet.s.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { UpdateScriptBase } from "./utils/UpdateScriptBase.sol"; + +contract DeployScript is UpdateScriptBase { + function run() + public + returns (address[] memory facets, bytes memory cutData) + { + return update("GasZipFacet"); + } +} diff --git a/test/solidity/Facets/GenericSwapFacetV3.t.sol b/test/solidity/Facets/GenericSwapFacetV3.t.sol index 166c62237..0c7000344 100644 --- a/test/solidity/Facets/GenericSwapFacetV3.t.sol +++ b/test/solidity/Facets/GenericSwapFacetV3.t.sol @@ -1946,7 +1946,7 @@ contract GenericSwapFacetV3Test is DSTest, DiamondTest, TestHelpers { // get swapData ( LibSwap.SwapData[] memory swapData, - uint256 amountIn, + , uint256 minAmountOut ) = _produceSwapDataMultiswapERC20FeeAndSwapToNative( address(genericSwapFacetV3) diff --git a/tmpfile b/tmpfile new file mode 100644 index 000000000..e69de29bb From 2c7b52556ea056eea025b5c617147f48f1ff9a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 27 Jun 2024 09:55:12 +0700 Subject: [PATCH 029/100] fixes issue in deploy log file --- deployments/_deployments_log_file.json | 30 ++++++++++++++------------ deployments/bsc.diamond.staging.json | 30 +++++++++++++------------- tmpfile | 0 3 files changed, 31 insertions(+), 29 deletions(-) delete mode 100644 tmpfile diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index b0416b620..a98fe21de 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -18957,20 +18957,6 @@ } } }, - "GasZip": { - "bsc": { - "staging": { - "1.0.0": [ - { - "ADDRESS": "0x70f365dC8d4cF91C2318337157E76BB9c786C116", - "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-05-02 16:20:25", - "CONSTRUCTOR_ARGS": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a364f8c717caad9a442737eb7b8a55cc6cf18d80000000000000000000000004a364f8c717caad9a442737eb7b8a55cc6cf18d8", - } - ] - } - } - }, "MayanFacet": { "bsc": { "staging": { @@ -19444,5 +19430,21 @@ ] } } + }, + "GasZipFacet": { + "bsc": { + "staging": { + "1.0.0": [ + { + "ADDRESS": "0xf92D157a857d55B5DA987E35488e746EA85f1ca3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-06-27 09:53:54", + "CONSTRUCTOR_ARGS": "0x00000000000000000000000085e5fb57844be79b42997c898d177a39f328ccf0", + "SALT": "", + "VERIFIED": "true" + } + ] + } + } } } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index ff21b73f1..a0faf5e21 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -3,35 +3,35 @@ "Facets": { "0x06045F5FA6EA7c6AcEb104b55BcD6C3dE3a08831": { "Name": "DiamondCutFacet", - "Version": "" + "Version": "1.0.0" }, "0x8938CEa23C3c5eAABb895765f5B0b2b07D680402": { "Name": "DiamondLoupeFacet", - "Version": "" + "Version": "1.0.0" }, "0x53d4Bcd5BEa4e863376b7eA43D7465351a4d71B0": { "Name": "OwnershipFacet", - "Version": "" + "Version": "1.0.0" }, "0x83be9a6642c41f7ef78A0B60a355B5D7f3C9A62f": { "Name": "WithdrawFacet", - "Version": "" + "Version": "1.0.0" }, "0xB94Fd26F6b138E1bE6CfEa5Ec4F67C5573F5d6AD": { "Name": "DexManagerFacet", - "Version": "" + "Version": "1.0.0" }, "0x1A841931913806FB7570B43bcD64A487A8E7A50c": { "Name": "AccessManagerFacet", - "Version": "" + "Version": "1.0.0" }, "0x27A0B9Dd7ee2762e15CCF36DF2F54A4A7B7a9304": { "Name": "PeripheryRegistryFacet", - "Version": "" + "Version": "1.0.0" }, "0x9E39c6906F8FBC16D7CC996aB81bcBeD0F6E021d": { "Name": "AllBridgeFacet", - "Version": "" + "Version": "2.0.0" }, "0xd6aba485e836d1C717734fFD00A25d16Cf738b49": { "Name": "", @@ -39,7 +39,7 @@ }, "0x9a077b9dD746237d8854848BDB01521B47edC8DF": { "Name": "LIFuelFacet", - "Version": "" + "Version": "1.0.0" }, "0x04BD4b6430483cFdD0450D0aFb08633d33C93275": { "Name": "", @@ -51,27 +51,27 @@ }, "0xAfcC5c55d5Ec3082675D51331E7Ed9AdE195db48": { "Name": "StargateFacet", - "Version": "" + "Version": "2.2.0" }, "0xa6aAe470E7B8E8916e692882A5db25bB40C398A7": { "Name": "ThorSwapFacet", - "Version": "" + "Version": "0.0.3" }, "0x8BFC9f022ACb65dfa0Eb6CCbA45c04C2a6cb9A34": { "Name": "SquidFacet", - "Version": "" + "Version": "0.0.8" }, "0x7ac3EB2D191EBAb9E925CAbFD4F8155be066b3aa": { "Name": "AmarokFacetPacked", - "Version": "" + "Version": "1.0.0" }, "0xA08EcCb4aDd1556CC42ABD5d8dFbEe8a56012359": { "Name": "GenericSwapFacet", - "Version": "" + "Version": "1.0.0" }, "0xf92D157a857d55B5DA987E35488e746EA85f1ca3": { "Name": "GasZipFacet", - "Version": "" + "Version": "1.0.0" } }, "Periphery": { diff --git a/tmpfile b/tmpfile deleted file mode 100644 index e69de29bb..000000000 From 0ed7770917ddc9ee2147b5a78df5e9458aff09fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 27 Jun 2024 16:18:45 +0700 Subject: [PATCH 030/100] adds a function with deposit for standalone gas-zip only calls --- src/Facets/GasZipFacet.sol | 19 +++++++-- test/solidity/Facets/GasZipFacet.t.sol | 53 ++++++++++++++------------ 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index df00a76b6..2f172a57b 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -28,22 +28,33 @@ contract GasZipFacet { gasZipRouter = IGasZip(_gasZipRouter); } - /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract + /// @notice Pulls and swaps ERC20 tokens to native and then deposits these native tokens in the GasZip router contract /// @param _swapData The swap data struct /// @param _destinationChainId the id of the chain where gas should be made available /// @param _recipient the address to receive the gas on dst chain - function depositToGasZipERC20( + function depositToGasZipERC20WithDeposit( LibSwap.SwapData calldata _swapData, uint256 _destinationChainId, address _recipient - ) public { + ) external { // pull tokens from caller (e.g. LI.FI diamond) _swapData.sendingAssetId.safeTransferFrom( msg.sender, address(this), _swapData.fromAmount ); + depositToGasZipERC20(_swapData, _destinationChainId, _recipient); + } + /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract + /// @param _swapData The swap data struct + /// @param _destinationChainId the id of the chain where gas should be made available + /// @param _recipient the address to receive the gas on dst chain + function depositToGasZipERC20( + LibSwap.SwapData calldata _swapData, + uint256 _destinationChainId, + address _recipient + ) public { // execute the swapData that swaps the ERC20 token into native LibSwap.swap(0, _swapData); @@ -62,7 +73,7 @@ contract GasZipFacet { uint256 _amountToZip, uint256 _destinationChainId, address _recipient - ) public payable { + ) external payable { // call the gas zip router and deposit tokens gasZipRouter.deposit{ value: _amountToZip }( _destinationChainId, diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 43b0c6a04..8a3b07719 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -35,7 +35,7 @@ contract TestGasZipFacet is GasZipFacet { } } -contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { +contract GasZipFacetTest is DSTest, DiamondTest, TestHelpers { address public constant GAS_ZIP_ROUTER_MAINNET = 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; address internal ADDRESS_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; @@ -81,7 +81,7 @@ contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { defaultUSDCAmount = 10 * 10 ** usdc.decimals(); // 10 USDC // add genericSwapFacet (v1) to diamond (for gas usage comparison) - bytes4[] memory functionSelectors = new bytes4[](5); + bytes4[] memory functionSelectors = new bytes4[](6); functionSelectors[0] = gasZipFacet.depositToGasZipNative.selector; functionSelectors[1] = gasZipFacet.depositToGasZipERC20.selector; functionSelectors[2] = gasZipFacet.addDex.selector; @@ -89,6 +89,9 @@ contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { functionSelectors[4] = gasZipFacet .setFunctionApprovalBySignature .selector; + functionSelectors[5] = gasZipFacet + .depositToGasZipERC20WithDeposit + .selector; addFacet(diamond, address(gasZipFacet), functionSelectors); gasZipFacet = TestGasZipFacet(payable(address(diamond))); @@ -337,28 +340,7 @@ contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { }(bridgeData, swapData); } - function _getGnosisBridgeFacet() - internal - returns (TestGnosisBridgeFacet gnosisBridgeFacet) - { - gnosisBridgeFacet = new TestGnosisBridgeFacet( - IXDaiBridge(XDAI_BRIDGE) - ); - - bytes4[] memory functionSelectors = new bytes4[](2); - functionSelectors[0] = gnosisBridgeFacet - .startBridgeTokensViaXDaiBridge - .selector; - functionSelectors[1] = gnosisBridgeFacet - .swapAndStartBridgeTokensViaXDaiBridge - .selector; - - addFacet(diamond, address(gnosisBridgeFacet), functionSelectors); - - gnosisBridgeFacet = TestGnosisBridgeFacet(address(diamond)); - } - - function test_canSwapERC20ToNativeAndDeposit() public { + function test_canSwapERC20ToNativeAndDeposit_DirectCallToFacet() public { ( LibSwap.SwapData memory swapData, uint256 amountOutMin @@ -381,13 +363,34 @@ contract GasZipFacetV3Test is DSTest, DiamondTest, TestHelpers { ); // deposit via GasZip periphery contract - gasZipFacet.depositToGasZipERC20( + gasZipFacet.depositToGasZipERC20WithDeposit( swapData, defaultDestinationChains, defaultRecipientAddress ); } + function _getGnosisBridgeFacet() + internal + returns (TestGnosisBridgeFacet gnosisBridgeFacet) + { + gnosisBridgeFacet = new TestGnosisBridgeFacet( + IXDaiBridge(XDAI_BRIDGE) + ); + + bytes4[] memory functionSelectors = new bytes4[](2); + functionSelectors[0] = gnosisBridgeFacet + .startBridgeTokensViaXDaiBridge + .selector; + functionSelectors[1] = gnosisBridgeFacet + .swapAndStartBridgeTokensViaXDaiBridge + .selector; + + addFacet(diamond, address(gnosisBridgeFacet), functionSelectors); + + gnosisBridgeFacet = TestGnosisBridgeFacet(address(diamond)); + } + function _getUniswapCalldataForERC20ToNativeSwap( address sendingAssetId, uint256 fromAmount From c9a5a759b997e83d9edef2c8da7279668c971e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Fri, 28 Jun 2024 15:35:17 +0700 Subject: [PATCH 031/100] adds the usual startBridge... functions to the facet + tests --- src/Facets/GasZipFacet.sol | 125 ++++-- .../Facets/GasZipFacet.protocol.t.sol | 407 ++++++++++++++++++ test/solidity/Facets/GasZipFacet.t.sol | 384 +++-------------- test/solidity/Facets/MayanFacet.t.sol | 1 - test/solidity/utils/TestBaseFacet.sol | 3 + 5 files changed, 564 insertions(+), 356 deletions(-) create mode 100644 test/solidity/Facets/GasZipFacet.protocol.t.sol diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 2f172a57b..7436b3460 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -1,14 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; +import { ILiFi } from "../Interfaces/ILiFi.sol"; import { LibSwap } from "../Libraries/LibSwap.sol"; +import { LibAsset } from "../Libraries/LibAsset.sol"; +import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; +import { SwapperV2 } from "../Helpers/SwapperV2.sol"; +import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { ERC20 } from "solady/tokens/ERC20.sol"; import { NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; interface IGasZip { function deposit( - uint256 destinationChain, + uint256 destinationChains, address recipient ) external payable; } @@ -17,9 +22,17 @@ interface IGasZip { /// @author LI.FI (https://li.fi) /// @notice Provides functionality to swap ERC20 tokens to native and deposit them to the gas.zip protocol (https://www.gas.zip/) /// @custom:version 1.0.0 -contract GasZipFacet { +contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; + /// @dev GasZip-specific bridge data + /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) + /// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip + struct GasZipData { + uint256 destinationChains; + LibSwap.SwapData gasZipSwapData; + } + /// State /// IGasZip public immutable gasZipRouter; @@ -28,31 +41,64 @@ contract GasZipFacet { gasZipRouter = IGasZip(_gasZipRouter); } - /// @notice Pulls and swaps ERC20 tokens to native and then deposits these native tokens in the GasZip router contract - /// @param _swapData The swap data struct - /// @param _destinationChainId the id of the chain where gas should be made available - /// @param _recipient the address to receive the gas on dst chain - function depositToGasZipERC20WithDeposit( - LibSwap.SwapData calldata _swapData, - uint256 _destinationChainId, - address _recipient - ) external { - // pull tokens from caller (e.g. LI.FI diamond) - _swapData.sendingAssetId.safeTransferFrom( - msg.sender, - address(this), - _swapData.fromAmount + /// @notice Bridges tokens using the gas.zip protocol + /// @param _bridgeData The core information needed for bridging + /// @param _gasZipData GasZip-specific bridge data + function startBridgeTokensViaGasZip( + ILiFi.BridgeData memory _bridgeData, + GasZipData calldata _gasZipData + ) + external + payable + nonReentrant + refundExcessNative(payable(msg.sender)) + validateBridgeData(_bridgeData) + doesNotContainSourceSwaps(_bridgeData) + doesNotContainDestinationCalls(_bridgeData) + { + LibAsset.depositAsset( + _bridgeData.sendingAssetId, + _bridgeData.minAmount + ); + + _startBridge(_bridgeData, _gasZipData); + } + + /// @notice Performs a swap before bridging via the gas.zip protocol + /// @param _bridgeData The core information needed for bridging + /// @param _swapData An array of swap related data for performing swaps before bridging + /// @param _gasZipData GasZip-specific bridge data + function swapAndStartBridgeTokensViaGasZip( + ILiFi.BridgeData memory _bridgeData, + LibSwap.SwapData[] calldata _swapData, + GasZipData calldata _gasZipData + ) + external + payable + nonReentrant + refundExcessNative(payable(msg.sender)) + containsSourceSwaps(_bridgeData) + doesNotContainDestinationCalls(_bridgeData) + validateBridgeData(_bridgeData) + { + _bridgeData.minAmount = _depositAndSwap( + _bridgeData.transactionId, + _bridgeData.minAmount, + _swapData, + payable(msg.sender) ); - depositToGasZipERC20(_swapData, _destinationChainId, _recipient); + + _startBridge(_bridgeData, _gasZipData); } /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract - /// @param _swapData The swap data struct - /// @param _destinationChainId the id of the chain where gas should be made available + /// @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 _destinationChains a value that represents a list of chains to which gas should be distributed /// @param _recipient the address to receive the gas on dst chain function depositToGasZipERC20( LibSwap.SwapData calldata _swapData, - uint256 _destinationChainId, + uint256 _destinationChains, address _recipient ) public { // execute the swapData that swaps the ERC20 token into native @@ -60,24 +106,51 @@ contract GasZipFacet { // call the gas zip router and deposit tokens gasZipRouter.deposit{ value: address(this).balance }( - _destinationChainId, + _destinationChains, _recipient ); } /// @notice Deposits native tokens in the GasZip router contract - /// @param _amountToZip The swap data struct - /// @param _destinationChainId the id of the chain where gas should be made available + /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge + /// @param _amountToZip The amount to be deposited to the protocol + /// @param _destinationChains a value that represents a list of chains to which gas should be distributed /// @param _recipient the address to receive the gas on dst chain function depositToGasZipNative( uint256 _amountToZip, - uint256 _destinationChainId, + uint256 _destinationChains, address _recipient - ) external payable { + ) public payable { // call the gas zip router and deposit tokens gasZipRouter.deposit{ value: _amountToZip }( - _destinationChainId, + _destinationChains, _recipient ); } + + /// Internal Methods /// + + /// @dev Contains the business logic for the bridge via Gas.zip + /// @param _bridgeData The core information needed for bridging + /// @param _gasZipData GasZip-specific bridge data + function _startBridge( + ILiFi.BridgeData memory _bridgeData, + GasZipData calldata _gasZipData + ) internal { + // deposit to gas.zip depending on which asset type is being used + if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) + depositToGasZipNative( + _bridgeData.minAmount, + _gasZipData.destinationChains, + _bridgeData.receiver + ); + else + depositToGasZipERC20( + _gasZipData.gasZipSwapData, + _gasZipData.destinationChains, + _bridgeData.receiver + ); + + emit LiFiTransferStarted(_bridgeData); + } } diff --git a/test/solidity/Facets/GasZipFacet.protocol.t.sol b/test/solidity/Facets/GasZipFacet.protocol.t.sol new file mode 100644 index 000000000..a76c0eabe --- /dev/null +++ b/test/solidity/Facets/GasZipFacet.protocol.t.sol @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity 0.8.17; + +import { Test, DSTest } from "forge-std/Test.sol"; +import { console } from "../utils/Console.sol"; +import { DiamondTest, LiFiDiamond } from "../utils/DiamondTest.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; +import { LibSwap } from "lifi/Libraries/LibSwap.sol"; +import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; +import { FeeCollector } from "lifi/Periphery/FeeCollector.sol"; +import { ContractCallNotAllowed, CumulativeSlippageTooHigh, NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; + +import { UniswapV2Router02 } from "../utils/Interfaces.sol"; +import { TestHelpers, MockUniswapDEX, NonETHReceiver } from "../utils/TestHelpers.sol"; +import { ERC20 } from "solady/tokens/ERC20.sol"; +import { GnosisBridgeFacet } from "lifi/Facets/GnosisBridgeFacet.sol"; +import { IXDaiBridge } from "lifi/Interfaces/IXDaiBridge.sol"; +import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; + +// Stub GenericSwapFacet Contract +contract TestGasZipFacet is GasZipFacet { + constructor(address gasZipRouter) GasZipFacet(gasZipRouter) {} + + function addDex(address _dex) external { + LibAllowList.addAllowedContract(_dex); + } + + function removeDex(address _dex) external { + LibAllowList.removeAllowedContract(_dex); + } + + function setFunctionApprovalBySignature(bytes4 _signature) external { + LibAllowList.addAllowedSelector(_signature); + } +} + +contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { + address public constant GAS_ZIP_ROUTER_MAINNET = + 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; + address internal ADDRESS_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address internal ADDRESS_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address internal ADDRESS_DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address internal ADDRESS_UNISWAP = + 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; + address internal constant XDAI_BRIDGE = + 0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016; + + LiFiDiamond internal diamond; + TestGasZipFacet internal gasZipFacet; + ERC20 internal usdc; + ERC20 internal dai; + UniswapV2Router02 internal uniswap; + FeeCollector internal feeCollector; + + uint256 public defaultDestinationChains = 96; + address public defaultRecipientAddress = address(12345); + address public defaultRefundAddress = address(56789); + uint256 public defaultNativeAmount = 0.0006 ether; + uint256 public defaultUSDCAmount; + + event Deposit(address from, uint256 chains, uint256 amount, address to); + + function fork() internal { + string memory rpcUrl = vm.envString("ETH_NODE_URI_MAINNET"); + uint256 blockNumber = 20173181; + vm.createSelectFork(rpcUrl, blockNumber); + } + + function setUp() public { + fork(); + + // deploy contracts + diamond = createDiamond(); + gasZipFacet = new TestGasZipFacet(GAS_ZIP_ROUTER_MAINNET); + usdc = ERC20(ADDRESS_USDC); + dai = ERC20(ADDRESS_DAI); + uniswap = UniswapV2Router02(ADDRESS_UNISWAP); + feeCollector = new FeeCollector(address(this)); + + defaultUSDCAmount = 10 * 10 ** usdc.decimals(); // 10 USDC + + // add gasZipFacet to diamond + bytes4[] memory functionSelectors = new bytes4[](5); + functionSelectors[0] = gasZipFacet.depositToGasZipNative.selector; + functionSelectors[1] = gasZipFacet.depositToGasZipERC20.selector; + functionSelectors[2] = gasZipFacet.addDex.selector; + functionSelectors[3] = gasZipFacet.removeDex.selector; + functionSelectors[4] = gasZipFacet + .setFunctionApprovalBySignature + .selector; + addFacet(diamond, address(gasZipFacet), functionSelectors); + + gasZipFacet = TestGasZipFacet(payable(address(diamond))); + + // whitelist uniswap dex with function selectors + gasZipFacet.addDex(address(uniswap)); + gasZipFacet.setFunctionApprovalBySignature( + uniswap.swapExactTokensForTokens.selector + ); + gasZipFacet.setFunctionApprovalBySignature( + uniswap.swapExactETHForTokens.selector + ); + + vm.label(address(gasZipFacet), "LiFiDiamond"); + vm.label(ADDRESS_WETH, "WETH_TOKEN"); + vm.label(ADDRESS_USDC, "USDC_TOKEN"); + vm.label(ADDRESS_UNISWAP, "UNISWAP_V2_ROUTER"); + } + + function test_canDepositNative() public { + // set up expected event + vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); + emit Deposit( + address(gasZipFacet), + defaultDestinationChains, + defaultNativeAmount, + defaultRecipientAddress + ); + + // deposit via GasZip periphery contract + gasZipFacet.depositToGasZipNative{ value: defaultNativeAmount }( + defaultNativeAmount, + defaultDestinationChains, + defaultRecipientAddress + ); + } + + function test_canCollectERC20FeesThenSwapToERC20ThenDepositThenBridge() + public + { + // Testcase: + // 1. pay 1 USDC fee to FeeCollector in USDC + // 2. swap remaining (9) USDC to DAI + // 3. deposit 2 DAI to gasZip + // 4. bridge remaining DAI to Gnosis using GnosisBridgeFacet + + deal(ADDRESS_USDC, address(this), defaultUSDCAmount); + + // get swapData for feeCollection + LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](3); + uint256 feeCollectionAmount = 1 * 10 ** usdc.decimals(); // 1 USD + + swapData[0] = LibSwap.SwapData( + address(feeCollector), + address(feeCollector), + ADDRESS_USDC, + ADDRESS_USDC, + defaultUSDCAmount, + abi.encodeWithSelector( + feeCollector.collectTokenFees.selector, + ADDRESS_USDC, + feeCollectionAmount, + 0, + address(this) + ), + true + ); + + // get swapData for swap + uint256 swapInputAmount = defaultUSDCAmount - feeCollectionAmount; + // prepare swap data + address[] memory path = new address[](2); + path[0] = ADDRESS_USDC; + path[1] = ADDRESS_DAI; + + // Calculate USDC input amount + uint256[] memory amounts = uniswap.getAmountsOut( + swapInputAmount, + path + ); + uint256 swapOutputAmount = amounts[1]; + + swapData[1] = LibSwap.SwapData( + address(uniswap), + address(uniswap), + ADDRESS_USDC, + ADDRESS_DAI, + swapInputAmount, + abi.encodeWithSelector( + uniswap.swapExactTokensForTokens.selector, + swapInputAmount, + swapOutputAmount, + path, + address(diamond), + block.timestamp + 20 minutes + ), + false // not required since tokens are already in diamond + ); + + // // get swapData for gas zip + uint256 gasZipERC20Amount = 2 * 10 ** dai.decimals(); + ( + LibSwap.SwapData memory gasZipSwapData, + + ) = _getUniswapCalldataForERC20ToNativeSwap( + ADDRESS_DAI, + gasZipERC20Amount + ); + + swapData[2] = LibSwap.SwapData( + address(gasZipFacet), + address(gasZipFacet), + ADDRESS_DAI, + ADDRESS_DAI, + gasZipERC20Amount, + abi.encodeWithSelector( + gasZipFacet.depositToGasZipERC20.selector, + gasZipSwapData, + defaultDestinationChains, + defaultRecipientAddress + ), + false // not required since tokens are already in the diamond + ); + + // get BridgeData + ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({ + transactionId: "", + bridge: "GnosisBridge", + integrator: "", + referrer: address(0), + sendingAssetId: ADDRESS_DAI, + receiver: defaultRecipientAddress, + minAmount: swapOutputAmount - gasZipERC20Amount, + destinationChainId: 100, + hasSourceSwaps: true, + hasDestinationCall: false + }); + + // whitelist gasZipFacet and FeeCollector + gasZipFacet.addDex(address(gasZipFacet)); + gasZipFacet.setFunctionApprovalBySignature( + gasZipFacet.depositToGasZipERC20.selector + ); + gasZipFacet.addDex(address(feeCollector)); + gasZipFacet.setFunctionApprovalBySignature( + feeCollector.collectTokenFees.selector + ); + + // bridge using (standalone) GnosisBridgeFacet + TestGnosisBridgeFacet gnosisBridgeFacet = _getGnosisBridgeFacet(); + + // set approval for bridging + usdc.approve(address(gnosisBridgeFacet), defaultUSDCAmount); + + gnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge( + bridgeData, + swapData + ); + } + + function test_canDepositNativeThenSwapThenBridge() public { + // Testcase: + // 1. deposit small native amount to gasZip + // 2. swap remaining native to DAI + // 3. bridge remaining DAI to Gnosis using GnosisBridgeFacet + + uint256 nativeFromAmount = 1 ether; + + vm.deal(address(this), nativeFromAmount); + + uint256 nativeZipAmount = 1e14; + + // get swapData for gas zip + LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](2); + swapData[0] = LibSwap.SwapData( + address(gasZipFacet), + address(gasZipFacet), + address(0), + address(0), + nativeZipAmount, + abi.encodeWithSelector( + gasZipFacet.depositToGasZipNative.selector, + nativeZipAmount, + defaultDestinationChains, + defaultRecipientAddress + ), + false + ); + + // get swapData for swap + uint256 swapInputAmount = nativeFromAmount - nativeZipAmount; + + // prepare swap data + address[] memory path = new address[](2); + path[0] = ADDRESS_WETH; + path[1] = ADDRESS_DAI; + + // Calculate expected amountOut + uint256[] memory amounts = uniswap.getAmountsOut( + swapInputAmount, + path + ); + uint256 swapOutputAmount = amounts[1]; + + swapData[1] = LibSwap.SwapData( + address(uniswap), + address(uniswap), + address(0), + ADDRESS_DAI, + swapInputAmount, + abi.encodeWithSelector( + uniswap.swapExactETHForTokens.selector, + swapOutputAmount, + path, + address(diamond), + block.timestamp + 20 minutes + ), + false // not required since tokens are already in diamond + ); + + // get BridgeData + ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({ + transactionId: "", + bridge: "GnosisBridge", + integrator: "", + referrer: address(0), + sendingAssetId: ADDRESS_DAI, + receiver: defaultRecipientAddress, + minAmount: swapOutputAmount, + destinationChainId: 100, + hasSourceSwaps: true, + hasDestinationCall: false + }); + + // whitelist gasZipFacet and FeeCollector + gasZipFacet.addDex(address(gasZipFacet)); + gasZipFacet.setFunctionApprovalBySignature( + gasZipFacet.depositToGasZipNative.selector + ); + + // bridge using (standalone) GnosisBridgeFacet + TestGnosisBridgeFacet gnosisBridgeFacet = _getGnosisBridgeFacet(); + + gnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge{ + value: nativeFromAmount + }(bridgeData, swapData); + } + + function _getGnosisBridgeFacet() + internal + returns (TestGnosisBridgeFacet gnosisBridgeFacet) + { + gnosisBridgeFacet = new TestGnosisBridgeFacet( + IXDaiBridge(XDAI_BRIDGE) + ); + + bytes4[] memory functionSelectors = new bytes4[](2); + functionSelectors[0] = gnosisBridgeFacet + .startBridgeTokensViaXDaiBridge + .selector; + functionSelectors[1] = gnosisBridgeFacet + .swapAndStartBridgeTokensViaXDaiBridge + .selector; + + addFacet(diamond, address(gnosisBridgeFacet), functionSelectors); + + gnosisBridgeFacet = TestGnosisBridgeFacet(address(diamond)); + } + + function _getUniswapCalldataForERC20ToNativeSwap( + address sendingAssetId, + uint256 fromAmount + ) + internal + view + returns (LibSwap.SwapData memory swapData, uint256 amountOutMin) + { + // prepare swap data + address[] memory path = new address[](2); + path[0] = sendingAssetId; + path[1] = ADDRESS_WETH; + + // Calculate USDC input amount + uint256[] memory amounts = uniswap.getAmountsOut(fromAmount, path); + amountOutMin = amounts[1]; + + swapData = LibSwap.SwapData( + address(uniswap), + address(uniswap), + sendingAssetId, + ADDRESS_WETH, + fromAmount, + abi.encodeWithSelector( + uniswap.swapExactTokensForETH.selector, + fromAmount, + amountOutMin, + path, + address(gasZipFacet), + block.timestamp + 20 seconds + ), + false // not required since tokens are already in diamond + ); + } +} + +contract TestGnosisBridgeFacet is GnosisBridgeFacet { + constructor(IXDaiBridge _xDaiBridge) GnosisBridgeFacet(_xDaiBridge) {} + + function addDex(address _dex) external { + LibAllowList.addAllowedContract(_dex); + } + + function setFunctionApprovalBySignature(bytes4 _signature) external { + LibAllowList.addAllowedSelector(_signature); + } +} diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 8a3b07719..56bae1d10 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -1,22 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity 0.8.17; - -import { Test, DSTest } from "forge-std/Test.sol"; -import { console } from "../utils/Console.sol"; -import { DiamondTest, LiFiDiamond } from "../utils/DiamondTest.sol"; -import { Vm } from "forge-std/Vm.sol"; import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; -import { LibSwap } from "lifi/Libraries/LibSwap.sol"; -import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; -import { FeeCollector } from "lifi/Periphery/FeeCollector.sol"; -import { ContractCallNotAllowed, CumulativeSlippageTooHigh, NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; - -import { UniswapV2Router02 } from "../utils/Interfaces.sol"; -import { TestHelpers, MockUniswapDEX, NonETHReceiver } from "../utils/TestHelpers.sol"; -import { ERC20 } from "solady/tokens/ERC20.sol"; -import { GnosisBridgeFacet } from "lifi/Facets/GnosisBridgeFacet.sol"; -import { IXDaiBridge } from "lifi/Interfaces/IXDaiBridge.sol"; -import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; +import { ILiFi, LibSwap, LibAllowList, TestBaseFacet, console, ERC20 } from "../utils/TestBaseFacet.sol"; // Stub GenericSwapFacet Contract contract TestGasZipFacet is GasZipFacet { @@ -35,360 +20,109 @@ contract TestGasZipFacet is GasZipFacet { } } -contract GasZipFacetTest is DSTest, DiamondTest, TestHelpers { +contract GasZipFacetTest is TestBaseFacet { address public constant GAS_ZIP_ROUTER_MAINNET = 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; - address internal ADDRESS_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address internal ADDRESS_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address internal ADDRESS_DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; - address internal ADDRESS_UNISWAP = - 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; - address internal constant XDAI_BRIDGE = - 0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016; - LiFiDiamond internal diamond; TestGasZipFacet internal gasZipFacet; - ERC20 internal usdc; - ERC20 internal dai; - UniswapV2Router02 internal uniswap; - FeeCollector internal feeCollector; + GasZipFacet.GasZipData internal gasZipData; uint256 public defaultDestinationChains = 96; address public defaultRecipientAddress = address(12345); address public defaultRefundAddress = address(56789); - uint256 public defaultNativeAmount = 0.0006 ether; - uint256 public defaultUSDCAmount; + // uint256 public defaultNativeAmount = 0.0006 ether; event Deposit(address from, uint256 chains, uint256 amount, address to); - function fork() internal { - string memory rpcUrl = vm.envString("ETH_NODE_URI_MAINNET"); - uint256 blockNumber = 20173181; - vm.createSelectFork(rpcUrl, blockNumber); - } - function setUp() public { - fork(); + // set custom block no for mainnet forking + customBlockNumberForForking = 17484106; + + initTestBase(); // deploy contracts - diamond = createDiamond(); gasZipFacet = new TestGasZipFacet(GAS_ZIP_ROUTER_MAINNET); - usdc = ERC20(ADDRESS_USDC); - dai = ERC20(ADDRESS_DAI); - uniswap = UniswapV2Router02(ADDRESS_UNISWAP); - feeCollector = new FeeCollector(address(this)); - - defaultUSDCAmount = 10 * 10 ** usdc.decimals(); // 10 USDC - // add genericSwapFacet (v1) to diamond (for gas usage comparison) - bytes4[] memory functionSelectors = new bytes4[](6); - functionSelectors[0] = gasZipFacet.depositToGasZipNative.selector; - functionSelectors[1] = gasZipFacet.depositToGasZipERC20.selector; + // add gasZipFacet to diamond + bytes4[] memory functionSelectors = new bytes4[](5); + functionSelectors[0] = gasZipFacet.startBridgeTokensViaGasZip.selector; + functionSelectors[1] = gasZipFacet + .swapAndStartBridgeTokensViaGasZip + .selector; functionSelectors[2] = gasZipFacet.addDex.selector; functionSelectors[3] = gasZipFacet.removeDex.selector; functionSelectors[4] = gasZipFacet .setFunctionApprovalBySignature .selector; - functionSelectors[5] = gasZipFacet - .depositToGasZipERC20WithDeposit - .selector; addFacet(diamond, address(gasZipFacet), functionSelectors); gasZipFacet = TestGasZipFacet(payable(address(diamond))); // whitelist uniswap dex with function selectors gasZipFacet.addDex(address(uniswap)); + gasZipFacet.addDex(address(gasZipFacet)); gasZipFacet.setFunctionApprovalBySignature( uniswap.swapExactTokensForTokens.selector ); gasZipFacet.setFunctionApprovalBySignature( - uniswap.swapExactETHForTokens.selector - ); - - vm.label(address(gasZipFacet), "LiFiDiamond"); - vm.label(ADDRESS_WETH, "WETH_TOKEN"); - vm.label(ADDRESS_USDC, "USDC_TOKEN"); - vm.label(ADDRESS_UNISWAP, "UNISWAP_V2_ROUTER"); - } - - function test_canDepositNative() public { - // set up expected event - vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); - emit Deposit( - address(gasZipFacet), - defaultDestinationChains, - defaultNativeAmount, - defaultRecipientAddress - ); - - // deposit via GasZip periphery contract - gasZipFacet.depositToGasZipNative{ value: defaultNativeAmount }( - defaultNativeAmount, - defaultDestinationChains, - defaultRecipientAddress - ); - } - - function test_canCollectERC20FeesThenSwapToERC20ThenDepositThenBridge() - public - { - // Testcase: - // 1. pay 1 USDC fee to FeeCollector in USDC - // 2. swap remaining (9) USDC to DAI - // 3. deposit 2 DAI to gasZip - // 4. bridge remaining DAI to Gnosis using GnosisBridgeFacet - - deal(ADDRESS_USDC, address(this), defaultUSDCAmount); - - // get swapData for feeCollection - LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](3); - uint256 feeCollectionAmount = 1 * 10 ** usdc.decimals(); // 1 USD - - swapData[0] = LibSwap.SwapData( - address(feeCollector), - address(feeCollector), - ADDRESS_USDC, - ADDRESS_USDC, - defaultUSDCAmount, - abi.encodeWithSelector( - feeCollector.collectTokenFees.selector, - ADDRESS_USDC, - feeCollectionAmount, - 0, - address(this) - ), - true - ); - - // get swapData for swap - uint256 swapInputAmount = defaultUSDCAmount - feeCollectionAmount; - // prepare swap data - address[] memory path = new address[](2); - path[0] = ADDRESS_USDC; - path[1] = ADDRESS_DAI; - - // Calculate USDC input amount - uint256[] memory amounts = uniswap.getAmountsOut( - swapInputAmount, - path - ); - uint256 swapOutputAmount = amounts[1]; - - swapData[1] = LibSwap.SwapData( - address(uniswap), - address(uniswap), - ADDRESS_USDC, - ADDRESS_DAI, - swapInputAmount, - abi.encodeWithSelector( - uniswap.swapExactTokensForTokens.selector, - swapInputAmount, - swapOutputAmount, - path, - address(diamond), - block.timestamp + 20 minutes - ), - false // not required since tokens are already in diamond - ); - - // // get swapData for gas zip - uint256 gasZipERC20Amount = 2 * 10 ** dai.decimals(); - ( - LibSwap.SwapData memory gasZipSwapData, - - ) = _getUniswapCalldataForERC20ToNativeSwap( - ADDRESS_DAI, - gasZipERC20Amount - ); - - swapData[2] = LibSwap.SwapData( - address(gasZipFacet), - address(gasZipFacet), - ADDRESS_DAI, - ADDRESS_DAI, - gasZipERC20Amount, - abi.encodeWithSelector( - gasZipFacet.depositToGasZipERC20.selector, - gasZipSwapData, - defaultDestinationChains, - defaultRecipientAddress - ), - false // not required since tokens are already in the diamond + uniswap.swapTokensForExactETH.selector ); - - // get BridgeData - ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({ - transactionId: "", - bridge: "GnosisBridge", - integrator: "", - referrer: address(0), - sendingAssetId: ADDRESS_DAI, - receiver: defaultRecipientAddress, - minAmount: swapOutputAmount - gasZipERC20Amount, - destinationChainId: 100, - hasSourceSwaps: true, - hasDestinationCall: false - }); - - // whitelist gasZipFacet and FeeCollector - gasZipFacet.addDex(address(gasZipFacet)); gasZipFacet.setFunctionApprovalBySignature( - gasZipFacet.depositToGasZipERC20.selector + uniswap.swapETHForExactTokens.selector ); - gasZipFacet.addDex(address(feeCollector)); gasZipFacet.setFunctionApprovalBySignature( - feeCollector.collectTokenFees.selector - ); - - // bridge using (standalone) GnosisBridgeFacet - TestGnosisBridgeFacet gnosisBridgeFacet = _getGnosisBridgeFacet(); - - // set approval for bridging - usdc.approve(address(gnosisBridgeFacet), defaultUSDCAmount); - - gnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge( - bridgeData, - swapData - ); - } - - function test_canDepositNativeThenSwapThenBridge() public { - // Testcase: - // 1. deposit small native amount to gasZip - // 2. swap remaining native to DAI - // 3. bridge remaining DAI to Gnosis using GnosisBridgeFacet - - uint256 nativeFromAmount = 1 ether; - - vm.deal(address(this), nativeFromAmount); - - uint256 nativeZipAmount = 1e14; - - // get swapData for gas zip - LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](2); - swapData[0] = LibSwap.SwapData( - address(gasZipFacet), - address(gasZipFacet), - address(0), - address(0), - nativeZipAmount, - abi.encodeWithSelector( - gasZipFacet.depositToGasZipNative.selector, - nativeZipAmount, - defaultDestinationChains, - defaultRecipientAddress - ), - false - ); - - // get swapData for swap - uint256 swapInputAmount = nativeFromAmount - nativeZipAmount; - - // prepare swap data - address[] memory path = new address[](2); - path[0] = ADDRESS_WETH; - path[1] = ADDRESS_DAI; - - // Calculate expected amountOut - uint256[] memory amounts = uniswap.getAmountsOut( - swapInputAmount, - path - ); - uint256 swapOutputAmount = amounts[1]; - - swapData[1] = LibSwap.SwapData( - address(uniswap), - address(uniswap), - address(0), - ADDRESS_DAI, - swapInputAmount, - abi.encodeWithSelector( - uniswap.swapExactETHForTokens.selector, - swapOutputAmount, - path, - address(diamond), - block.timestamp + 20 minutes - ), - false // not required since tokens are already in diamond + gasZipFacet.depositToGasZipERC20.selector ); - - // get BridgeData - ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({ - transactionId: "", - bridge: "GnosisBridge", - integrator: "", - referrer: address(0), - sendingAssetId: ADDRESS_DAI, - receiver: defaultRecipientAddress, - minAmount: swapOutputAmount, - destinationChainId: 100, - hasSourceSwaps: true, - hasDestinationCall: false - }); - - // whitelist gasZipFacet and FeeCollector - gasZipFacet.addDex(address(gasZipFacet)); gasZipFacet.setFunctionApprovalBySignature( gasZipFacet.depositToGasZipNative.selector ); - // bridge using (standalone) GnosisBridgeFacet - TestGnosisBridgeFacet gnosisBridgeFacet = _getGnosisBridgeFacet(); - - gnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge{ - value: nativeFromAmount - }(bridgeData, swapData); - } - - function test_canSwapERC20ToNativeAndDeposit_DirectCallToFacet() public { ( - LibSwap.SwapData memory swapData, - uint256 amountOutMin + LibSwap.SwapData memory gasZipSwapData, + ) = _getUniswapCalldataForERC20ToNativeSwap( ADDRESS_USDC, defaultUSDCAmount ); - deal(ADDRESS_USDC, address(this), defaultUSDCAmount); - - ERC20(ADDRESS_USDC).approve(address(gasZipFacet), defaultUSDCAmount); + setFacetAddressInTestBase(address(gasZipFacet), "GasZipFacet"); - // set up expected event - vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); - emit Deposit( - address(gasZipFacet), - defaultDestinationChains, - amountOutMin, - defaultRecipientAddress - ); + // produce valid GasZipData + gasZipData = GasZipFacet.GasZipData({ + destinationChains: defaultDestinationChains, + gasZipSwapData: gasZipSwapData + }); - // deposit via GasZip periphery contract - gasZipFacet.depositToGasZipERC20WithDeposit( - swapData, - defaultDestinationChains, - defaultRecipientAddress - ); + vm.label(address(gasZipFacet), "LiFiDiamond"); + vm.label(ADDRESS_WETH, "WETH_TOKEN"); + vm.label(ADDRESS_USDC, "USDC_TOKEN"); + vm.label(ADDRESS_UNISWAP, "UNISWAP_V2_ROUTER"); } - function _getGnosisBridgeFacet() - internal - returns (TestGnosisBridgeFacet gnosisBridgeFacet) - { - gnosisBridgeFacet = new TestGnosisBridgeFacet( - IXDaiBridge(XDAI_BRIDGE) - ); - - bytes4[] memory functionSelectors = new bytes4[](2); - functionSelectors[0] = gnosisBridgeFacet - .startBridgeTokensViaXDaiBridge - .selector; - functionSelectors[1] = gnosisBridgeFacet - .swapAndStartBridgeTokensViaXDaiBridge - .selector; - - addFacet(diamond, address(gnosisBridgeFacet), functionSelectors); + function initiateBridgeTxWithFacet(bool isNative) internal override { + if (isNative) { + gasZipFacet.startBridgeTokensViaGasZip{ + value: bridgeData.minAmount + }(bridgeData, gasZipData); + } else { + gasZipFacet.startBridgeTokensViaGasZip(bridgeData, gasZipData); + } + } - gnosisBridgeFacet = TestGnosisBridgeFacet(address(diamond)); + function initiateSwapAndBridgeTxWithFacet( + bool isNative + ) internal override { + if (isNative) { + gasZipFacet.swapAndStartBridgeTokensViaGasZip{ + value: swapData[0].fromAmount + }(bridgeData, swapData, gasZipData); + } else { + gasZipFacet.swapAndStartBridgeTokensViaGasZip( + bridgeData, + swapData, + gasZipData + ); + } } function _getUniswapCalldataForERC20ToNativeSwap( @@ -425,16 +159,8 @@ contract GasZipFacetTest is DSTest, DiamondTest, TestHelpers { false // not required since tokens are already in diamond ); } -} -contract TestGnosisBridgeFacet is GnosisBridgeFacet { - constructor(IXDaiBridge _xDaiBridge) GnosisBridgeFacet(_xDaiBridge) {} - - function addDex(address _dex) external { - LibAllowList.addAllowedContract(_dex); - } - - function setFunctionApprovalBySignature(bytes4 _signature) external { - LibAllowList.addAllowedSelector(_signature); + function testBase_CanBridgeTokens_fuzzed(uint256 amount) public override { + // deactivated for this facet since we would have to update the calldata that swaps from ERC20 to native for every amount } } diff --git a/test/solidity/Facets/MayanFacet.t.sol b/test/solidity/Facets/MayanFacet.t.sol index a958abf2b..b526bc264 100644 --- a/test/solidity/Facets/MayanFacet.t.sol +++ b/test/solidity/Facets/MayanFacet.t.sol @@ -285,7 +285,6 @@ contract MayanFacetTest is TestBaseFacet { validMayanData = invalidMayanDataEVM2Solana; vm.startPrank(USER_SENDER); - console.log(USER_RECEIVER); usdc.approve(_facetTestContractAddress, type(uint256).max); vm.expectRevert( diff --git a/test/solidity/utils/TestBaseFacet.sol b/test/solidity/utils/TestBaseFacet.sol index 174f71b12..b4ad84292 100644 --- a/test/solidity/utils/TestBaseFacet.sol +++ b/test/solidity/utils/TestBaseFacet.sol @@ -79,6 +79,9 @@ abstract contract TestBaseFacet is TestBase { bridgeData.sendingAssetId = ADDRESS_USDC; bridgeData.minAmount = amount; + console.log("minAmount : ", bridgeData.minAmount); + console.log(" amount : ", amount); + console.log(" balance: ", usdc.balanceOf(USER_SENDER)); //prepare check for events vm.expectEmit(true, true, true, true, _facetTestContractAddress); emit LiFiTransferStarted(bridgeData); From fd55da5b748097b05ac8c656e68ea465c4f204cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Fri, 28 Jun 2024 20:36:27 +0700 Subject: [PATCH 032/100] adds a amountOutMin parameter for ERC20 deposit function --- src/Facets/GasZipFacet.sol | 15 ++++++++++----- test/solidity/Facets/GasZipFacet.protocol.t.sol | 7 ++++--- test/solidity/Facets/GasZipFacet.t.sol | 5 +++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 7436b3460..dfa866347 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -28,9 +28,11 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @dev GasZip-specific bridge data /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) /// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip + /// @param amountOutMin (only required for ERC20 tokens): the native amount we expect to receive from swap and plan to deposit to gas.zip struct GasZipData { uint256 destinationChains; LibSwap.SwapData gasZipSwapData; + uint256 amountOutMin; } /// State /// @@ -94,18 +96,20 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract /// @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 _destinationChains a value that represents a list of chains to which gas should be distributed - /// @param _recipient the address to receive the gas on dst chain + /// @param _destinationChains A value that represents a list of chains to which gas should be distributed + /// @param _recipient The address to receive the gas on dst chain + /// @param _amountOutMin The native amount we expect to receive from swap and plan to deposit to gas.zip function depositToGasZipERC20( LibSwap.SwapData calldata _swapData, uint256 _destinationChains, - address _recipient + address _recipient, + uint256 _amountOutMin ) public { // execute the swapData that swaps the ERC20 token into native LibSwap.swap(0, _swapData); // call the gas zip router and deposit tokens - gasZipRouter.deposit{ value: address(this).balance }( + gasZipRouter.deposit{ value: _amountOutMin }( _destinationChains, _recipient ); @@ -148,7 +152,8 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { depositToGasZipERC20( _gasZipData.gasZipSwapData, _gasZipData.destinationChains, - _bridgeData.receiver + _bridgeData.receiver, + _gasZipData.amountOutMin ); emit LiFiTransferStarted(_bridgeData); diff --git a/test/solidity/Facets/GasZipFacet.protocol.t.sol b/test/solidity/Facets/GasZipFacet.protocol.t.sol index a76c0eabe..b12726f90 100644 --- a/test/solidity/Facets/GasZipFacet.protocol.t.sol +++ b/test/solidity/Facets/GasZipFacet.protocol.t.sol @@ -157,7 +157,7 @@ contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { true ); - // get swapData for swap + // get swapData for USDC to DAI swap uint256 swapInputAmount = defaultUSDCAmount - feeCollectionAmount; // prepare swap data address[] memory path = new address[](2); @@ -192,7 +192,7 @@ contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { uint256 gasZipERC20Amount = 2 * 10 ** dai.decimals(); ( LibSwap.SwapData memory gasZipSwapData, - + uint256 amountOutMinGasZipSwap ) = _getUniswapCalldataForERC20ToNativeSwap( ADDRESS_DAI, gasZipERC20Amount @@ -208,7 +208,8 @@ contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { gasZipFacet.depositToGasZipERC20.selector, gasZipSwapData, defaultDestinationChains, - defaultRecipientAddress + defaultRecipientAddress, + amountOutMinGasZipSwap ), false // not required since tokens are already in the diamond ); diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 56bae1d10..aab42747f 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -79,7 +79,7 @@ contract GasZipFacetTest is TestBaseFacet { ( LibSwap.SwapData memory gasZipSwapData, - + uint256 amountOutMin ) = _getUniswapCalldataForERC20ToNativeSwap( ADDRESS_USDC, defaultUSDCAmount @@ -90,7 +90,8 @@ contract GasZipFacetTest is TestBaseFacet { // produce valid GasZipData gasZipData = GasZipFacet.GasZipData({ destinationChains: defaultDestinationChains, - gasZipSwapData: gasZipSwapData + gasZipSwapData: gasZipSwapData, + amountOutMin: amountOutMin }); vm.label(address(gasZipFacet), "LiFiDiamond"); From 22ef3433ce4565fb3af567256a7f7afa20a00e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 1 Jul 2024 09:37:55 +0700 Subject: [PATCH 033/100] redeployed gasZipFacet to bsc staging --- deployments/_deployments_log_file.json | 6 +++--- deployments/bsc.diamond.staging.json | 10 +++++----- deployments/bsc.staging.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index a98fe21de..e75cf2aab 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -19436,12 +19436,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0xf92D157a857d55B5DA987E35488e746EA85f1ca3", + "ADDRESS": "0x70cD2f8937aF9599583c18042C63704DfF3B67f6", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-06-27 09:53:54", + "TIMESTAMP": "2024-06-28 20:55:43", "CONSTRUCTOR_ARGS": "0x00000000000000000000000085e5fb57844be79b42997c898d177a39f328ccf0", "SALT": "", - "VERIFIED": "true" + "VERIFIED": "false" } ] } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index a0faf5e21..a6b9b1119 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -17,9 +17,9 @@ "Name": "WithdrawFacet", "Version": "1.0.0" }, - "0xB94Fd26F6b138E1bE6CfEa5Ec4F67C5573F5d6AD": { - "Name": "DexManagerFacet", - "Version": "1.0.0" + "0x5c9cbc3FB4FF588199BFbE68b72Fc127dd0D8630": { + "Name": "", + "Version": "" }, "0x1A841931913806FB7570B43bcD64A487A8E7A50c": { "Name": "AccessManagerFacet", @@ -45,7 +45,7 @@ "Name": "", "Version": "" }, - "0xb179bfa49d8B5d272288C59b26ac93f036735790": { + "0xd596C903d78870786c5DB0E448ce7F87A65A0daD": { "Name": "", "Version": "" }, @@ -69,7 +69,7 @@ "Name": "GenericSwapFacet", "Version": "1.0.0" }, - "0xf92D157a857d55B5DA987E35488e746EA85f1ca3": { + "0x70cD2f8937aF9599583c18042C63704DfF3B67f6": { "Name": "GasZipFacet", "Version": "1.0.0" } diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index cd6866a8c..93554fe4a 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -28,5 +28,5 @@ "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A", "MayanBridgeFacet": "0x5Ba4FeD1DAd2fD057A9f687B399B8e4cF2368214", "MayanFacet": "0xcED4B608A468ce334c75c6652e18E2Ba7f3F44dA", - "GasZipFacet": "0xf92D157a857d55B5DA987E35488e746EA85f1ca3" + "GasZipFacet": "0x70cD2f8937aF9599583c18042C63704DfF3B67f6" } \ No newline at end of file From d7d9fe3557e252807385f6436d3afa1b7ff0f8a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 1 Jul 2024 11:30:11 +0700 Subject: [PATCH 034/100] feat: removes destinationChains parameter from GasZipData --- lib/solmate | 2 +- src/Facets/GasZipFacet.sol | 10 ++++------ test/solidity/Facets/GasZipFacet.t.sol | 1 - 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/solmate b/lib/solmate index c89230993..2001af43a 160000 --- a/lib/solmate +++ b/lib/solmate @@ -1 +1 @@ -Subproject commit c892309933b25c03d32b1b0d674df7ae292ba925 +Subproject commit 2001af43aedb46fdc2335d2a7714fb2dae7cfcd1 diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index dfa866347..91cd29ad4 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -26,11 +26,9 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; /// @dev GasZip-specific bridge data - /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) /// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip /// @param amountOutMin (only required for ERC20 tokens): the native amount we expect to receive from swap and plan to deposit to gas.zip struct GasZipData { - uint256 destinationChains; LibSwap.SwapData gasZipSwapData; uint256 amountOutMin; } @@ -96,7 +94,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract /// @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 _destinationChains A value that represents a list of chains to which gas should be distributed + /// @param _destinationChains A value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) /// @param _recipient The address to receive the gas on dst chain /// @param _amountOutMin The native amount we expect to receive from swap and plan to deposit to gas.zip function depositToGasZipERC20( @@ -118,7 +116,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @notice Deposits native tokens in the GasZip router contract /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge /// @param _amountToZip The amount to be deposited to the protocol - /// @param _destinationChains a value that represents a list of chains to which gas should be distributed + /// @param _destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) /// @param _recipient the address to receive the gas on dst chain function depositToGasZipNative( uint256 _amountToZip, @@ -145,13 +143,13 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) depositToGasZipNative( _bridgeData.minAmount, - _gasZipData.destinationChains, + _bridgeData.destinationChainId, _bridgeData.receiver ); else depositToGasZipERC20( _gasZipData.gasZipSwapData, - _gasZipData.destinationChains, + _bridgeData.destinationChainId, _bridgeData.receiver, _gasZipData.amountOutMin ); diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index aab42747f..49494642f 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -89,7 +89,6 @@ contract GasZipFacetTest is TestBaseFacet { // produce valid GasZipData gasZipData = GasZipFacet.GasZipData({ - destinationChains: defaultDestinationChains, gasZipSwapData: gasZipSwapData, amountOutMin: amountOutMin }); From 29b8472b85060cb7c10ef0ae29a1a52a2f51e277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 1 Jul 2024 11:44:29 +0700 Subject: [PATCH 035/100] docs: updates GasZipFacet docs --- docs/GasZipFacet.md | 52 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/docs/GasZipFacet.md b/docs/GasZipFacet.md index dcbe9fbbc..fbebc9555 100644 --- a/docs/GasZipFacet.md +++ b/docs/GasZipFacet.md @@ -2,12 +2,25 @@ ## Description -The GasZipFacet provides function to deposit ERC20 and native tokens to the Gas.zip protocol (https://www.gas.zip/) +The GasZipFacet offers functionality to send native gas tokens to another chain using the Gas.Zip protocol (https://www.gas.zip/) +It can be used as a regular bridge facet or as a (LibSwap.SwapData) swap step. + +If used as a regular bridge facet, it will either execute one or more swap steps prior to bridging, or bridge directly. +If used as a swap step, it can be combined with any other bridge as a prior step before bridging. +This allows for maximum flexibility when it comes to sending gas to another chain. ## How To Use -The contract provides two public methods: +### Functions for bridging + +- `function startBridgeTokensViaGasZip(BridgeData memory _bridgeData, GasZipData calldata _gasZipData)` + - Simply bridges tokens using GasZipFacet +- `function swapAndStartBridgeTokensViaGasZip(BridgeData memory _bridgeData, SwapData[] calldata _swapData, GasZipData calldata _gasZipData)` + - Performs swap(s) before bridging tokens using GasZipFacet +### Functions for using this facet as a LibSwap.SwapData step + +The contract provides two public methods: One for ERC20 tokens (these will be swapped into native before depositing to gas.zip) ```solidity @@ -22,7 +35,7 @@ function depositToGasZipERC20( ) ``` -and another for Native tokens (these will be directly deposited) +and another for native tokens (these will be directly deposited) ```solidity /// @notice Deposits native tokens in the GasZip router contract @@ -35,3 +48,36 @@ function depositToGasZipNative( address _recipient ) ``` + +## Bridge Specific Parameters + +Some of the methods listed above take a variable labeled `_gasZipData`. + +This data is specific to Gas.Zip and is represented as the following struct type: + +```solidity +/// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip +/// @param amountOutMin (only required for ERC20 tokens): the native amount we expect to receive from swap and plan to deposit to gas.zip +struct GasZipData { + LibSwap.SwapData gasZipSwapData; + uint256 amountOutMin; +} +``` + +## Swap Data + +Some methods accept a `SwapData _swapData` parameter. + +Swapping is performed by a swap specific library that expects an array of calldata to can be run on various DEXs (i.e. Uniswap) to make one or multiple swaps before performing another action. + +The swap library can be found [here](../src/Libraries/LibSwap.sol). + +## LiFi Data + +Most of the methods accept a `BridgeData _bridgeData` parameter. + +The facet uses the `destinationChainId` parameter to determine which chain to send gas to. +It will send the `minAmount` to this chain (or convert it to native before in case of ERC20). +The funds will be sent to the `receiver` address. + +The `_bridgeData` also used to emit events that we can later track and index in our subgraphs and provide data on how our contracts are being used. `BridgeData` and the events we can emit can be found [here](../src/Interfaces/ILiFi.sol). From 2683fc04a178127fe3225f08437c97eef24d225f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 2 Jul 2024 11:17:34 +0700 Subject: [PATCH 036/100] updates facet (startBridge => nativeOnly) --- src/Facets/GasZipFacet.sol | 55 ++++++++++++-------------- test/solidity/Facets/GasZipFacet.t.sol | 1 + 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 91cd29ad4..92e2db29b 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -26,9 +26,11 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; /// @dev GasZip-specific bridge data + /// @param gasZipChainId The Gas.zip-specific chainId of the chain on which gas should be received on (https://dev.gas1.zip/gas/chain-support/outbound) /// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip /// @param amountOutMin (only required for ERC20 tokens): the native amount we expect to receive from swap and plan to deposit to gas.zip struct GasZipData { + uint256 gasZipChainId; LibSwap.SwapData gasZipSwapData; uint256 amountOutMin; } @@ -42,6 +44,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { } /// @notice Bridges tokens using the gas.zip protocol + /// @dev this function only supports native flow. For ERC20 flows this facet should be used as a protocol step instead /// @param _bridgeData The core information needed for bridging /// @param _gasZipData GasZip-specific bridge data function startBridgeTokensViaGasZip( @@ -61,7 +64,13 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { _bridgeData.minAmount ); - _startBridge(_bridgeData, _gasZipData); + depositToGasZipNative( + _bridgeData.minAmount, + _gasZipData.gasZipChainId, + _bridgeData.receiver + ); + + emit LiFiTransferStarted(_bridgeData); } /// @notice Performs a swap before bridging via the gas.zip protocol @@ -88,7 +97,22 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { payable(msg.sender) ); - _startBridge(_bridgeData, _gasZipData); + // deposit to gas.zip depending on which asset type is being used + if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) + depositToGasZipNative( + _bridgeData.minAmount, + _gasZipData.gasZipChainId, + _bridgeData.receiver + ); + else + depositToGasZipERC20( + _gasZipData.gasZipSwapData, + _gasZipData.gasZipChainId, + _bridgeData.receiver, + _gasZipData.amountOutMin + ); + + emit LiFiTransferStarted(_bridgeData); } /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract @@ -129,31 +153,4 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { _recipient ); } - - /// Internal Methods /// - - /// @dev Contains the business logic for the bridge via Gas.zip - /// @param _bridgeData The core information needed for bridging - /// @param _gasZipData GasZip-specific bridge data - function _startBridge( - ILiFi.BridgeData memory _bridgeData, - GasZipData calldata _gasZipData - ) internal { - // deposit to gas.zip depending on which asset type is being used - if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) - depositToGasZipNative( - _bridgeData.minAmount, - _bridgeData.destinationChainId, - _bridgeData.receiver - ); - else - depositToGasZipERC20( - _gasZipData.gasZipSwapData, - _bridgeData.destinationChainId, - _bridgeData.receiver, - _gasZipData.amountOutMin - ); - - emit LiFiTransferStarted(_bridgeData); - } } diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 49494642f..b2d9c1427 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -89,6 +89,7 @@ contract GasZipFacetTest is TestBaseFacet { // produce valid GasZipData gasZipData = GasZipFacet.GasZipData({ + gasZipChainId: 17, // Polygon (https://dev.gas1.zip/gas/chain-support/outbound) gasZipSwapData: gasZipSwapData, amountOutMin: amountOutMin }); From c488dc1d27edc58ce6d62007aa3ee0748965f9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 2 Jul 2024 11:31:47 +0700 Subject: [PATCH 037/100] replaces amountOutMin parameter with balance checks & updates tests --- src/Facets/GasZipFacet.sol | 26 +++++++++++++------------- test/solidity/Facets/GasZipFacet.t.sol | 24 +++++++++++++++++++++--- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 92e2db29b..ad7a4ab16 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -9,7 +9,7 @@ import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { ERC20 } from "solady/tokens/ERC20.sol"; -import { NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; +import { NativeAssetTransferFailed, InvalidCallData } from "lifi/Errors/GenericErrors.sol"; interface IGasZip { function deposit( @@ -28,11 +28,9 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @dev GasZip-specific bridge data /// @param gasZipChainId The Gas.zip-specific chainId of the chain on which gas should be received on (https://dev.gas1.zip/gas/chain-support/outbound) /// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip - /// @param amountOutMin (only required for ERC20 tokens): the native amount we expect to receive from swap and plan to deposit to gas.zip struct GasZipData { uint256 gasZipChainId; LibSwap.SwapData gasZipSwapData; - uint256 amountOutMin; } /// State /// @@ -59,10 +57,8 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { doesNotContainSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) { - LibAsset.depositAsset( - _bridgeData.sendingAssetId, - _bridgeData.minAmount - ); + if (!LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) + revert InvalidCallData(); depositToGasZipNative( _bridgeData.minAmount, @@ -108,8 +104,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { depositToGasZipERC20( _gasZipData.gasZipSwapData, _gasZipData.gasZipChainId, - _bridgeData.receiver, - _gasZipData.amountOutMin + _bridgeData.receiver ); emit LiFiTransferStarted(_bridgeData); @@ -120,18 +115,23 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @param _swapData The swap data that executes the swap from ERC20 to native /// @param _destinationChains A value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) /// @param _recipient The address to receive the gas on dst chain - /// @param _amountOutMin The native amount we expect to receive from swap and plan to deposit to gas.zip function depositToGasZipERC20( LibSwap.SwapData calldata _swapData, uint256 _destinationChains, - address _recipient, - uint256 _amountOutMin + address _recipient ) public { + // get the current native balance + uint256 currentNativeBalance = address(this).balance; + // execute the swapData that swaps the ERC20 token into native LibSwap.swap(0, _swapData); + // calculate the swap output amount using the initial native balance + uint256 swapOutputAmount = address(this).balance - + currentNativeBalance; + // call the gas zip router and deposit tokens - gasZipRouter.deposit{ value: _amountOutMin }( + gasZipRouter.deposit{ value: swapOutputAmount }( _destinationChains, _recipient ); diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index b2d9c1427..f60883dcc 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -79,7 +79,7 @@ contract GasZipFacetTest is TestBaseFacet { ( LibSwap.SwapData memory gasZipSwapData, - uint256 amountOutMin + ) = _getUniswapCalldataForERC20ToNativeSwap( ADDRESS_USDC, defaultUSDCAmount @@ -90,8 +90,7 @@ contract GasZipFacetTest is TestBaseFacet { // produce valid GasZipData gasZipData = GasZipFacet.GasZipData({ gasZipChainId: 17, // Polygon (https://dev.gas1.zip/gas/chain-support/outbound) - gasZipSwapData: gasZipSwapData, - amountOutMin: amountOutMin + gasZipSwapData: gasZipSwapData }); vm.label(address(gasZipFacet), "LiFiDiamond"); @@ -164,4 +163,23 @@ contract GasZipFacetTest is TestBaseFacet { function testBase_CanBridgeTokens_fuzzed(uint256 amount) public override { // deactivated for this facet since we would have to update the calldata that swaps from ERC20 to native for every amount } + + function testBase_CanBridgeTokens() public override { + // the startBridgeTokensViaGasZip can only be used for native tokens, therefore we need to adapt this test case + vm.startPrank(USER_SENDER); + + // update bridgeData to use native + bridgeData.sendingAssetId = address(0); + + //prepare check for events + vm.expectEmit(true, true, true, true, _facetTestContractAddress); + emit LiFiTransferStarted(bridgeData); + + initiateBridgeTxWithFacet(true); + vm.stopPrank(); + } + + function testBase_Revert_CallerHasInsufficientFunds() public override { + // the startBridgeTokensViaGasZip can only be used for native tokens, therefore this test case is not applicable + } } From 3f37ddf920bb3adb6df672c9a0dd3dfc69b2b0b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 2 Jul 2024 11:32:56 +0700 Subject: [PATCH 038/100] updates docs --- docs/GasZipFacet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GasZipFacet.md b/docs/GasZipFacet.md index fbebc9555..b4d1de156 100644 --- a/docs/GasZipFacet.md +++ b/docs/GasZipFacet.md @@ -14,7 +14,7 @@ This allows for maximum flexibility when it comes to sending gas to another chai ### Functions for bridging - `function startBridgeTokensViaGasZip(BridgeData memory _bridgeData, GasZipData calldata _gasZipData)` - - Simply bridges tokens using GasZipFacet + - Simply bridges tokens using GasZipFacet (can only be used for native tokens) - `function swapAndStartBridgeTokensViaGasZip(BridgeData memory _bridgeData, SwapData[] calldata _swapData, GasZipData calldata _gasZipData)` - Performs swap(s) before bridging tokens using GasZipFacet From 261b93d2547f8dbb1299d679259529c419bb4fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 2 Jul 2024 12:18:46 +0700 Subject: [PATCH 039/100] fixes typo in URL and adds link to config file --- config/gaszip.json | 1 + lib/solmate | 2 +- src/Facets/GasZipFacet.sol | 2 +- test/solidity/Facets/GasZipFacet.t.sol | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config/gaszip.json b/config/gaszip.json index 92050cbdd..9cc90ff28 100644 --- a/config/gaszip.json +++ b/config/gaszip.json @@ -1,5 +1,6 @@ { "gasZipRouters": { + "---ListOfRouterAddresses---": "https://dev.gas.zip/gas/chain-support/inbound", "mainnet": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", "arbitrum": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", "blast": "0x6d4Ac5Ba1D649030227718C05ca4399F8522828b", diff --git a/lib/solmate b/lib/solmate index c89230993..2001af43a 160000 --- a/lib/solmate +++ b/lib/solmate @@ -1 +1 @@ -Subproject commit c892309933b25c03d32b1b0d674df7ae292ba925 +Subproject commit 2001af43aedb46fdc2335d2a7714fb2dae7cfcd1 diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index ad7a4ab16..db9f65d82 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -26,7 +26,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; /// @dev GasZip-specific bridge data - /// @param gasZipChainId The Gas.zip-specific chainId of the chain on which gas should be received on (https://dev.gas1.zip/gas/chain-support/outbound) + /// @param gasZipChainId The Gas.zip-specific chainId of the chain on which gas should be received on (https://dev.gas.zip/gas/chain-support/outbound) /// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip struct GasZipData { uint256 gasZipChainId; diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index f60883dcc..7e6959a77 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -89,7 +89,7 @@ contract GasZipFacetTest is TestBaseFacet { // produce valid GasZipData gasZipData = GasZipFacet.GasZipData({ - gasZipChainId: 17, // Polygon (https://dev.gas1.zip/gas/chain-support/outbound) + gasZipChainId: 17, // Polygon (https://dev.gas.zip/gas/chain-support/outbound) gasZipSwapData: gasZipSwapData }); From 71c324e9eb9c44dbeaa4a9631f17973b70f46ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 3 Jul 2024 09:37:17 +0700 Subject: [PATCH 040/100] restricts swapAndBridge.. function to native only + updates tests --- src/Facets/GasZipFacet.sol | 27 ++++---- test/solidity/Facets/GasZipFacet.t.sol | 89 +++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 17 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index db9f65d82..c103381af 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -27,10 +27,8 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @dev GasZip-specific bridge data /// @param gasZipChainId The Gas.zip-specific chainId of the chain on which gas should be received on (https://dev.gas.zip/gas/chain-support/outbound) - /// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip struct GasZipData { uint256 gasZipChainId; - LibSwap.SwapData gasZipSwapData; } /// State /// @@ -57,6 +55,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { doesNotContainSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) { + // this function shall only be used for native assets if (!LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) revert InvalidCallData(); @@ -86,6 +85,11 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { doesNotContainDestinationCalls(_bridgeData) validateBridgeData(_bridgeData) { + // this function shall only be used for ERC20 assets + if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) + revert InvalidCallData(); + + // deposit and swap ERC20 tokens _bridgeData.minAmount = _depositAndSwap( _bridgeData.transactionId, _bridgeData.minAmount, @@ -93,19 +97,12 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { payable(msg.sender) ); - // deposit to gas.zip depending on which asset type is being used - if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) - depositToGasZipNative( - _bridgeData.minAmount, - _gasZipData.gasZipChainId, - _bridgeData.receiver - ); - else - depositToGasZipERC20( - _gasZipData.gasZipSwapData, - _gasZipData.gasZipChainId, - _bridgeData.receiver - ); + // deposit to gas.zip + depositToGasZipNative( + _bridgeData.minAmount, + _gasZipData.gasZipChainId, + _bridgeData.receiver + ); emit LiFiTransferStarted(_bridgeData); } diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 7e6959a77..954d6a0a1 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.17; import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; import { ILiFi, LibSwap, LibAllowList, TestBaseFacet, console, ERC20 } from "../utils/TestBaseFacet.sol"; +import { InvalidCallData } from "lifi/Errors/GenericErrors.sol"; // Stub GenericSwapFacet Contract contract TestGasZipFacet is GasZipFacet { @@ -89,10 +90,11 @@ contract GasZipFacetTest is TestBaseFacet { // produce valid GasZipData gasZipData = GasZipFacet.GasZipData({ - gasZipChainId: 17, // Polygon (https://dev.gas.zip/gas/chain-support/outbound) - gasZipSwapData: gasZipSwapData + gasZipChainId: 17 // Polygon (https://dev.gas.zip/gas/chain-support/outbound) }); + bridgeData.bridge = "GasZip"; + vm.label(address(gasZipFacet), "LiFiDiamond"); vm.label(ADDRESS_WETH, "WETH_TOKEN"); vm.label(ADDRESS_USDC, "USDC_TOKEN"); @@ -182,4 +184,87 @@ contract GasZipFacetTest is TestBaseFacet { function testBase_Revert_CallerHasInsufficientFunds() public override { // the startBridgeTokensViaGasZip can only be used for native tokens, therefore this test case is not applicable } + + function testBase_CanSwapAndBridgeNativeTokens() public override { + // the swapAndStartBridgeTokensViaGasZip can only be used for ERC20 tokens + // therefore this test is expected to revert with InvalidCallData() + vm.startPrank(USER_SENDER); + + // prepare bridgeData + bridgeData.hasSourceSwaps = true; + bridgeData.sendingAssetId = address(0); + + // prepare swap data + address[] memory path = new address[](2); + path[0] = ADDRESS_USDC; + path[1] = ADDRESS_WETH; + + uint256 amountOut = defaultNativeAmount; + + // Calculate USDC input amount + uint256[] memory amounts = uniswap.getAmountsIn(amountOut, path); + uint256 amountIn = amounts[0]; + + bridgeData.minAmount = amountOut; + + delete swapData; + swapData.push( + LibSwap.SwapData({ + callTo: address(uniswap), + approveTo: address(uniswap), + sendingAssetId: ADDRESS_USDC, + receivingAssetId: address(0), + fromAmount: amountIn, + callData: abi.encodeWithSelector( + uniswap.swapTokensForExactETH.selector, + amountOut, + amountIn, + path, + _facetTestContractAddress, + block.timestamp + 20 minutes + ), + requiresDeposit: true + }) + ); + + // approval + usdc.approve(_facetTestContractAddress, amountIn); + + vm.expectRevert(InvalidCallData.selector); + + // execute call in child contract + initiateSwapAndBridgeTxWithFacet(false); + } + + function testBase_CanSwapAndBridgeTokens() public override { + vm.startPrank(USER_SENDER); + + // prepare bridgeData + bridgeData.hasSourceSwaps = true; + bridgeData.minAmount = defaultNativeAmount; + + // reset swap data + setDefaultSwapDataSingleDAItoETH(); + + // approval + dai.approve(_facetTestContractAddress, swapData[0].fromAmount); + + //prepare check for events + vm.expectEmit(true, true, true, true, _facetTestContractAddress); + emit AssetSwapped( + bridgeData.transactionId, + ADDRESS_UNISWAP, + ADDRESS_DAI, + address(0), + swapData[0].fromAmount, + bridgeData.minAmount, + block.timestamp + ); + + vm.expectEmit(true, true, true, true, _facetTestContractAddress); + emit LiFiTransferStarted(bridgeData); + + // execute call in child contract + initiateSwapAndBridgeTxWithFacet(false); + } } From bbbcbf3bada91bdb629b84cf8747d5e0ec737025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 3 Jul 2024 09:37:17 +0700 Subject: [PATCH 041/100] restricts swapAndBridge.. function to native only + updates tests --- src/Facets/GasZipFacet.sol | 27 +++-- test/solidity/Facets/GasZipFacet.t.sol | 132 ++++++++++++++++--------- 2 files changed, 99 insertions(+), 60 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index db9f65d82..c103381af 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -27,10 +27,8 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @dev GasZip-specific bridge data /// @param gasZipChainId The Gas.zip-specific chainId of the chain on which gas should be received on (https://dev.gas.zip/gas/chain-support/outbound) - /// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip struct GasZipData { uint256 gasZipChainId; - LibSwap.SwapData gasZipSwapData; } /// State /// @@ -57,6 +55,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { doesNotContainSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) { + // this function shall only be used for native assets if (!LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) revert InvalidCallData(); @@ -86,6 +85,11 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { doesNotContainDestinationCalls(_bridgeData) validateBridgeData(_bridgeData) { + // this function shall only be used for ERC20 assets + if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) + revert InvalidCallData(); + + // deposit and swap ERC20 tokens _bridgeData.minAmount = _depositAndSwap( _bridgeData.transactionId, _bridgeData.minAmount, @@ -93,19 +97,12 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { payable(msg.sender) ); - // deposit to gas.zip depending on which asset type is being used - if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) - depositToGasZipNative( - _bridgeData.minAmount, - _gasZipData.gasZipChainId, - _bridgeData.receiver - ); - else - depositToGasZipERC20( - _gasZipData.gasZipSwapData, - _gasZipData.gasZipChainId, - _bridgeData.receiver - ); + // deposit to gas.zip + depositToGasZipNative( + _bridgeData.minAmount, + _gasZipData.gasZipChainId, + _bridgeData.receiver + ); emit LiFiTransferStarted(_bridgeData); } diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 7e6959a77..19e5110c9 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.17; import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; import { ILiFi, LibSwap, LibAllowList, TestBaseFacet, console, ERC20 } from "../utils/TestBaseFacet.sol"; +import { InvalidCallData } from "lifi/Errors/GenericErrors.sol"; // Stub GenericSwapFacet Contract contract TestGasZipFacet is GasZipFacet { @@ -77,22 +78,15 @@ contract GasZipFacetTest is TestBaseFacet { gasZipFacet.depositToGasZipNative.selector ); - ( - LibSwap.SwapData memory gasZipSwapData, - - ) = _getUniswapCalldataForERC20ToNativeSwap( - ADDRESS_USDC, - defaultUSDCAmount - ); - setFacetAddressInTestBase(address(gasZipFacet), "GasZipFacet"); // produce valid GasZipData gasZipData = GasZipFacet.GasZipData({ - gasZipChainId: 17, // Polygon (https://dev.gas.zip/gas/chain-support/outbound) - gasZipSwapData: gasZipSwapData + gasZipChainId: 17 // Polygon (https://dev.gas.zip/gas/chain-support/outbound) }); + bridgeData.bridge = "GasZip"; + vm.label(address(gasZipFacet), "LiFiDiamond"); vm.label(ADDRESS_WETH, "WETH_TOKEN"); vm.label(ADDRESS_USDC, "USDC_TOKEN"); @@ -125,41 +119,6 @@ contract GasZipFacetTest is TestBaseFacet { } } - function _getUniswapCalldataForERC20ToNativeSwap( - address sendingAssetId, - uint256 fromAmount - ) - internal - view - returns (LibSwap.SwapData memory swapData, uint256 amountOutMin) - { - // prepare swap data - address[] memory path = new address[](2); - path[0] = sendingAssetId; - path[1] = ADDRESS_WETH; - - // Calculate USDC input amount - uint256[] memory amounts = uniswap.getAmountsOut(fromAmount, path); - amountOutMin = amounts[1]; - - swapData = LibSwap.SwapData( - address(uniswap), - address(uniswap), - sendingAssetId, - ADDRESS_WETH, - fromAmount, - abi.encodeWithSelector( - uniswap.swapExactTokensForETH.selector, - fromAmount, - amountOutMin, - path, - address(gasZipFacet), - block.timestamp + 20 seconds - ), - false // not required since tokens are already in diamond - ); - } - function testBase_CanBridgeTokens_fuzzed(uint256 amount) public override { // deactivated for this facet since we would have to update the calldata that swaps from ERC20 to native for every amount } @@ -182,4 +141,87 @@ contract GasZipFacetTest is TestBaseFacet { function testBase_Revert_CallerHasInsufficientFunds() public override { // the startBridgeTokensViaGasZip can only be used for native tokens, therefore this test case is not applicable } + + function testBase_CanSwapAndBridgeNativeTokens() public override { + // the swapAndStartBridgeTokensViaGasZip can only be used for ERC20 tokens + // therefore this test is expected to revert with InvalidCallData() + vm.startPrank(USER_SENDER); + + // prepare bridgeData + bridgeData.hasSourceSwaps = true; + bridgeData.sendingAssetId = address(0); + + // prepare swap data + address[] memory path = new address[](2); + path[0] = ADDRESS_USDC; + path[1] = ADDRESS_WETH; + + uint256 amountOut = defaultNativeAmount; + + // Calculate USDC input amount + uint256[] memory amounts = uniswap.getAmountsIn(amountOut, path); + uint256 amountIn = amounts[0]; + + bridgeData.minAmount = amountOut; + + delete swapData; + swapData.push( + LibSwap.SwapData({ + callTo: address(uniswap), + approveTo: address(uniswap), + sendingAssetId: ADDRESS_USDC, + receivingAssetId: address(0), + fromAmount: amountIn, + callData: abi.encodeWithSelector( + uniswap.swapTokensForExactETH.selector, + amountOut, + amountIn, + path, + _facetTestContractAddress, + block.timestamp + 20 minutes + ), + requiresDeposit: true + }) + ); + + // approval + usdc.approve(_facetTestContractAddress, amountIn); + + vm.expectRevert(InvalidCallData.selector); + + // execute call in child contract + initiateSwapAndBridgeTxWithFacet(false); + } + + function testBase_CanSwapAndBridgeTokens() public override { + vm.startPrank(USER_SENDER); + + // prepare bridgeData + bridgeData.hasSourceSwaps = true; + bridgeData.minAmount = defaultNativeAmount; + + // reset swap data + setDefaultSwapDataSingleDAItoETH(); + + // approval + dai.approve(_facetTestContractAddress, swapData[0].fromAmount); + + //prepare check for events + vm.expectEmit(true, true, true, true, _facetTestContractAddress); + emit AssetSwapped( + bridgeData.transactionId, + ADDRESS_UNISWAP, + ADDRESS_DAI, + address(0), + swapData[0].fromAmount, + bridgeData.minAmount, + block.timestamp + ); + + vm.expectEmit(true, true, true, true, _facetTestContractAddress); + emit LiFiTransferStarted(bridgeData); + + // execute call in child contract + initiateSwapAndBridgeTxWithFacet(false); + } } From 3994945bf042578f813677e332edfe873e4a83a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 3 Jul 2024 09:45:01 +0700 Subject: [PATCH 042/100] redeployed gasZipFacet to bsc staging --- deployments/_deployments_log_file.json | 6 +++--- deployments/bsc.diamond.staging.json | 2 +- deployments/bsc.staging.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 0b25b1aa4..908f9a93c 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -19844,12 +19844,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0x70cD2f8937aF9599583c18042C63704DfF3B67f6", + "ADDRESS": "0xA269cb81E6bBB86683558e449cb1bAFFdb155Bfc", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-06-28 20:55:43", + "TIMESTAMP": "2024-07-03 09:43:10", "CONSTRUCTOR_ARGS": "0x00000000000000000000000085e5fb57844be79b42997c898d177a39f328ccf0", "SALT": "", - "VERIFIED": "false" + "VERIFIED": "true" } ] } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index a6b9b1119..b4ce67292 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -69,7 +69,7 @@ "Name": "GenericSwapFacet", "Version": "1.0.0" }, - "0x70cD2f8937aF9599583c18042C63704DfF3B67f6": { + "0xA269cb81E6bBB86683558e449cb1bAFFdb155Bfc": { "Name": "GasZipFacet", "Version": "1.0.0" } diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 93554fe4a..3c635d5c8 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -28,5 +28,5 @@ "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A", "MayanBridgeFacet": "0x5Ba4FeD1DAd2fD057A9f687B399B8e4cF2368214", "MayanFacet": "0xcED4B608A468ce334c75c6652e18E2Ba7f3F44dA", - "GasZipFacet": "0x70cD2f8937aF9599583c18042C63704DfF3B67f6" + "GasZipFacet": "0xA269cb81E6bBB86683558e449cb1bAFFdb155Bfc" } \ No newline at end of file From 9c7173dc865899ca23c92f271c9f41e99a7a8e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 23 Sep 2024 08:05:57 +0700 Subject: [PATCH 043/100] Gas.Zip split into facet & periphery, tests and docs added --- config/gaszip.json | 15 +- docs/GasZipFacet.md | 51 +-- docs/GasZipPeriphery.md | 51 +++ .../deploy/facets/DeployGasZipPeriphery.s.sol | 50 +++ .../deploy/resources/deployRequirements.json | 16 +- src/Facets/GasZipFacet.sol | 128 +++----- src/Interfaces/IGasZip.sol | 20 ++ src/Periphery/GasZipPeriphery.sol | 100 ++++++ test/solidity/Facets/GasZipFacet.t.sol | 173 +++++++--- .../GasZipPeriphery.t.sol} | 303 ++++++++++-------- test/solidity/utils/TestBase.sol | 2 +- 11 files changed, 596 insertions(+), 313 deletions(-) create mode 100644 docs/GasZipPeriphery.md create mode 100644 script/deploy/facets/DeployGasZipPeriphery.s.sol create mode 100644 src/Interfaces/IGasZip.sol create mode 100644 src/Periphery/GasZipPeriphery.sol rename test/solidity/{Facets/GasZipFacet.protocol.t.sol => Periphery/GasZipPeriphery.t.sol} (53%) diff --git a/config/gaszip.json b/config/gaszip.json index 9cc90ff28..a52993085 100644 --- a/config/gaszip.json +++ b/config/gaszip.json @@ -3,18 +3,19 @@ "---ListOfRouterAddresses---": "https://dev.gas.zip/gas/chain-support/inbound", "mainnet": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", "arbitrum": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", - "blast": "0x6d4Ac5Ba1D649030227718C05ca4399F8522828b", "avalanche": "0x5Fa63e70a6aa982bDaDbdc0f520B32dB7CADA1eF", "base": "0x40a132f0ed6e1d63621586db1fabfb8a7587f2ac", - "mode": "0xB7D54342E5d993FE38897ECf7B5081eF5cdB5c18", "bsc": "0x85e5fb57844be79b42997c898d177a39f328ccf0", - "zksync": "0x370475E7f574CA67C9bf9C43fc57191545C2a84b", - "mantle": "0x4C80aa9cEc1651DC42Bfca15BBe08bb6bbf8AbBC", + "blast": "0x6d4Ac5Ba1D649030227718C05ca4399F8522828b", "gnosis": "0x87709e691A8C2037407eCdB4C271000ec2caC6Ee", - "optimism": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", - "polygon": "0xea0b31a1bb831ca9b32d950fc17f369444744a8e", "linea": "0x6dB5Dd4eaaAd3C1B1890BDF81272a79E6223c051", + "mantle": "0x4C80aa9cEc1651DC42Bfca15BBe08bb6bbf8AbBC", "metis": "0x73aaa8230Cd7564b088a8CD1fC7447B7D4C0Bb3c", - "scroll": "0x5B5040638C7e6283d6D0780b09a854d395C12e59" + "mode": "0xB7D54342E5d993FE38897ECf7B5081eF5cdB5c18", + "optimism": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", + "polygon": "0xea0b31a1bb831ca9b32d950fc17f369444744a8e", + "scroll": "0x5B5040638C7e6283d6D0780b09a854d395C12e59", + "xlayer": "0x391E7C679d29bD940d63be94AD22A25d25b5A604", + "zksync": "0x370475E7f574CA67C9bf9C43fc57191545C2a84b" } } diff --git a/docs/GasZipFacet.md b/docs/GasZipFacet.md index b4d1de156..6d0e71b42 100644 --- a/docs/GasZipFacet.md +++ b/docs/GasZipFacet.md @@ -2,52 +2,17 @@ ## Description -The GasZipFacet offers functionality to send native gas tokens to another chain using the Gas.Zip protocol (https://www.gas.zip/) -It can be used as a regular bridge facet or as a (LibSwap.SwapData) swap step. - -If used as a regular bridge facet, it will either execute one or more swap steps prior to bridging, or bridge directly. -If used as a swap step, it can be combined with any other bridge as a prior step before bridging. -This allows for maximum flexibility when it comes to sending gas to another chain. +The GasZipFacet offers functionality to send native gas tokens to other chains using the Gas.Zip protocol (https://www.gas.zip/) +If gas is sent to several chains, each chain will receive an equal amount ## How To Use ### Functions for bridging - `function startBridgeTokensViaGasZip(BridgeData memory _bridgeData, GasZipData calldata _gasZipData)` - - Simply bridges tokens using GasZipFacet (can only be used for native tokens) + - Simply deposits native tokens to Gas.zip protocol (this function can only be used for native tokens) - `function swapAndStartBridgeTokensViaGasZip(BridgeData memory _bridgeData, SwapData[] calldata _swapData, GasZipData calldata _gasZipData)` - - Performs swap(s) before bridging tokens using GasZipFacet - -### Functions for using this facet as a LibSwap.SwapData step - -The contract provides two public methods: -One for ERC20 tokens (these will be swapped into native before depositing to gas.zip) - -```solidity -/// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract -/// @param _swapData The swap data struct -/// @param _destinationChainId the id of the chain where gas should be made available -/// @param _recipient the address to receive the gas on dst chain -function depositToGasZipERC20( - LibSwap.SwapData calldata _swapData, - uint256 _destinationChainId, - address _recipient -) -``` - -and another for native tokens (these will be directly deposited) - -```solidity -/// @notice Deposits native tokens in the GasZip router contract -/// @param _amountToZip The swap data struct -/// @param _destinationChainId the id of the chain where gas should be made available -/// @param _recipient the address to receive the gas on dst chain -function depositToGasZipNative( - uint256 _amountToZip, - uint256 _destinationChainId, - address _recipient -) -``` + - Performs swap(s) from ERC20 to native before depositing to Gas.zip protocol. The last receiving token must be native. ## Bridge Specific Parameters @@ -56,11 +21,11 @@ Some of the methods listed above take a variable labeled `_gasZipData`. This data is specific to Gas.Zip and is represented as the following struct type: ```solidity -/// @param gasZipSwapData (only required for ERC20 tokens): the swapData that swaps from ERC20 to native before depositing to gas.zip -/// @param amountOutMin (only required for ERC20 tokens): the native amount we expect to receive from swap and plan to deposit to gas.zip +/// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) +/// @param receiver the address to receive the gas on dst chain struct GasZipData { - LibSwap.SwapData gasZipSwapData; - uint256 amountOutMin; + uint256 destinationChains; + address receiver; } ``` diff --git a/docs/GasZipPeriphery.md b/docs/GasZipPeriphery.md new file mode 100644 index 000000000..766127376 --- /dev/null +++ b/docs/GasZipPeriphery.md @@ -0,0 +1,51 @@ +# GasZipPeriphery + +## Description + +The GasZipPeriphery contract offers functionality to send native gas tokens to other chains using the Gas.Zip protocol (https://www.gas.zip/) +It can be used as (LibSwap.SwapData) swap step prior to bridging. + +## How To Use + +### Functions for using this facet as a LibSwap.SwapData step + +The contract provides two public methods: +One for ERC20 tokens (these will be swapped into native before depositing to Gas.zip using the LiFiDEXAggregator) + +```solidity +/// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract +/// Swaps are only allowed via the LiFiDEXAggregator +/// @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 address should receive gas on which chains +function depositToGasZipERC20( + LibSwap.SwapData calldata _swapData, + IGasZip.GasZipData calldata _gasZipData +) +``` + +and another for native tokens (these will be directly deposited) + +```solidity +/// @notice Deposits native tokens to the GasZip router contract +/// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge +/// @param _gasZipData contains information about which address should receive gas on which chains +function depositToGasZipNative( + IGasZip.GasZipData calldata _gasZipData +) +``` + +## Bridge Specific Parameters + +Some of the methods listed above take a variable labeled `_gasZipData`. + +This data is specific to Gas.Zip and is represented as the following struct type: + +```solidity +/// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) +/// @param receiver the address to receive the gas on dst chain +struct GasZipData { + uint256 destinationChains; + address receiver; +} +``` diff --git a/script/deploy/facets/DeployGasZipPeriphery.s.sol b/script/deploy/facets/DeployGasZipPeriphery.s.sol new file mode 100644 index 000000000..1a814698a --- /dev/null +++ b/script/deploy/facets/DeployGasZipPeriphery.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { GasZipPeriphery } from "lifi/Periphery/GasZipPeriphery.sol"; +import { stdJson } from "forge-std/Script.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("GasZipPeriphery") {} + + function run() + public + returns (GasZipPeriphery deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + + deployed = GasZipPeriphery(deploy(type(GasZipPeriphery).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + // get gasZipRouter address + string memory gasZipConfig = string.concat( + root, + "/config/gaszip.json" + ); + + string memory gasZipConfigJson = vm.readFile(gasZipConfig); + + address gasZipRouter = gasZipConfigJson.readAddress( + string.concat(".gasZipRouters.", network) + ); + + // get LiFiDEXAggregator address + string memory deployLog = string.concat( + root, + "/deployments/", + network, + ".", + fileSuffix, + "json" + ); + string memory json = vm.readFile(deployLog); + + address liFiDEXAggregator = json.readAddress(".LiFiDEXAggregator"); + + return abi.encode(gasZipRouter, liFiDEXAggregator); + } +} diff --git a/script/deploy/resources/deployRequirements.json b/script/deploy/resources/deployRequirements.json index fade20427..74aa668de 100644 --- a/script/deploy/resources/deployRequirements.json +++ b/script/deploy/resources/deployRequirements.json @@ -463,7 +463,7 @@ } } }, - "GasZip": { + "GasZipFacet": { "configData": { "_gasZipRouter": { "configFileName": "gaszip.json", @@ -471,5 +471,19 @@ "allowToDeployWithZeroAddress": "false" } } + }, + "GasZipPeriphery": { + "configData": { + "_gasZipRouter": { + "configFileName": "gaszip.json", + "keyInConfigFile": ".gasZipRouters.", + "allowToDeployWithZeroAddress": "false" + } + }, + "contractAddresses": { + "LiFiDEXAggregator": { + "allowToDeployWithZeroAddress": "false" + } + } } } diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index c103381af..14ace9544 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.17; import { ILiFi } from "../Interfaces/ILiFi.sol"; +import { IGasZip } from "../Interfaces/IGasZip.sol"; import { LibSwap } from "../Libraries/LibSwap.sol"; import { LibAsset } from "../Libraries/LibAsset.sol"; import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; @@ -9,27 +10,16 @@ import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { ERC20 } from "solady/tokens/ERC20.sol"; -import { NativeAssetTransferFailed, InvalidCallData } from "lifi/Errors/GenericErrors.sol"; - -interface IGasZip { - function deposit( - uint256 destinationChains, - address recipient - ) external payable; -} /// @title GasZipFacet /// @author LI.FI (https://li.fi) /// @notice Provides functionality to swap ERC20 tokens to native and deposit them to the gas.zip protocol (https://www.gas.zip/) -/// @custom:version 1.0.0 +/// @custom:version 2.0.0 contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; - /// @dev GasZip-specific bridge data - /// @param gasZipChainId The Gas.zip-specific chainId of the chain on which gas should be received on (https://dev.gas.zip/gas/chain-support/outbound) - struct GasZipData { - uint256 gasZipChainId; - } + error OnlySwapsFromERC20ToNativeAllowed(); + error OnlyNativeAllowed(); /// State /// IGasZip public immutable gasZipRouter; @@ -45,7 +35,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @param _gasZipData GasZip-specific bridge data function startBridgeTokensViaGasZip( ILiFi.BridgeData memory _bridgeData, - GasZipData calldata _gasZipData + IGasZip.GasZipData calldata _gasZipData ) external payable @@ -55,27 +45,22 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { doesNotContainSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) { - // this function shall only be used for native assets + // this function / path shall only be used for native assets if (!LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) - revert InvalidCallData(); - - depositToGasZipNative( - _bridgeData.minAmount, - _gasZipData.gasZipChainId, - _bridgeData.receiver - ); + revert OnlyNativeAllowed(); - emit LiFiTransferStarted(_bridgeData); + // deposit native to Gas.zip + _startBridge(_bridgeData, _gasZipData); } - /// @notice Performs a swap before bridging via the gas.zip protocol - /// @param _bridgeData The core information needed for bridging + /// @notice Performs a swap from ERC20 to native before depositing to the gas.zip protocol + /// @param _bridgeData The core information needed for depositing /// @param _swapData An array of swap related data for performing swaps before bridging /// @param _gasZipData GasZip-specific bridge data function swapAndStartBridgeTokensViaGasZip( ILiFi.BridgeData memory _bridgeData, LibSwap.SwapData[] calldata _swapData, - GasZipData calldata _gasZipData + IGasZip.GasZipData calldata _gasZipData ) external payable @@ -85,11 +70,14 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { doesNotContainDestinationCalls(_bridgeData) validateBridgeData(_bridgeData) { - // this function shall only be used for ERC20 assets - if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) - revert InvalidCallData(); - - // deposit and swap ERC20 tokens + // this function / path shall only be used for ERC20 assets + if ( + !LibAsset.isNativeAsset( + _swapData[_swapData.length - 1].receivingAssetId + ) + ) revert OnlySwapsFromERC20ToNativeAllowed(); + + // deposit and swap ERC20 tokens to n ative _bridgeData.minAmount = _depositAndSwap( _bridgeData.transactionId, _bridgeData.minAmount, @@ -97,57 +85,39 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { payable(msg.sender) ); - // deposit to gas.zip - depositToGasZipNative( - _bridgeData.minAmount, - _gasZipData.gasZipChainId, - _bridgeData.receiver - ); - - emit LiFiTransferStarted(_bridgeData); + // deposit native to Gas.zip + _startBridge(_bridgeData, _gasZipData); } - /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract - /// @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 _destinationChains A value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) - /// @param _recipient The address to receive the gas on dst chain - function depositToGasZipERC20( - LibSwap.SwapData calldata _swapData, - uint256 _destinationChains, - address _recipient - ) public { - // get the current native balance - uint256 currentNativeBalance = address(this).balance; - - // execute the swapData that swaps the ERC20 token into native - LibSwap.swap(0, _swapData); - - // calculate the swap output amount using the initial native balance - uint256 swapOutputAmount = address(this).balance - - currentNativeBalance; - - // call the gas zip router and deposit tokens - gasZipRouter.deposit{ value: swapOutputAmount }( - _destinationChains, - _recipient + /// @dev Contains the business logic for depositing to GasZip protocol + /// @param _bridgeData The core information needed for bridging + /// @param _gasZipData Data specific to Gas.zip protocol + function _startBridge( + ILiFi.BridgeData memory _bridgeData, + IGasZip.GasZipData calldata _gasZipData + ) internal { + // should we conduct any calldata checks here? + // e.g. verify the receiver address (add parameter in _gasZipData and cross-check with bytes calldata) + + // deposit native to Gas.zip (v1) https://dev.gas.zip/gas/code-examples/contractDeposit + gasZipRouter.deposit{ value: _bridgeData.minAmount }( + _gasZipData.destinationChains, + _gasZipData.receiver ); + + emit LiFiTransferStarted(_bridgeData); } - /// @notice Deposits native tokens in the GasZip router contract - /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge - /// @param _amountToZip The amount to be deposited to the protocol - /// @param _destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) - /// @param _recipient the address to receive the gas on dst chain - function depositToGasZipNative( - uint256 _amountToZip, - uint256 _destinationChains, - address _recipient - ) public payable { - // call the gas zip router and deposit tokens - gasZipRouter.deposit{ value: _amountToZip }( - _destinationChains, - _recipient - ); + /// @dev Returns a value that signals to Gas.zip to which chains gas should be sent in equal parts + /// @param _chainIds a list of Gas.zip-specific chainIds (not the original chainIds), see https://dev.gas.zip/gas/chain-support/outbound + function getDestinationChainsValue( + uint8[] memory _chainIds + ) public pure returns (uint256 destinationChains) { + for (uint256 i = 0; i < _chainIds.length; i++) { + // Shift destinationChains left by 8 bits and add the next chainID + destinationChains = + (destinationChains << 8) | + uint256(_chainIds[i]); + } } } diff --git a/src/Interfaces/IGasZip.sol b/src/Interfaces/IGasZip.sol new file mode 100644 index 000000000..ba17d3b96 --- /dev/null +++ b/src/Interfaces/IGasZip.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +/// @custom:version 1.0.0 +pragma solidity 0.8.17; + +/// @title Interface for GasZip +/// @author LI.FI (https://li.fi) +interface IGasZip { + /// @dev GasZip-specific bridge data + /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) + /// @param receiver the address to receive the gas on dst chain + struct GasZipData { + uint256 destinationChains; + address receiver; + } + + function deposit( + uint256 destinationChains, + address recipient + ) external payable; +} diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol new file mode 100644 index 000000000..b5a31e094 --- /dev/null +++ b/src/Periphery/GasZipPeriphery.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { ILiFi } from "../Interfaces/ILiFi.sol"; +import { IGasZip } from "../Interfaces/IGasZip.sol"; +import { LibSwap } from "../Libraries/LibSwap.sol"; +import { LibAsset, IERC20 } from "../Libraries/LibAsset.sol"; +import { LibUtil } from "../Libraries/LibUtil.sol"; +import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; +import { SwapperV2 } from "../Helpers/SwapperV2.sol"; +import { Validatable } from "../Helpers/Validatable.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; +import { ERC20 } from "solady/tokens/ERC20.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.0 +contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { + using SafeTransferLib for address; + + event DepositedToGasZip(uint256 amount); + + /// State /// + IGasZip public immutable gasZipRouter; + address public immutable liFiDEXAggregator; + + /// Constructor /// + constructor(address _gasZipRouter, address _liFiDEXAggregator) { + gasZipRouter = IGasZip(_gasZipRouter); + liFiDEXAggregator = _liFiDEXAggregator; + } + + /// @notice Swaps ERC20 tokens to native and deposits these native tokens in the GasZip router contract + /// Swaps are only allowed via the LiFiDEXAggregator + /// @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 address should receive gas on which chains + function depositToGasZipERC20( + LibSwap.SwapData calldata _swapData, + IGasZip.GasZipData calldata _gasZipData + ) public { + // 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, + type(uint256).max + ); + + // execute swap using LiFiDEXAggregator + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory res) = liFiDEXAggregator.call( + _swapData.callData + ); + if (!success) { + LibUtil.revertWith(res); + } + // extract the swap output amount from the call return value + uint256 swapOutputAmount = abi.decode(res, (uint256)); + + // deposit native tokens to Gas.zip protocol + GasZipPeriphery(payable(address(this))).depositToGasZipNative{ + value: swapOutputAmount + }(_gasZipData); + } + + /// @notice Deposits native tokens to the GasZip router contract + /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge + /// @param _gasZipData contains information about which address should receive gas on which chains + function depositToGasZipNative( + IGasZip.GasZipData calldata _gasZipData + ) public payable { + // deposit native to Gas.zip (v1) https://dev.gas.zip/gas/code-examples/contractDeposit + gasZipRouter.deposit{ value: msg.value }( + _gasZipData.destinationChains, + _gasZipData.receiver + ); + + emit DepositedToGasZip(msg.value); + } + + /// @dev Returns a value that signals to Gas.zip to which chains gas should be sent in equal parts + /// @param _chainIds a list of Gas.zip-specific chainIds (not the original chainIds), see https://dev.gas.zip/gas/chain-support/outbound + function getDestinationChainsValue( + uint8[] memory _chainIds + ) public pure returns (uint256 destinationChains) { + for (uint256 i = 0; i < _chainIds.length; i++) { + // Shift destinationChains left by 8 bits and add the next chainID + destinationChains = + (destinationChains << 8) | + uint256(_chainIds[i]); + } + } + + // Required to receive ETH from ERC20-to-Native swaps + receive() external payable {} +} diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 19e5110c9..ceba7ba8e 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity 0.8.17; import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; +import { IGasZip } from "lifi/Interfaces/IGasZip.sol"; import { ILiFi, LibSwap, LibAllowList, TestBaseFacet, console, ERC20 } from "../utils/TestBaseFacet.sol"; import { InvalidCallData } from "lifi/Errors/GenericErrors.sol"; @@ -26,7 +27,7 @@ contract GasZipFacetTest is TestBaseFacet { 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; TestGasZipFacet internal gasZipFacet; - GasZipFacet.GasZipData internal gasZipData; + IGasZip.GasZipData internal gasZipData; uint256 public defaultDestinationChains = 96; address public defaultRecipientAddress = address(12345); @@ -35,6 +36,9 @@ contract GasZipFacetTest is TestBaseFacet { event Deposit(address from, uint256 chains, uint256 amount, address to); + error OnlySwapsFromERC20ToNativeAllowed(); + error OnlyNativeAllowed(); + function setUp() public { // set custom block no for mainnet forking customBlockNumberForForking = 17484106; @@ -45,14 +49,16 @@ contract GasZipFacetTest is TestBaseFacet { gasZipFacet = new TestGasZipFacet(GAS_ZIP_ROUTER_MAINNET); // add gasZipFacet to diamond - bytes4[] memory functionSelectors = new bytes4[](5); + bytes4[] memory functionSelectors = new bytes4[](6); functionSelectors[0] = gasZipFacet.startBridgeTokensViaGasZip.selector; functionSelectors[1] = gasZipFacet .swapAndStartBridgeTokensViaGasZip .selector; - functionSelectors[2] = gasZipFacet.addDex.selector; - functionSelectors[3] = gasZipFacet.removeDex.selector; - functionSelectors[4] = gasZipFacet + functionSelectors[2] = gasZipFacet.getDestinationChainsValue.selector; + + functionSelectors[3] = gasZipFacet.addDex.selector; + functionSelectors[4] = gasZipFacet.removeDex.selector; + functionSelectors[5] = gasZipFacet .setFunctionApprovalBySignature .selector; addFacet(diamond, address(gasZipFacet), functionSelectors); @@ -69,20 +75,20 @@ contract GasZipFacetTest is TestBaseFacet { uniswap.swapTokensForExactETH.selector ); gasZipFacet.setFunctionApprovalBySignature( - uniswap.swapETHForExactTokens.selector - ); - gasZipFacet.setFunctionApprovalBySignature( - gasZipFacet.depositToGasZipERC20.selector + uniswap.swapExactTokensForETH.selector ); gasZipFacet.setFunctionApprovalBySignature( - gasZipFacet.depositToGasZipNative.selector + uniswap.swapETHForExactTokens.selector ); setFacetAddressInTestBase(address(gasZipFacet), "GasZipFacet"); // produce valid GasZipData - gasZipData = GasZipFacet.GasZipData({ - gasZipChainId: 17 // Polygon (https://dev.gas.zip/gas/chain-support/outbound) + uint8[] memory chainIds = new uint8[](1); + chainIds[0] = 17; // polygon + gasZipData = IGasZip.GasZipData({ + destinationChains: gasZipFacet.getDestinationChainsValue(chainIds), + receiver: USER_RECEIVER }); bridgeData.bridge = "GasZip"; @@ -138,30 +144,93 @@ contract GasZipFacetTest is TestBaseFacet { vm.stopPrank(); } + function testBase_CanBridgeNativeTokens() public override { + // defaultNativeAmount is too high, therefore we need to override this test + vm.startPrank(USER_SENDER); + // customize bridgeData + bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = 10000000000000000; // ~2 USD + + //prepare check for events + vm.expectEmit(true, true, true, true, _facetTestContractAddress); + emit LiFiTransferStarted(bridgeData); + + initiateBridgeTxWithFacet(true); + vm.stopPrank(); + } + function testBase_Revert_CallerHasInsufficientFunds() public override { // the startBridgeTokensViaGasZip can only be used for native tokens, therefore this test case is not applicable } - function testBase_CanSwapAndBridgeNativeTokens() public override { - // the swapAndStartBridgeTokensViaGasZip can only be used for ERC20 tokens - // therefore this test is expected to revert with InvalidCallData() + function testRevert_WillFailWhenTryingToSwapERC20ToERC20() public { vm.startPrank(USER_SENDER); + // prepare swapData for an ERC20 to ERC20 swap + setDefaultSwapDataSingleDAItoUSDC(); + + // set max approval + dai.approve(address(gasZipFacet), type(uint256).max); + // prepare bridgeData bridgeData.hasSourceSwaps = true; - bridgeData.sendingAssetId = address(0); - // prepare swap data - address[] memory path = new address[](2); - path[0] = ADDRESS_USDC; - path[1] = ADDRESS_WETH; + // expect that the call reverts + vm.expectRevert(OnlySwapsFromERC20ToNativeAllowed.selector); + + // initiate transaction + initiateSwapAndBridgeTxWithFacet(false); + } + + function testRevert_WillFailWhenTryingToBridgeERC20() public { + vm.startPrank(USER_SENDER); + + // approval + usdc.approve(_facetTestContractAddress, bridgeData.minAmount); + + // expect the call to revert + vm.expectRevert(OnlyNativeAllowed.selector); + + initiateBridgeTxWithFacet(false); + vm.stopPrank(); + } + + function testBase_Revert_SwapAndBridgeWithInvalidSwapData() + public + override + { + // since the facets accesses the swapData parameter already before trying to execute the swap, we need to override the expected error message + vm.startPrank(USER_SENDER); + + // prepare bridgeData + bridgeData.hasSourceSwaps = true; - uint256 amountOut = defaultNativeAmount; + // reset swap data + delete swapData; + + vm.expectRevert(); + + // execute call in child contract + initiateSwapAndBridgeTxWithFacet(false); + } - // Calculate USDC input amount - uint256[] memory amounts = uniswap.getAmountsIn(amountOut, path); - uint256 amountIn = amounts[0]; + function testBase_CanSwapAndBridgeTokens() public override { + vm.startPrank(USER_SENDER); + // prepare bridgeData + bridgeData.hasSourceSwaps = true; + + // reset create swapData (5 DAI to native) + uint256 daiAmount = 5 * 10 ** dai.decimals(); + + // Swap DAI -> ETH + address[] memory path = new address[](2); + path[0] = ADDRESS_DAI; + path[1] = ADDRESS_WETH; + + // Calculate DAI amount + uint256[] memory amounts = uniswap.getAmountsOut(daiAmount, path); + uint256 amountOut = amounts[1]; bridgeData.minAmount = amountOut; delete swapData; @@ -169,13 +238,13 @@ contract GasZipFacetTest is TestBaseFacet { LibSwap.SwapData({ callTo: address(uniswap), approveTo: address(uniswap), - sendingAssetId: ADDRESS_USDC, + sendingAssetId: ADDRESS_DAI, receivingAssetId: address(0), - fromAmount: amountIn, + fromAmount: daiAmount, callData: abi.encodeWithSelector( - uniswap.swapTokensForExactETH.selector, + uniswap.swapExactTokensForETH.selector, + daiAmount, amountOut, - amountIn, path, _facetTestContractAddress, block.timestamp + 20 minutes @@ -185,26 +254,7 @@ contract GasZipFacetTest is TestBaseFacet { ); // approval - usdc.approve(_facetTestContractAddress, amountIn); - - vm.expectRevert(InvalidCallData.selector); - - // execute call in child contract - initiateSwapAndBridgeTxWithFacet(false); - } - - function testBase_CanSwapAndBridgeTokens() public override { - vm.startPrank(USER_SENDER); - - // prepare bridgeData - bridgeData.hasSourceSwaps = true; - bridgeData.minAmount = defaultNativeAmount; - - // reset swap data - setDefaultSwapDataSingleDAItoETH(); - - // approval - dai.approve(_facetTestContractAddress, swapData[0].fromAmount); + dai.approve(_facetTestContractAddress, daiAmount); //prepare check for events vm.expectEmit(true, true, true, true, _facetTestContractAddress); @@ -213,7 +263,7 @@ contract GasZipFacetTest is TestBaseFacet { ADDRESS_UNISWAP, ADDRESS_DAI, address(0), - swapData[0].fromAmount, + daiAmount, bridgeData.minAmount, block.timestamp ); @@ -224,4 +274,29 @@ contract GasZipFacetTest is TestBaseFacet { // execute call in child contract initiateSwapAndBridgeTxWithFacet(false); } + + function test_getDestinationChainsValueReturnsCorrectValues() public { + // case 1 + uint8[] memory chainIds = new uint8[](1); + chainIds[0] = 17; // Polygon + + assertEq(gasZipFacet.getDestinationChainsValue(chainIds), 17); + + // case 2 + chainIds = new uint8[](2); + chainIds[0] = 51; + chainIds[1] = 52; + + assertEq(gasZipFacet.getDestinationChainsValue(chainIds), 13108); + + // case 3 + chainIds = new uint8[](5); + chainIds[0] = 15; // Avalanche + chainIds[1] = 54; // Base + chainIds[2] = 96; // Blast + chainIds[3] = 14; // BSC + chainIds[4] = 59; // Linea + + assertEq(gasZipFacet.getDestinationChainsValue(chainIds), 65336774203); + } } diff --git a/test/solidity/Facets/GasZipFacet.protocol.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol similarity index 53% rename from test/solidity/Facets/GasZipFacet.protocol.t.sol rename to test/solidity/Periphery/GasZipPeriphery.t.sol index b12726f90..b9b6cc6f1 100644 --- a/test/solidity/Facets/GasZipFacet.protocol.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -1,26 +1,22 @@ // SPDX-License-Identifier: Unlicense pragma solidity 0.8.17; -import { Test, DSTest } from "forge-std/Test.sol"; -import { console } from "../utils/Console.sol"; -import { DiamondTest, LiFiDiamond } from "../utils/DiamondTest.sol"; -import { Vm } from "forge-std/Vm.sol"; -import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; +import { GasZipPeriphery } from "lifi/Periphery/GasZipPeriphery.sol"; import { LibSwap } from "lifi/Libraries/LibSwap.sol"; import { LibAllowList } from "lifi/Libraries/LibAllowList.sol"; import { FeeCollector } from "lifi/Periphery/FeeCollector.sol"; -import { ContractCallNotAllowed, CumulativeSlippageTooHigh, NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; - -import { UniswapV2Router02 } from "../utils/Interfaces.sol"; -import { TestHelpers, MockUniswapDEX, NonETHReceiver } from "../utils/TestHelpers.sol"; -import { ERC20 } from "solady/tokens/ERC20.sol"; import { GnosisBridgeFacet } from "lifi/Facets/GnosisBridgeFacet.sol"; +import { TestGnosisBridgeFacet } from "test/solidity/Facets/GnosisBridgeFacet.t.sol"; +import { TestBase, console, ILiFi, ERC20 } from "../utils/TestBase.sol"; import { IXDaiBridge } from "lifi/Interfaces/IXDaiBridge.sol"; -import { ILiFi } from "lifi/Interfaces/ILiFi.sol"; +import { IGasZip } from "lifi/Interfaces/IGasZip.sol"; // Stub GenericSwapFacet Contract -contract TestGasZipFacet is GasZipFacet { - constructor(address gasZipRouter) GasZipFacet(gasZipRouter) {} +contract TestGasZipPeriphery is GasZipPeriphery { + constructor( + address gasZipRouter, + address liFiDEXAggregator + ) GasZipPeriphery(gasZipRouter, liFiDEXAggregator) {} function addDex(address _dex) external { LibAllowList.addAllowedContract(_dex); @@ -35,94 +31,66 @@ contract TestGasZipFacet is GasZipFacet { } } -contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { +contract GasZipPeripheryTest is TestBase { address public constant GAS_ZIP_ROUTER_MAINNET = 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; - address internal ADDRESS_WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address internal ADDRESS_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address internal ADDRESS_DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; - address internal ADDRESS_UNISWAP = - 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; + address public constant LIFI_DEX_AGGREGATOR_MAINNET = + 0xe43ca1Dee3F0fc1e2df73A0745674545F11A59F5; address internal constant XDAI_BRIDGE = 0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016; - LiFiDiamond internal diamond; - TestGasZipFacet internal gasZipFacet; - ERC20 internal usdc; - ERC20 internal dai; - UniswapV2Router02 internal uniswap; + TestGnosisBridgeFacet internal gnosisBridgeFacet; + TestGasZipPeriphery internal gasZipPeriphery; + IGasZip.GasZipData internal defaultGasZipData; FeeCollector internal feeCollector; + address internal liFiDEXAggregator = LIFI_DEX_AGGREGATOR_MAINNET; uint256 public defaultDestinationChains = 96; - address public defaultRecipientAddress = address(12345); - address public defaultRefundAddress = address(56789); - uint256 public defaultNativeAmount = 0.0006 ether; - uint256 public defaultUSDCAmount; event Deposit(address from, uint256 chains, uint256 amount, address to); - function fork() internal { - string memory rpcUrl = vm.envString("ETH_NODE_URI_MAINNET"); - uint256 blockNumber = 20173181; - vm.createSelectFork(rpcUrl, blockNumber); - } - function setUp() public { - fork(); + customBlockNumberForForking = 20789064; + initTestBase(); // deploy contracts - diamond = createDiamond(); - gasZipFacet = new TestGasZipFacet(GAS_ZIP_ROUTER_MAINNET); - usdc = ERC20(ADDRESS_USDC); - dai = ERC20(ADDRESS_DAI); - uniswap = UniswapV2Router02(ADDRESS_UNISWAP); + gasZipPeriphery = new TestGasZipPeriphery( + GAS_ZIP_ROUTER_MAINNET, + LIFI_DEX_AGGREGATOR_MAINNET + ); feeCollector = new FeeCollector(address(this)); defaultUSDCAmount = 10 * 10 ** usdc.decimals(); // 10 USDC - // add gasZipFacet to diamond - bytes4[] memory functionSelectors = new bytes4[](5); - functionSelectors[0] = gasZipFacet.depositToGasZipNative.selector; - functionSelectors[1] = gasZipFacet.depositToGasZipERC20.selector; - functionSelectors[2] = gasZipFacet.addDex.selector; - functionSelectors[3] = gasZipFacet.removeDex.selector; - functionSelectors[4] = gasZipFacet - .setFunctionApprovalBySignature - .selector; - addFacet(diamond, address(gasZipFacet), functionSelectors); + // set up diamond with GnosisBridgeFacet so we have a bridge to test with + gnosisBridgeFacet = _getGnosisBridgeFacet(); - gasZipFacet = TestGasZipFacet(payable(address(diamond))); + defaultGasZipData = IGasZip.GasZipData({ + destinationChains: defaultDestinationChains, + receiver: USER_RECEIVER + }); - // whitelist uniswap dex with function selectors - gasZipFacet.addDex(address(uniswap)); - gasZipFacet.setFunctionApprovalBySignature( - uniswap.swapExactTokensForTokens.selector - ); - gasZipFacet.setFunctionApprovalBySignature( - uniswap.swapExactETHForTokens.selector - ); + bridgeData.bridge = "gnosis"; + bridgeData.sendingAssetId = ADDRESS_DAI; + bridgeData.minAmount = defaultDAIAmount; + bridgeData.destinationChainId = 100; - vm.label(address(gasZipFacet), "LiFiDiamond"); - vm.label(ADDRESS_WETH, "WETH_TOKEN"); - vm.label(ADDRESS_USDC, "USDC_TOKEN"); - vm.label(ADDRESS_UNISWAP, "UNISWAP_V2_ROUTER"); + vm.label(address(gasZipPeriphery), "GasZipPeriphery"); } function test_canDepositNative() public { // set up expected event vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); emit Deposit( - address(gasZipFacet), + address(gasZipPeriphery), defaultDestinationChains, defaultNativeAmount, - defaultRecipientAddress + USER_RECEIVER ); // deposit via GasZip periphery contract - gasZipFacet.depositToGasZipNative{ value: defaultNativeAmount }( - defaultNativeAmount, - defaultDestinationChains, - defaultRecipientAddress + gasZipPeriphery.depositToGasZipNative{ value: defaultNativeAmount }( + defaultGasZipData ); } @@ -192,24 +160,22 @@ contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { uint256 gasZipERC20Amount = 2 * 10 ** dai.decimals(); ( LibSwap.SwapData memory gasZipSwapData, - uint256 amountOutMinGasZipSwap - ) = _getUniswapCalldataForERC20ToNativeSwap( + + ) = _getLiFiDEXAggregatorCalldataForERC20ToNativeSwap( ADDRESS_DAI, gasZipERC20Amount ); swapData[2] = LibSwap.SwapData( - address(gasZipFacet), - address(gasZipFacet), + address(gasZipPeriphery), + address(gasZipPeriphery), ADDRESS_DAI, ADDRESS_DAI, gasZipERC20Amount, abi.encodeWithSelector( - gasZipFacet.depositToGasZipERC20.selector, + gasZipPeriphery.depositToGasZipERC20.selector, gasZipSwapData, - defaultDestinationChains, - defaultRecipientAddress, - amountOutMinGasZipSwap + defaultGasZipData ), false // not required since tokens are already in the diamond ); @@ -221,26 +187,23 @@ contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { integrator: "", referrer: address(0), sendingAssetId: ADDRESS_DAI, - receiver: defaultRecipientAddress, + receiver: USER_RECEIVER, minAmount: swapOutputAmount - gasZipERC20Amount, destinationChainId: 100, hasSourceSwaps: true, hasDestinationCall: false }); - // whitelist gasZipFacet and FeeCollector - gasZipFacet.addDex(address(gasZipFacet)); - gasZipFacet.setFunctionApprovalBySignature( - gasZipFacet.depositToGasZipERC20.selector + // whitelist gasZipPeriphery and FeeCollector + gasZipPeriphery.addDex(address(gasZipPeriphery)); + gasZipPeriphery.setFunctionApprovalBySignature( + gasZipPeriphery.depositToGasZipERC20.selector ); - gasZipFacet.addDex(address(feeCollector)); - gasZipFacet.setFunctionApprovalBySignature( + gasZipPeriphery.addDex(address(feeCollector)); + gasZipPeriphery.setFunctionApprovalBySignature( feeCollector.collectTokenFees.selector ); - // bridge using (standalone) GnosisBridgeFacet - TestGnosisBridgeFacet gnosisBridgeFacet = _getGnosisBridgeFacet(); - // set approval for bridging usdc.approve(address(gnosisBridgeFacet), defaultUSDCAmount); @@ -265,16 +228,14 @@ contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { // get swapData for gas zip LibSwap.SwapData[] memory swapData = new LibSwap.SwapData[](2); swapData[0] = LibSwap.SwapData( - address(gasZipFacet), - address(gasZipFacet), + address(gasZipPeriphery), + address(gasZipPeriphery), address(0), address(0), nativeZipAmount, abi.encodeWithSelector( - gasZipFacet.depositToGasZipNative.selector, - nativeZipAmount, - defaultDestinationChains, - defaultRecipientAddress + gasZipPeriphery.depositToGasZipNative.selector, + defaultGasZipData ), false ); @@ -317,49 +278,143 @@ contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { integrator: "", referrer: address(0), sendingAssetId: ADDRESS_DAI, - receiver: defaultRecipientAddress, + receiver: USER_RECEIVER, minAmount: swapOutputAmount, destinationChainId: 100, hasSourceSwaps: true, hasDestinationCall: false }); - // whitelist gasZipFacet and FeeCollector - gasZipFacet.addDex(address(gasZipFacet)); - gasZipFacet.setFunctionApprovalBySignature( - gasZipFacet.depositToGasZipNative.selector + // whitelist gasZipPeriphery and FeeCollector + gasZipPeriphery.addDex(address(gasZipPeriphery)); + gasZipPeriphery.setFunctionApprovalBySignature( + gasZipPeriphery.depositToGasZipNative.selector ); - // bridge using (standalone) GnosisBridgeFacet - TestGnosisBridgeFacet gnosisBridgeFacet = _getGnosisBridgeFacet(); - gnosisBridgeFacet.swapAndStartBridgeTokensViaXDaiBridge{ value: nativeFromAmount }(bridgeData, swapData); } + function test_getDestinationChainsValueReturnsCorrectValues() public { + // case 1 + uint8[] memory chainIds = new uint8[](1); + chainIds[0] = 17; // Polygon + + assertEq(gasZipPeriphery.getDestinationChainsValue(chainIds), 17); + + // case 2 + chainIds = new uint8[](2); + chainIds[0] = 51; + chainIds[1] = 52; + + assertEq(gasZipPeriphery.getDestinationChainsValue(chainIds), 13108); + + // case 3 + chainIds = new uint8[](5); + chainIds[0] = 15; // Avalanche + chainIds[1] = 54; // Base + chainIds[2] = 96; // Blast + chainIds[3] = 14; // BSC + chainIds[4] = 59; // Linea + + assertEq( + gasZipPeriphery.getDestinationChainsValue(chainIds), + 65336774203 + ); + } + + function testRevert_WillFailIfSwapViaLiFiDEXAggregratorIsUnsuccessful() + public + { + vm.startPrank(USER_SENDER); + + // 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 + ); + + // use an invalid function selector to force the call to LiFiDEXAggregator to fail + gasZipSwapData.callData = hex"3a3f7332"; + + // expect the following call to fail without an error reason + vm.expectRevert(); + + // execute the call + gasZipPeriphery.depositToGasZipERC20( + gasZipSwapData, + defaultGasZipData + ); + } + function _getGnosisBridgeFacet() internal - returns (TestGnosisBridgeFacet gnosisBridgeFacet) + returns (TestGnosisBridgeFacet _gnosisBridgeFacet) { - gnosisBridgeFacet = new TestGnosisBridgeFacet( + _gnosisBridgeFacet = new TestGnosisBridgeFacet( IXDaiBridge(XDAI_BRIDGE) ); - bytes4[] memory functionSelectors = new bytes4[](2); - functionSelectors[0] = gnosisBridgeFacet + bytes4[] memory functionSelectors = new bytes4[](4); + functionSelectors[0] = _gnosisBridgeFacet .startBridgeTokensViaXDaiBridge .selector; - functionSelectors[1] = gnosisBridgeFacet + functionSelectors[1] = _gnosisBridgeFacet .swapAndStartBridgeTokensViaXDaiBridge .selector; + functionSelectors[2] = _gnosisBridgeFacet.addDex.selector; + functionSelectors[3] = _gnosisBridgeFacet + .setFunctionApprovalBySignature + .selector; - addFacet(diamond, address(gnosisBridgeFacet), functionSelectors); + addFacet(diamond, address(_gnosisBridgeFacet), functionSelectors); + + _gnosisBridgeFacet = TestGnosisBridgeFacet(address(diamond)); + + // whitelist DEXs / Periphery contracts + _gnosisBridgeFacet.addDex(address(uniswap)); + _gnosisBridgeFacet.addDex(address(gasZipPeriphery)); + _gnosisBridgeFacet.addDex(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 + ); - gnosisBridgeFacet = TestGnosisBridgeFacet(address(diamond)); + setFacetAddressInTestBase(address(gnosisBridgeFacet), "GnosisFacet"); } - function _getUniswapCalldataForERC20ToNativeSwap( + function _getLiFiDEXAggregatorCalldataForERC20ToNativeSwap( address sendingAssetId, uint256 fromAmount ) @@ -377,32 +432,14 @@ contract GasZipProtocolTest is DSTest, DiamondTest, TestHelpers { amountOutMin = amounts[1]; swapData = LibSwap.SwapData( - address(uniswap), - address(uniswap), + liFiDEXAggregator, + liFiDEXAggregator, sendingAssetId, - ADDRESS_WETH, + address(0), fromAmount, - abi.encodeWithSelector( - uniswap.swapExactTokensForETH.selector, - fromAmount, - amountOutMin, - path, - address(gasZipFacet), - block.timestamp + 20 seconds - ), - false // not required since tokens are already in diamond + // this is calldata for the DEXAggregator to swap 2 DAI to native + hex"2646478b0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000001bc16d674ec80000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000002f245b38c0d49000000000000000000000000b9a555095d3d45211072aef86d1622d1f6fdf31600000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000073026B175474E89094C44Da98b954EedeAC495271d0F01ffff00e92Cc0e5Db597066b3C26016b2fb32830401A31A01e43ca1Dee3F0fc1e2df73A0745674545F11A59F5000bb801C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc201ffff0200B9A555095D3d45211072aEf86D1622D1f6FDf31600000000000000000000000000", + true ); } } - -contract TestGnosisBridgeFacet is GnosisBridgeFacet { - constructor(IXDaiBridge _xDaiBridge) GnosisBridgeFacet(_xDaiBridge) {} - - function addDex(address _dex) external { - LibAllowList.addAllowedContract(_dex); - } - - function setFunctionApprovalBySignature(bytes4 _signature) external { - LibAllowList.addAllowedSelector(_signature); - } -} diff --git a/test/solidity/utils/TestBase.sol b/test/solidity/utils/TestBase.sol index 4b992d891..3a91c0996 100644 --- a/test/solidity/utils/TestBase.sol +++ b/test/solidity/utils/TestBase.sol @@ -336,7 +336,7 @@ abstract contract TestBase is Test, DiamondTest, ILiFi { //@dev: be careful that _facetTestContractAddress is set before calling this function function setDefaultSwapDataSingleDAItoETH() internal virtual { delete swapData; - // Swap DAI -> USDC + // Swap DAI -> ETH address[] memory path = new address[](2); path[0] = ADDRESS_DAI; path[1] = ADDRESS_WETH; From fe10183c00d172100660d9227ce20082334cb887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 23 Sep 2024 08:33:05 +0700 Subject: [PATCH 044/100] remove receiver address from GasZipData struct --- docs/GasZipFacet.md | 2 -- docs/GasZipPeriphery.md | 12 +++++++----- src/Facets/GasZipFacet.sol | 5 +---- src/Interfaces/IGasZip.sol | 1 - src/Periphery/GasZipPeriphery.sol | 16 ++++++++++------ test/solidity/Facets/GasZipFacet.t.sol | 3 +-- test/solidity/Periphery/GasZipPeriphery.t.sol | 12 +++++++----- 7 files changed, 26 insertions(+), 25 deletions(-) diff --git a/docs/GasZipFacet.md b/docs/GasZipFacet.md index 6d0e71b42..e0b51755e 100644 --- a/docs/GasZipFacet.md +++ b/docs/GasZipFacet.md @@ -22,10 +22,8 @@ This data is specific to Gas.Zip and is represented as the following struct type ```solidity /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) -/// @param receiver the address to receive the gas on dst chain struct GasZipData { uint256 destinationChains; - address receiver; } ``` diff --git a/docs/GasZipPeriphery.md b/docs/GasZipPeriphery.md index 766127376..d1b12db70 100644 --- a/docs/GasZipPeriphery.md +++ b/docs/GasZipPeriphery.md @@ -17,10 +17,12 @@ One for ERC20 tokens (these will be swapped into native before depositing to Gas /// Swaps are only allowed via the LiFiDEXAggregator /// @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 address should receive gas on which chains +/// @param _gasZipData contains information about which chains gas should be sent to +/// @param _receiver address the gas should be sent to function depositToGasZipERC20( LibSwap.SwapData calldata _swapData, - IGasZip.GasZipData calldata _gasZipData + IGasZip.GasZipData calldata _gasZipData, + address _receiver ) ``` @@ -29,9 +31,11 @@ and another for native tokens (these will be directly deposited) ```solidity /// @notice Deposits native tokens to the GasZip router contract /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge -/// @param _gasZipData contains information about which address should receive gas on which chains +/// @param _gasZipData contains information about which chains gas should be sent to +/// @param _receiver address the gas should be sent to function depositToGasZipNative( IGasZip.GasZipData calldata _gasZipData + address _receiver ) ``` @@ -43,9 +47,7 @@ This data is specific to Gas.Zip and is represented as the following struct type ```solidity /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) -/// @param receiver the address to receive the gas on dst chain struct GasZipData { uint256 destinationChains; - address receiver; } ``` diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 14ace9544..93fc6f4a0 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -96,13 +96,10 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { ILiFi.BridgeData memory _bridgeData, IGasZip.GasZipData calldata _gasZipData ) internal { - // should we conduct any calldata checks here? - // e.g. verify the receiver address (add parameter in _gasZipData and cross-check with bytes calldata) - // deposit native to Gas.zip (v1) https://dev.gas.zip/gas/code-examples/contractDeposit gasZipRouter.deposit{ value: _bridgeData.minAmount }( _gasZipData.destinationChains, - _gasZipData.receiver + _bridgeData.receiver ); emit LiFiTransferStarted(_bridgeData); diff --git a/src/Interfaces/IGasZip.sol b/src/Interfaces/IGasZip.sol index ba17d3b96..8d614e3b9 100644 --- a/src/Interfaces/IGasZip.sol +++ b/src/Interfaces/IGasZip.sol @@ -10,7 +10,6 @@ interface IGasZip { /// @param receiver the address to receive the gas on dst chain struct GasZipData { uint256 destinationChains; - address receiver; } function deposit( diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index b5a31e094..2b08b6b22 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -35,10 +35,12 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// Swaps are only allowed via the LiFiDEXAggregator /// @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 address should receive gas on which chains + /// @param _gasZipData contains information about which chains gas should be sent to + /// @param _receiver address the gas should be sent to function depositToGasZipERC20( LibSwap.SwapData calldata _swapData, - IGasZip.GasZipData calldata _gasZipData + IGasZip.GasZipData calldata _gasZipData, + address _receiver ) public { // deposit ERC20 asset from diamond LibAsset.depositAsset(_swapData.sendingAssetId, _swapData.fromAmount); @@ -64,19 +66,21 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { // deposit native tokens to Gas.zip protocol GasZipPeriphery(payable(address(this))).depositToGasZipNative{ value: swapOutputAmount - }(_gasZipData); + }(_gasZipData, _receiver); } /// @notice Deposits native tokens to the GasZip router contract /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge - /// @param _gasZipData contains information about which address should receive gas on which chains + /// @param _gasZipData contains information about which chains gas should be sent to + /// @param _receiver address the gas should be sent to function depositToGasZipNative( - IGasZip.GasZipData calldata _gasZipData + IGasZip.GasZipData calldata _gasZipData, + address _receiver ) public payable { // deposit native to Gas.zip (v1) https://dev.gas.zip/gas/code-examples/contractDeposit gasZipRouter.deposit{ value: msg.value }( _gasZipData.destinationChains, - _gasZipData.receiver + _receiver ); emit DepositedToGasZip(msg.value); diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index ceba7ba8e..3228a3e7f 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -87,8 +87,7 @@ contract GasZipFacetTest is TestBaseFacet { uint8[] memory chainIds = new uint8[](1); chainIds[0] = 17; // polygon gasZipData = IGasZip.GasZipData({ - destinationChains: gasZipFacet.getDestinationChainsValue(chainIds), - receiver: USER_RECEIVER + destinationChains: gasZipFacet.getDestinationChainsValue(chainIds) }); bridgeData.bridge = "GasZip"; diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index b9b6cc6f1..b8a13e010 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -66,8 +66,7 @@ contract GasZipPeripheryTest is TestBase { gnosisBridgeFacet = _getGnosisBridgeFacet(); defaultGasZipData = IGasZip.GasZipData({ - destinationChains: defaultDestinationChains, - receiver: USER_RECEIVER + destinationChains: defaultDestinationChains }); bridgeData.bridge = "gnosis"; @@ -90,7 +89,8 @@ contract GasZipPeripheryTest is TestBase { // deposit via GasZip periphery contract gasZipPeriphery.depositToGasZipNative{ value: defaultNativeAmount }( - defaultGasZipData + defaultGasZipData, + USER_RECEIVER ); } @@ -235,7 +235,8 @@ contract GasZipPeripheryTest is TestBase { nativeZipAmount, abi.encodeWithSelector( gasZipPeriphery.depositToGasZipNative.selector, - defaultGasZipData + defaultGasZipData, + USER_RECEIVER ), false ); @@ -351,7 +352,8 @@ contract GasZipPeripheryTest is TestBase { // execute the call gasZipPeriphery.depositToGasZipERC20( gasZipSwapData, - defaultGasZipData + defaultGasZipData, + USER_RECEIVER ); } From c9d79f018d9ce08092e1c2d33c479e7f7d0c0507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 23 Sep 2024 08:38:30 +0700 Subject: [PATCH 045/100] minor fixes --- src/Facets/GasZipFacet.sol | 2 +- src/Interfaces/IGasZip.sol | 1 - test/solidity/utils/TestBaseFacet.sol | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 93fc6f4a0..6af5110e2 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -13,7 +13,7 @@ import { ERC20 } from "solady/tokens/ERC20.sol"; /// @title GasZipFacet /// @author LI.FI (https://li.fi) -/// @notice Provides functionality to swap ERC20 tokens to native and deposit them to the gas.zip protocol (https://www.gas.zip/) +/// @notice Provides functionality to swap ERC20 tokens to native and deposit them to the gas.zip protocol (https://www.gas.zip/) /// @custom:version 2.0.0 contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; diff --git a/src/Interfaces/IGasZip.sol b/src/Interfaces/IGasZip.sol index 8d614e3b9..713869d45 100644 --- a/src/Interfaces/IGasZip.sol +++ b/src/Interfaces/IGasZip.sol @@ -7,7 +7,6 @@ pragma solidity 0.8.17; interface IGasZip { /// @dev GasZip-specific bridge data /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) - /// @param receiver the address to receive the gas on dst chain struct GasZipData { uint256 destinationChains; } diff --git a/test/solidity/utils/TestBaseFacet.sol b/test/solidity/utils/TestBaseFacet.sol index ef703fbfc..33375ee74 100644 --- a/test/solidity/utils/TestBaseFacet.sol +++ b/test/solidity/utils/TestBaseFacet.sol @@ -79,9 +79,6 @@ abstract contract TestBaseFacet is TestBase { bridgeData.sendingAssetId = ADDRESS_USDC; bridgeData.minAmount = amount; - console.log("minAmount : ", bridgeData.minAmount); - console.log(" amount : ", amount); - console.log(" balance: ", usdc.balanceOf(USER_SENDER)); //prepare check for events vm.expectEmit(true, true, true, true, _facetTestContractAddress); emit LiFiTransferStarted(bridgeData); From 0e3ae146c3dab6115a2853cffec921a2bc1d5719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 23 Sep 2024 09:13:42 +0700 Subject: [PATCH 046/100] some changes based on coderabbit review comments --- src/Facets/GasZipFacet.sol | 2 ++ src/Periphery/GasZipPeriphery.sol | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 6af5110e2..8aa282a46 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -110,6 +110,8 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { function getDestinationChainsValue( uint8[] memory _chainIds ) public pure returns (uint256 destinationChains) { + require(_chainIds.length <= 32, "Too many chain IDs"); + for (uint256 i = 0; i < _chainIds.length; i++) { // Shift destinationChains left by 8 bits and add the next chainID destinationChains = diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 2b08b6b22..70e34eadd 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -10,7 +10,6 @@ import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; -import { ERC20 } from "solady/tokens/ERC20.sol"; /// @title GasZipPeriphery /// @author LI.FI (https://li.fi) @@ -19,8 +18,6 @@ import { ERC20 } from "solady/tokens/ERC20.sol"; contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; - event DepositedToGasZip(uint256 amount); - /// State /// IGasZip public immutable gasZipRouter; address public immutable liFiDEXAggregator; @@ -82,8 +79,6 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { _gasZipData.destinationChains, _receiver ); - - emit DepositedToGasZip(msg.value); } /// @dev Returns a value that signals to Gas.zip to which chains gas should be sent in equal parts @@ -91,6 +86,8 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { function getDestinationChainsValue( uint8[] memory _chainIds ) public pure returns (uint256 destinationChains) { + require(_chainIds.length <= 32, "Too many chain IDs"); + for (uint256 i = 0; i < _chainIds.length; i++) { // Shift destinationChains left by 8 bits and add the next chainID destinationChains = From f19a83952ca7356ebc84008bb42a8c548f2ddce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 26 Sep 2024 11:11:03 +0700 Subject: [PATCH 047/100] opens swapAndBridge on GasZipFacet for both ERC20 and native --- package.json | 3 +- src/Facets/GasZipFacet.sol | 11 +- test/solidity/Facets/GasZipFacet.t.sol | 21 +- yarn.lock | 468 +++++++++++++++++++++++-- 4 files changed, 443 insertions(+), 60 deletions(-) diff --git a/package.json b/package.json index 3bf999c1a..4c687329c 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "hardhat-ethers": "^1.0.1", "light-spinner": "^1.0.4", "merkletreejs": "^0.3.11", + "node-gyp": "^10.2.0", "notify-send": "^0.1.2", "pino": "^7.9.2", "tsx": "^4.7.2", @@ -119,4 +120,4 @@ "solhint --fix" ] } -} \ No newline at end of file +} diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 8aa282a46..444930d00 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -53,7 +53,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { _startBridge(_bridgeData, _gasZipData); } - /// @notice Performs a swap from ERC20 to native before depositing to the gas.zip protocol + /// @notice Performs one or multiple actions (e.g. fee collection, swapping) that must end with the native token before depositing to the gas.zip protocol /// @param _bridgeData The core information needed for depositing /// @param _swapData An array of swap related data for performing swaps before bridging /// @param _gasZipData GasZip-specific bridge data @@ -70,14 +70,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { doesNotContainDestinationCalls(_bridgeData) validateBridgeData(_bridgeData) { - // this function / path shall only be used for ERC20 assets - if ( - !LibAsset.isNativeAsset( - _swapData[_swapData.length - 1].receivingAssetId - ) - ) revert OnlySwapsFromERC20ToNativeAllowed(); - - // deposit and swap ERC20 tokens to n ative + // deposit and swap ERC20 tokens to native _bridgeData.minAmount = _depositAndSwap( _bridgeData.transactionId, _bridgeData.minAmount, diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 3228a3e7f..e7ea2ce23 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -41,7 +41,7 @@ contract GasZipFacetTest is TestBaseFacet { function setUp() public { // set custom block no for mainnet forking - customBlockNumberForForking = 17484106; + customBlockNumberForForking = 20832175; initTestBase(); @@ -162,25 +162,6 @@ contract GasZipFacetTest is TestBaseFacet { // the startBridgeTokensViaGasZip can only be used for native tokens, therefore this test case is not applicable } - function testRevert_WillFailWhenTryingToSwapERC20ToERC20() public { - vm.startPrank(USER_SENDER); - - // prepare swapData for an ERC20 to ERC20 swap - setDefaultSwapDataSingleDAItoUSDC(); - - // set max approval - dai.approve(address(gasZipFacet), type(uint256).max); - - // prepare bridgeData - bridgeData.hasSourceSwaps = true; - - // expect that the call reverts - vm.expectRevert(OnlySwapsFromERC20ToNativeAllowed.selector); - - // initiate transaction - initiateSwapAndBridgeTxWithFacet(false); - } - function testRevert_WillFailWhenTryingToBridgeERC20() public { vm.startPrank(USER_SENDER); diff --git a/yarn.lock b/yarn.lock index 1fcd97361..9bf5bebeb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -756,6 +756,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" @@ -1191,6 +1203,24 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.5.tgz#97c217f1db795395c04404291937edb528f3f218" integrity sha512-U1RH9OQ1mWYQfb+moX5aTgGjpVVlOcpiFI47wwnaGG4kLhcTy90cNiapoqZenxcRAITVbr0/+QSduINL5EsUIQ== +"@npmcli/agent@^2.0.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5" + integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og== + dependencies: + agent-base "^7.1.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + lru-cache "^10.0.1" + socks-proxy-agent "^8.0.3" + +"@npmcli/fs@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.1.tgz#59cdaa5adca95d135fc00f2bb53f5771575ce726" + integrity sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg== + dependencies: + semver "^7.3.5" + "@octokit/auth-token@^5.0.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-5.1.1.tgz#3bbfe905111332a17f72d80bd0b51a3e2fa2cf07" @@ -1284,6 +1314,11 @@ dependencies: "@octokit/openapi-types" "^22.2.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -2131,6 +2166,11 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q== +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + abitype@0.9.8: version "0.9.8" resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.8.tgz#1f120b6b717459deafd213dfbf3a3dd1bf10ae8c" @@ -2235,6 +2275,13 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" @@ -2342,7 +2389,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -ansi-styles@^6.0.0: +ansi-styles@^6.0.0, ansi-styles@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== @@ -2939,6 +2986,24 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cacache@^18.0.0: + version "18.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.4.tgz#4601d7578dadb59c66044e157d02a3314682d6a5" + integrity sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^10.0.1" + minipass "^7.0.3" + minipass-collect "^2.0.1" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -3139,6 +3204,11 @@ chownr@^1.0.1, chownr@^1.1.1, chownr@^1.1.4: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -3546,7 +3616,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -3974,6 +4044,13 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +encoding@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -4001,6 +4078,11 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + errno@~0.1.1: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -4718,6 +4800,11 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + express@^4.14.0: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" @@ -5025,6 +5112,14 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -5142,6 +5237,20 @@ fs-minipass@^1.2.7: dependencies: minipass "^2.6.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-minipass@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + dependencies: + minipass "^7.0.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5307,6 +5416,18 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.2.2, glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -5529,7 +5650,7 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -5781,6 +5902,11 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -5797,6 +5923,14 @@ http-https@^1.0.0: resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" integrity sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg== +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -5830,6 +5964,14 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.1: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^4.3.0: version "4.3.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" @@ -5854,6 +5996,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + idna-uts46-hx@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" @@ -6006,6 +6155,14 @@ io-ts@1.10.4: dependencies: fp-ts "^1.0.0" +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -6149,6 +6306,11 @@ is-interactive@^2.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -6314,6 +6476,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== + isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -6355,6 +6522,15 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jayson@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.0.tgz#60dc946a85197317f2b1439d672a8b0a99cea2f9" @@ -6436,6 +6612,11 @@ jsbi@^3.1.1: resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.2.5.tgz#b37bb90e0e5c2814c1c2a1bcd8c729888a2e37d6" integrity sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ== +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -6851,6 +7032,11 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== +lru-cache@^10.0.1, lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -6885,6 +7071,24 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +make-fetch-happen@^13.0.0: + version "13.0.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36" + integrity sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA== + dependencies: + "@npmcli/agent" "^2.0.0" + cacache "^18.0.0" + http-cache-semantics "^4.1.1" + is-lambda "^1.0.1" + minipass "^7.0.2" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + proc-log "^4.2.0" + promise-retry "^2.0.1" + ssri "^10.0.0" + make-iterator@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" @@ -7098,6 +7302,45 @@ minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minipass-collect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-2.0.1.tgz#1621bc77e12258a12c60d34e2276ec5c20680863" + integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== + dependencies: + minipass "^7.0.3" + +minipass-fetch@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.5.tgz#f0f97e40580affc4a35cc4a1349f05ae36cb1e4c" + integrity sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg== + dependencies: + minipass "^7.0.3" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" @@ -7106,6 +7349,23 @@ minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -7113,6 +7373,14 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" +minizlib@^2.1.1, minizlib@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -7125,7 +7393,7 @@ mkdirp-promise@^5.0.1: dependencies: mkdirp "*" -mkdirp@*, mkdirp@^1.0.4: +mkdirp@*, mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -7290,7 +7558,7 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@0.6.3: +negotiator@0.6.3, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== @@ -7354,6 +7622,22 @@ node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== +node-gyp@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.2.0.tgz#80101c4aa4f7ab225f13fcc8daaaac4eb1a8dd86" + integrity sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw== + dependencies: + env-paths "^2.2.0" + exponential-backoff "^3.1.1" + glob "^10.3.10" + graceful-fs "^4.2.6" + make-fetch-happen "^13.0.0" + nopt "^7.0.0" + proc-log "^4.1.0" + semver "^7.3.5" + tar "^6.2.1" + which "^4.0.0" + node-plop@^0.31.1: version "0.31.1" resolved "https://registry.yarnpkg.com/node-plop/-/node-plop-0.31.1.tgz#20dd03e6ffe6d5c56bc38fc0b04df3475c33c7d5" @@ -7385,6 +7669,13 @@ nopt@3.x: dependencies: abbrev "1" +nopt@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" + integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== + dependencies: + abbrev "^2.0.0" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -7678,6 +7969,11 @@ p-try@^1.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== +package-json-from-dist@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -7824,6 +8120,14 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -7998,6 +8302,11 @@ prettier@^2.3.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +proc-log@^4.1.0, proc-log@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034" + integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -8023,6 +8332,14 @@ progress@^2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + proto3-json-serializer@^0.1.8: version "0.1.9" resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-0.1.9.tgz#705ddb41b009dd3e6fcd8123edd72926abf65a34" @@ -8489,6 +8806,11 @@ retry@0.13.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -8632,7 +8954,7 @@ safe-stable-stringify@^2.1.0, safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz#34694bd8a30575b7f94792aa51527551bd733d61" integrity sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA== -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -8698,6 +9020,11 @@ semver@^7.3.4, semver@^7.3.7, semver@^7.3.8: dependencies: lru-cache "^6.0.0" +semver@^7.3.5: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + semver@^7.5.4, semver@^7.6.0: version "7.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" @@ -8836,6 +9163,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -8900,6 +9232,11 @@ slice-ansi@^5.0.0: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -8908,6 +9245,23 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" +socks-proxy-agent@^8.0.3: + version "8.0.4" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.8.3" + +socks@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + solc@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" @@ -9037,6 +9391,11 @@ split2@^4.0.0: resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -9068,6 +9427,13 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +ssri@^10.0.0: + version "10.0.6" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5" + integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== + dependencies: + minipass "^7.0.3" + stacktrace-parser@^0.1.10: version "0.1.10" resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" @@ -9124,6 +9490,15 @@ string-format@^2.0.0: resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -9141,16 +9516,7 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.0: +string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== @@ -9196,6 +9562,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -9210,13 +9583,6 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -9404,6 +9770,18 @@ tar@^4.0.2: safe-buffer "^5.2.1" yallist "^3.1.1" +tar@^6.1.11, tar@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + text-encoding-utf-8@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" @@ -9787,6 +10165,20 @@ unfetch@^4.2.0: resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + universal-user-agent@^7.0.0, universal-user-agent@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-7.0.2.tgz#52e7d0e9b3dc4df06cc33cb2b9fd79041a54827e" @@ -10726,6 +11118,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +which@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== + dependencies: + isexe "^3.1.1" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -10749,6 +11148,15 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -10758,14 +11166,14 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" wrappy@1: version "1.0.2" From a469766be95eb48f6ed7cd22e98279726f050bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Fri, 27 Sep 2024 09:43:43 +0700 Subject: [PATCH 048/100] deployed to bsc staging --- deployments/_deployments_log_file.json | 28 +++++++++++++++++++++++++- deployments/bsc.diamond.staging.json | 27 +++++++++++++++---------- deployments/bsc.staging.json | 7 ++++--- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index c23564f36..df9076410 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22802,8 +22802,34 @@ "SALT": "", "VERIFIED": "true" } + ], + "2.0.0": [ + { + "ADDRESS": "0xbE176F5a3f0A1A751acE340Bd585E37109Ea6986", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-09-26 19:38:44", + "CONSTRUCTOR_ARGS": "0x00000000000000000000000085e5fb57844be79b42997c898d177a39f328ccf0", + "SALT": "", + "VERIFIED": "true" + } + ] + } + } + }, + "GasZipPeriphery": { + "bsc": { + "staging": { + "1.0.0": [ + { + "ADDRESS": "0x238dAC126ef963b8817385cB8a6264c321bA61a4", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-09-26 19:41:37", + "CONSTRUCTOR_ARGS": "0x00000000000000000000000085e5fb57844be79b42997c898d177a39f328ccf0000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd8642", + "SALT": "", + "VERIFIED": "true" + } ] } } } -} \ No newline at end of file +} diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 0be9714af..be9ea5fbd 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -1,6 +1,10 @@ { "LiFiDiamond": { "Facets": { + "0xf03AFcA857918BE01EBD6C6800Fc2974b8a9eBA2": { + "Name": "", + "Version": "" + }, "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6": { "Name": "GenericSwapFacetV3", "Version": "1.0.1" @@ -77,23 +81,24 @@ "Name": "StargateFacetV2", "Version": "1.0.1" }, - "0xA269cb81E6bBB86683558e449cb1bAFFdb155Bfc": { + "0xbE176F5a3f0A1A751acE340Bd585E37109Ea6986": { "Name": "GasZipFacet", - "Version": "1.0.0" + "Version": "2.0.0" } }, "Periphery": { - "ERC20Proxy": "", - "Executor": "", - "FeeCollector": "", + "ERC20Proxy": "0xf90a432dD1D0541470BC9C440d9dEc3659755238", + "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", + "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "LiFuelFeeCollector": "", - "Receiver": "", + "GasZipPeriphery": "", + "LiFiDEXAggregator": "", + "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "ServiceFeeCollector": "", - "TokenWrapper": "", - "LiFiDEXAggregator": "" + "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d", + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } -} +} \ No newline at end of file diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 4cff93937..71c0d32ff 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -28,8 +28,9 @@ "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A", "MayanBridgeFacet": "0x5Ba4FeD1DAd2fD057A9f687B399B8e4cF2368214", "MayanFacet": "0xd596C903d78870786c5DB0E448ce7F87A65A0daD", - "GasZipFacet": "0xA269cb81E6bBB86683558e449cb1bAFFdb155Bfc", + "GasZipFacet": "0xbE176F5a3f0A1A751acE340Bd585E37109Ea6986", "GenericSwapFacetV3": "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", - "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642" -} + "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", + "GasZipPeriphery": "0x238dAC126ef963b8817385cB8a6264c321bA61a4" +} \ No newline at end of file From bed2752ea2c31a475d77f597ac424eab714837ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 30 Sep 2024 15:47:01 +0700 Subject: [PATCH 049/100] updated gasZip to use V2 router --- config/gaszip.json | 32 +++++++++---------- src/Facets/GasZipFacet.sol | 11 +++---- src/Interfaces/IGasZip.sol | 10 +++--- src/Periphery/GasZipPeriphery.sol | 16 ++++------ test/solidity/Facets/GasZipFacet.t.sol | 30 ++++++++++++++--- test/solidity/Periphery/GasZipPeriphery.t.sol | 32 ++++++++++--------- 6 files changed, 75 insertions(+), 56 deletions(-) diff --git a/config/gaszip.json b/config/gaszip.json index a52993085..e656b2235 100644 --- a/config/gaszip.json +++ b/config/gaszip.json @@ -1,21 +1,21 @@ { "gasZipRouters": { "---ListOfRouterAddresses---": "https://dev.gas.zip/gas/chain-support/inbound", - "mainnet": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", - "arbitrum": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", - "avalanche": "0x5Fa63e70a6aa982bDaDbdc0f520B32dB7CADA1eF", - "base": "0x40a132f0ed6e1d63621586db1fabfb8a7587f2ac", - "bsc": "0x85e5fb57844be79b42997c898d177a39f328ccf0", - "blast": "0x6d4Ac5Ba1D649030227718C05ca4399F8522828b", - "gnosis": "0x87709e691A8C2037407eCdB4C271000ec2caC6Ee", - "linea": "0x6dB5Dd4eaaAd3C1B1890BDF81272a79E6223c051", - "mantle": "0x4C80aa9cEc1651DC42Bfca15BBe08bb6bbf8AbBC", - "metis": "0x73aaa8230Cd7564b088a8CD1fC7447B7D4C0Bb3c", - "mode": "0xB7D54342E5d993FE38897ECf7B5081eF5cdB5c18", - "optimism": "0x9e22ebec84c7e4c4bd6d4ae7ff6f4d436d6d8390", - "polygon": "0xea0b31a1bb831ca9b32d950fc17f369444744a8e", - "scroll": "0x5B5040638C7e6283d6D0780b09a854d395C12e59", - "xlayer": "0x391E7C679d29bD940d63be94AD22A25d25b5A604", - "zksync": "0x370475E7f574CA67C9bf9C43fc57191545C2a84b" + "mainnet": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "arbitrum": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "avalanche": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "base": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "bsc": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "blast": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "gnosis": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "linea": "0xA60768b03eB14d940F6c9a8553329B7F9037C91b", + "mantle": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "metis": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "mode": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "optimism": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "polygon": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "scroll": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "xlayer": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "zksync": "0x252fb662e4d7435d2a5ded8ec94d8932cf76c178" } } diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 444930d00..b0e1ebeaa 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -9,7 +9,6 @@ import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; -import { ERC20 } from "solady/tokens/ERC20.sol"; /// @title GasZipFacet /// @author LI.FI (https://li.fi) @@ -32,7 +31,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @notice Bridges tokens using the gas.zip protocol /// @dev this function only supports native flow. For ERC20 flows this facet should be used as a protocol step instead /// @param _bridgeData The core information needed for bridging - /// @param _gasZipData GasZip-specific bridge data + /// @param _gasZipData contains information which chains and address gas should be sent to function startBridgeTokensViaGasZip( ILiFi.BridgeData memory _bridgeData, IGasZip.GasZipData calldata _gasZipData @@ -56,7 +55,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @notice Performs one or multiple actions (e.g. fee collection, swapping) that must end with the native token before depositing to the gas.zip protocol /// @param _bridgeData The core information needed for depositing /// @param _swapData An array of swap related data for performing swaps before bridging - /// @param _gasZipData GasZip-specific bridge data + /// @param _gasZipData contains information which chains and address gas should be sent to function swapAndStartBridgeTokensViaGasZip( ILiFi.BridgeData memory _bridgeData, LibSwap.SwapData[] calldata _swapData, @@ -84,15 +83,15 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @dev Contains the business logic for depositing to GasZip protocol /// @param _bridgeData The core information needed for bridging - /// @param _gasZipData Data specific to Gas.zip protocol + /// @param _gasZipData contains information which chains and address gas should be sent to function _startBridge( ILiFi.BridgeData memory _bridgeData, IGasZip.GasZipData calldata _gasZipData ) internal { - // deposit native to Gas.zip (v1) https://dev.gas.zip/gas/code-examples/contractDeposit + // We are depositing to a new contract that supports deposits for EVM chains + Solana (therefore 'receiver' address is bytes32) gasZipRouter.deposit{ value: _bridgeData.minAmount }( _gasZipData.destinationChains, - _bridgeData.receiver + _gasZipData.receiver ); emit LiFiTransferStarted(_bridgeData); diff --git a/src/Interfaces/IGasZip.sol b/src/Interfaces/IGasZip.sol index 713869d45..1d3df7028 100644 --- a/src/Interfaces/IGasZip.sol +++ b/src/Interfaces/IGasZip.sol @@ -7,12 +7,14 @@ pragma solidity 0.8.17; interface IGasZip { /// @dev GasZip-specific bridge data /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) + /// @param receiver the address on destination chain(s) where gas should be sent to struct GasZipData { uint256 destinationChains; + // EVM addresses need to be padded with trailing 0s, e.g.: + // 0x391E7C679D29BD940D63BE94AD22A25D25B5A604000000000000000000000000 (correct) + // 0x000000000000000000000000391E7C679D29BD940D63BE94AD22A25D25B5A604 (incorrect) + bytes32 receiver; } - function deposit( - uint256 destinationChains, - address recipient - ) external payable; + function deposit(uint256 chains, bytes32 to) external payable; } diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 70e34eadd..943668a57 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -33,11 +33,9 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @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 - /// @param _receiver address the gas should be sent to function depositToGasZipERC20( LibSwap.SwapData calldata _swapData, - IGasZip.GasZipData calldata _gasZipData, - address _receiver + IGasZip.GasZipData calldata _gasZipData ) public { // deposit ERC20 asset from diamond LibAsset.depositAsset(_swapData.sendingAssetId, _swapData.fromAmount); @@ -63,21 +61,19 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { // deposit native tokens to Gas.zip protocol GasZipPeriphery(payable(address(this))).depositToGasZipNative{ value: swapOutputAmount - }(_gasZipData, _receiver); + }(_gasZipData); } /// @notice Deposits native tokens to the GasZip router contract /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge - /// @param _gasZipData contains information about which chains gas should be sent to - /// @param _receiver address the gas should be sent to + /// @param _gasZipData contains information which chains and address gas should be sent to function depositToGasZipNative( - IGasZip.GasZipData calldata _gasZipData, - address _receiver + IGasZip.GasZipData calldata _gasZipData ) public payable { - // deposit native to Gas.zip (v1) https://dev.gas.zip/gas/code-examples/contractDeposit + // We are depositing to a new contract that supports deposits for EVM chains + Solana (therefore 'receiver' address is bytes32) gasZipRouter.deposit{ value: msg.value }( _gasZipData.destinationChains, - _receiver + _gasZipData.receiver ); } diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index e7ea2ce23..a593ee2f0 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -24,24 +24,27 @@ contract TestGasZipFacet is GasZipFacet { contract GasZipFacetTest is TestBaseFacet { address public constant GAS_ZIP_ROUTER_MAINNET = - 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; + 0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762; TestGasZipFacet internal gasZipFacet; IGasZip.GasZipData internal gasZipData; uint256 public defaultDestinationChains = 96; + uint256 internal defaultNativeDepositAmount = 1e16; + uint256 internal defaultERC20DepositAmount = 1e8; address public defaultRecipientAddress = address(12345); address public defaultRefundAddress = address(56789); - // uint256 public defaultNativeAmount = 0.0006 ether; + bytes32 internal defaultReceiverBytes32 = + bytes32(uint256(uint160(USER_RECEIVER))); - event Deposit(address from, uint256 chains, uint256 amount, address to); + event Deposit(address from, uint256 chains, uint256 amount, bytes32 to); error OnlySwapsFromERC20ToNativeAllowed(); error OnlyNativeAllowed(); function setUp() public { // set custom block no for mainnet forking - customBlockNumberForForking = 20832175; + customBlockNumberForForking = 20828620; initTestBase(); @@ -87,7 +90,8 @@ contract GasZipFacetTest is TestBaseFacet { uint8[] memory chainIds = new uint8[](1); chainIds[0] = 17; // polygon gasZipData = IGasZip.GasZipData({ - destinationChains: gasZipFacet.getDestinationChainsValue(chainIds) + destinationChains: defaultDestinationChains, + receiver: bytes32(uint256(uint160(USER_RECEIVER))) }); bridgeData.bridge = "GasZip"; @@ -134,8 +138,16 @@ contract GasZipFacetTest is TestBaseFacet { // update bridgeData to use native bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = defaultERC20DepositAmount; //prepare check for events + vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); + emit Deposit( + address(gasZipFacet), + defaultDestinationChains, + defaultERC20DepositAmount, + defaultReceiverBytes32 + ); vm.expectEmit(true, true, true, true, _facetTestContractAddress); emit LiFiTransferStarted(bridgeData); @@ -149,8 +161,16 @@ contract GasZipFacetTest is TestBaseFacet { // customize bridgeData bridgeData.sendingAssetId = address(0); bridgeData.minAmount = 10000000000000000; // ~2 USD + bridgeData.minAmount = defaultNativeDepositAmount; // ~2 USD //prepare check for events + vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); + emit Deposit( + address(gasZipFacet), + defaultDestinationChains, + defaultNativeDepositAmount, + defaultReceiverBytes32 + ); vm.expectEmit(true, true, true, true, _facetTestContractAddress); emit LiFiTransferStarted(bridgeData); diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index b8a13e010..fb9f55d46 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -33,7 +33,7 @@ contract TestGasZipPeriphery is GasZipPeriphery { contract GasZipPeripheryTest is TestBase { address public constant GAS_ZIP_ROUTER_MAINNET = - 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390; + 0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762; address public constant LIFI_DEX_AGGREGATOR_MAINNET = 0xe43ca1Dee3F0fc1e2df73A0745674545F11A59F5; address internal constant XDAI_BRIDGE = @@ -44,13 +44,16 @@ contract GasZipPeripheryTest is TestBase { IGasZip.GasZipData internal defaultGasZipData; FeeCollector internal feeCollector; address internal liFiDEXAggregator = LIFI_DEX_AGGREGATOR_MAINNET; + bytes32 internal defaultReceiverBytes32 = + bytes32(uint256(uint160(USER_RECEIVER))); + uint256 internal defaultNativeDepositAmount = 1e16; uint256 public defaultDestinationChains = 96; - event Deposit(address from, uint256 chains, uint256 amount, address to); + event Deposit(address from, uint256 chains, uint256 amount, bytes32 to); function setUp() public { - customBlockNumberForForking = 20789064; + customBlockNumberForForking = 20862358; initTestBase(); // deploy contracts @@ -66,7 +69,8 @@ contract GasZipPeripheryTest is TestBase { gnosisBridgeFacet = _getGnosisBridgeFacet(); defaultGasZipData = IGasZip.GasZipData({ - destinationChains: defaultDestinationChains + destinationChains: defaultDestinationChains, + receiver: defaultReceiverBytes32 }); bridgeData.bridge = "gnosis"; @@ -77,21 +81,20 @@ contract GasZipPeripheryTest is TestBase { vm.label(address(gasZipPeriphery), "GasZipPeriphery"); } - function test_canDepositNative() public { + function test_canDepositNatives() public { // set up expected event vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); emit Deposit( address(gasZipPeriphery), defaultDestinationChains, - defaultNativeAmount, - USER_RECEIVER + defaultNativeDepositAmount, + defaultReceiverBytes32 ); // deposit via GasZip periphery contract - gasZipPeriphery.depositToGasZipNative{ value: defaultNativeAmount }( - defaultGasZipData, - USER_RECEIVER - ); + gasZipPeriphery.depositToGasZipNative{ + value: defaultNativeDepositAmount + }(defaultGasZipData); } function test_canCollectERC20FeesThenSwapToERC20ThenDepositThenBridge() @@ -236,7 +239,7 @@ contract GasZipPeripheryTest is TestBase { abi.encodeWithSelector( gasZipPeriphery.depositToGasZipNative.selector, defaultGasZipData, - USER_RECEIVER + defaultReceiverBytes32 ), false ); @@ -352,8 +355,7 @@ contract GasZipPeripheryTest is TestBase { // execute the call gasZipPeriphery.depositToGasZipERC20( gasZipSwapData, - defaultGasZipData, - USER_RECEIVER + defaultGasZipData ); } @@ -440,7 +442,7 @@ contract GasZipPeripheryTest is TestBase { address(0), fromAmount, // this is calldata for the DEXAggregator to swap 2 DAI to native - hex"2646478b0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000001bc16d674ec80000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000002f245b38c0d49000000000000000000000000b9a555095d3d45211072aef86d1622d1f6fdf31600000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000073026B175474E89094C44Da98b954EedeAC495271d0F01ffff00e92Cc0e5Db597066b3C26016b2fb32830401A31A01e43ca1Dee3F0fc1e2df73A0745674545F11A59F5000bb801C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc201ffff0200B9A555095D3d45211072aEf86D1622D1f6FDf31600000000000000000000000000", + hex"2646478b0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000001bc16d674ec80000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000002ae9d9fa81428000000000000000000000000b9a555095d3d45211072aef86d1622d1f6fdf31600000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000073026B175474E89094C44Da98b954EedeAC495271d0F01ffff00682831244b0E97946ABC52Cb1893Cce398De3A3501e43ca1Dee3F0fc1e2df73A0745674545F11A59F5000bb801C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc201ffff0200B9A555095D3d45211072aEf86D1622D1f6FDf31600000000000000000000000000", true ); } From 1187290c314368d4db793ed3bf4cd2bef44e926e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 30 Sep 2024 15:57:06 +0700 Subject: [PATCH 050/100] updates git actions and docs --- .github/workflows/enforceTestCoverage.yml | 6 ++++-- .github/workflows/forge.yml | 2 +- docs/GasZipFacet.md | 6 ++++++ docs/GasZipPeriphery.md | 14 ++++++++------ package.json | 2 +- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/workflows/enforceTestCoverage.yml b/.github/workflows/enforceTestCoverage.yml index b4482a50b..bb80ec423 100644 --- a/.github/workflows/enforceTestCoverage.yml +++ b/.github/workflows/enforceTestCoverage.yml @@ -44,9 +44,11 @@ jobs: - name: Install Dependencies run: forge install - - name: Generate Coverage Report + - name: Generate and Filter Coverage Report run: | - forge coverage --report lcov --force + echo "Generating coverage report now" + + forge coverage --report lcov --force --evm-version shanghai --ir-minimum echo "Filtering coverage report to only contain coverage info for 'src/'' folder now" diff --git a/.github/workflows/forge.yml b/.github/workflows/forge.yml index 56bb97a8c..0a86f8fc5 100644 --- a/.github/workflows/forge.yml +++ b/.github/workflows/forge.yml @@ -48,6 +48,6 @@ jobs: - name: Run forge tests (with auto-repeat in case of error) uses: Wandalen/wretry.action@v3.5.0 with: - command: forge test + command: forge test --evm-version 'shanghai' attempt_limit: 10 attempt_delay: 15000 diff --git a/docs/GasZipFacet.md b/docs/GasZipFacet.md index e0b51755e..153d9711e 100644 --- a/docs/GasZipFacet.md +++ b/docs/GasZipFacet.md @@ -21,9 +21,15 @@ Some of the methods listed above take a variable labeled `_gasZipData`. This data is specific to Gas.Zip and is represented as the following struct type: ```solidity +/// @dev GasZip-specific bridge data /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) +/// @param receiver the address on destination chain(s) where gas should be sent to struct GasZipData { uint256 destinationChains; + // EVM addresses need to be padded with trailing 0s, e.g.: + // 0x391E7C679D29BD940D63BE94AD22A25D25B5A604000000000000000000000000 (correct) + // 0x000000000000000000000000391E7C679D29BD940D63BE94AD22A25D25B5A604 (incorrect) + bytes32 receiver; } ``` diff --git a/docs/GasZipPeriphery.md b/docs/GasZipPeriphery.md index d1b12db70..a45a0b910 100644 --- a/docs/GasZipPeriphery.md +++ b/docs/GasZipPeriphery.md @@ -17,12 +17,10 @@ One for ERC20 tokens (these will be swapped into native before depositing to Gas /// Swaps are only allowed via the LiFiDEXAggregator /// @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 -/// @param _receiver address the gas should be sent to +/// @param _gasZipData contains information which chains and address gas should be sent to function depositToGasZipERC20( LibSwap.SwapData calldata _swapData, IGasZip.GasZipData calldata _gasZipData, - address _receiver ) ``` @@ -31,11 +29,9 @@ and another for native tokens (these will be directly deposited) ```solidity /// @notice Deposits native tokens to the GasZip router contract /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge -/// @param _gasZipData contains information about which chains gas should be sent to -/// @param _receiver address the gas should be sent to +/// @param _gasZipData contains information which chains and address gas should be sent to function depositToGasZipNative( IGasZip.GasZipData calldata _gasZipData - address _receiver ) ``` @@ -46,8 +42,14 @@ Some of the methods listed above take a variable labeled `_gasZipData`. This data is specific to Gas.Zip and is represented as the following struct type: ```solidity +/// @dev GasZip-specific bridge data /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) +/// @param receiver the address on destination chain(s) where gas should be sent to struct GasZipData { uint256 destinationChains; + // EVM addresses need to be padded with trailing 0s, e.g.: + // 0x391E7C679D29BD940D63BE94AD22A25D25B5A604000000000000000000000000 (correct) + // 0x000000000000000000000000391E7C679D29BD940D63BE94AD22A25D25B5A604 (incorrect) + bytes32 receiver; } ``` diff --git a/package.json b/package.json index 4c687329c..adae36943 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "format": "prettier --check \"**/*.{ts,js,sol}\"", "format:fix": "prettier --write \"**/*.{ts,js,sol}\"", "compile": "make build", - "test": "make test", + "test": "forge test --evm-version 'shanghai'", "test:fix": "npm run lint:fix; npm run format:fix; npm run test", "gas": "make snapshot", "coverage": "rm -rf coverage && rm -f lcov-filtered.info && rm -f lcov.info && forge coverage --report lcov && ts-node script/utils/filter_lcov.ts lcov.info lcov-filtered.info 'test/' 'script/' && genhtml lcov-filtered.info --branch-coverage --output-dir coverage && open coverage/index.html", From 07a48938d8c3ab8e25a30448f8ee0bf81edf42f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 1 Oct 2024 07:33:31 +0700 Subject: [PATCH 051/100] adds an amount parameter to native deposit function in periphery --- deployments/_deployments_log_file.json | 28 ++++++- deployments/bsc.diamond.staging.json | 27 ++++--- deployments/bsc.staging.json | 7 +- docs/GasZipPeriphery.md | 4 +- script/deploy/_targetState.json | 79 ++++++++++++++++++- src/Periphery/GasZipPeriphery.sol | 10 +-- test/solidity/Periphery/GasZipPeriphery.t.sol | 4 +- 7 files changed, 135 insertions(+), 24 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index c23564f36..0a6660b28 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22802,8 +22802,34 @@ "SALT": "", "VERIFIED": "true" } + ], + "2.0.0": [ + { + "ADDRESS": "0x88c9Ed1C3d723F4F5fe14B18b4C91737208791bD", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-09-30 15:59:21", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + } + }, + "GasZipPeriphery": { + "bsc": { + "staging": { + "1.0.0": [ + { + "ADDRESS": "0xD6bbE41D2d8008377B73d67c247698C4252D08cE", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-09-30 16:01:55", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd8642", + "SALT": "", + "VERIFIED": "true" + } ] } } } -} \ No newline at end of file +} diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 0be9714af..b5b4a9229 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -1,6 +1,10 @@ { "LiFiDiamond": { "Facets": { + "0xf03AFcA857918BE01EBD6C6800Fc2974b8a9eBA2": { + "Name": "", + "Version": "" + }, "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6": { "Name": "GenericSwapFacetV3", "Version": "1.0.1" @@ -77,23 +81,24 @@ "Name": "StargateFacetV2", "Version": "1.0.1" }, - "0xA269cb81E6bBB86683558e449cb1bAFFdb155Bfc": { + "0x88c9Ed1C3d723F4F5fe14B18b4C91737208791bD": { "Name": "GasZipFacet", - "Version": "1.0.0" + "Version": "2.0.0" } }, "Periphery": { - "ERC20Proxy": "", - "Executor": "", - "FeeCollector": "", + "ERC20Proxy": "0xf90a432dD1D0541470BC9C440d9dEc3659755238", + "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", + "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "LiFuelFeeCollector": "", - "Receiver": "", + "GasZipPeriphery": "0xD6bbE41D2d8008377B73d67c247698C4252D08cE", + "LiFiDEXAggregator": "", + "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "ServiceFeeCollector": "", - "TokenWrapper": "", - "LiFiDEXAggregator": "" + "ServiceFeeCollector": "0xdF4257A2f61E5D432Dedbc9c65917C268323421d", + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } -} +} \ No newline at end of file diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 4cff93937..50f8185ce 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -28,8 +28,9 @@ "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A", "MayanBridgeFacet": "0x5Ba4FeD1DAd2fD057A9f687B399B8e4cF2368214", "MayanFacet": "0xd596C903d78870786c5DB0E448ce7F87A65A0daD", - "GasZipFacet": "0xA269cb81E6bBB86683558e449cb1bAFFdb155Bfc", + "GasZipFacet": "0x88c9Ed1C3d723F4F5fe14B18b4C91737208791bD", "GenericSwapFacetV3": "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", - "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642" -} + "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", + "GasZipPeriphery": "0xD6bbE41D2d8008377B73d67c247698C4252D08cE" +} \ No newline at end of file diff --git a/docs/GasZipPeriphery.md b/docs/GasZipPeriphery.md index a45a0b910..215eed607 100644 --- a/docs/GasZipPeriphery.md +++ b/docs/GasZipPeriphery.md @@ -30,8 +30,10 @@ and another for native tokens (these will be directly deposited) /// @notice Deposits native tokens to the GasZip router contract /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge /// @param _gasZipData contains information which chains and address gas should be sent to +/// @param _amount the total amount to be deposited (will be split equally across all chains) function depositToGasZipNative( - IGasZip.GasZipData calldata _gasZipData + IGasZip.GasZipData calldata _gasZipData, + uint256 _amount ) ``` diff --git a/script/deploy/_targetState.json b/script/deploy/_targetState.json index b214e6f41..a5c260e53 100644 --- a/script/deploy/_targetState.json +++ b/script/deploy/_targetState.json @@ -265,6 +265,83 @@ "SymbiosisFacet": "1.0.0", "ThorSwapFacet": "1.2.0" } + }, + "staging": { + "LiFiDiamond": { + "DiamondCutFacet": "1.0.0", + "DiamondLoupeFacet": "1.0.0", + "OwnershipFacet": "1.0.0", + "DexManagerFacet": "1.0.1", + "AccessManagerFacet": "1.0.0", + "WithdrawFacet": "1.0.0", + "PeripheryRegistryFacet": "1.0.0", + "GenericSwapFacet": "1.0.0", + "GenericSwapFacetV3": "1.0.1", + "LIFuelFacet": "1.0.1", + "CalldataVerificationFacet": "1.1.1", + "StandardizedCallFacet": "1.1.0", + "LiFiDiamond": "1.0.0", + "ERC20Proxy": "1.0.0", + "Executor": "2.0.0", + "FeeCollector": "1.0.0", + "Receiver": "2.0.2", + "LiFuelFeeCollector": "1.0.1", + "TokenWrapper": "1.0.0", + "LiFiDEXAggregator": "1.0.0", + "AllBridgeFacet": "2.0.0", + "AmarokFacet": "3.0.0", + "AmarokFacetPacked": "1.0.0", + "CBridgeFacet": "1.0.0", + "CBridgeFacetPacked": "1.0.3", + "RelayerCelerIM": "2.0.0", + "CelerIMFacetMutable": "2.0.0", + "HyphenFacet": "1.0.0", + "MayanFacet": "1.0.0", + "SquidFacet": "1.0.0", + "StargateFacet": "2.2.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0", + "SymbiosisFacet": "1.0.0", + "ThorSwapFacet": "1.2.0", + "GasZipPeriphery": "1.0.0" + }, + "LiFiDiamondImmutable": { + "DiamondCutFacet": "1.0.0", + "DiamondLoupeFacet": "1.0.0", + "OwnershipFacet": "1.0.0", + "DexManagerFacet": "1.0.1", + "AccessManagerFacet": "1.0.0", + "WithdrawFacet": "1.0.0", + "PeripheryRegistryFacet": "1.0.0", + "GenericSwapFacet": "1.0.0", + "GenericSwapFacetV3": "1.0.1", + "LIFuelFacet": "1.0.1", + "CalldataVerificationFacet": "1.1.1", + "StandardizedCallFacet": "1.1.0", + "LiFiDiamondImmutable": "1.0.0", + "ERC20Proxy": "1.0.0", + "Executor": "2.0.0", + "FeeCollector": "1.0.0", + "Receiver": "2.0.2", + "LiFuelFeeCollector": "1.0.1", + "TokenWrapper": "1.0.0", + "LiFiDEXAggregator": "1.0.0", + "AllBridgeFacet": "2.0.0", + "AmarokFacet": "3.0.0", + "AmarokFacetPacked": "1.0.0", + "CBridgeFacet": "1.0.0", + "CBridgeFacetPacked": "1.0.3", + "RelayerCelerIM": "2.0.0", + "CelerIMFacetImmutable": "2.0.0", + "HyphenFacet": "1.0.0", + "MayanFacet": "1.0.0", + "SquidFacet": "1.0.0", + "StargateFacet": "2.2.0", + "StargateFacetV2": "1.0.1", + "ReceiverStargateV2": "1.0.0", + "SymbiosisFacet": "1.0.0", + "ThorSwapFacet": "1.2.0" + } } }, "gnosis": { @@ -2255,4 +2332,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 943668a57..c39cdacb5 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -59,19 +59,19 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { uint256 swapOutputAmount = abi.decode(res, (uint256)); // deposit native tokens to Gas.zip protocol - GasZipPeriphery(payable(address(this))).depositToGasZipNative{ - value: swapOutputAmount - }(_gasZipData); + depositToGasZipNative(_gasZipData, swapOutputAmount); } /// @notice Deposits native tokens to the GasZip router contract /// @dev this function can be used as a LibSwap.SwapData protocol step to combine it with any other bridge /// @param _gasZipData contains information which chains and address gas should be sent to + /// @param _amount the total amount to be deposited (will be split equally across all chains) function depositToGasZipNative( - IGasZip.GasZipData calldata _gasZipData + IGasZip.GasZipData calldata _gasZipData, + uint256 _amount ) public payable { // We are depositing to a new contract that supports deposits for EVM chains + Solana (therefore 'receiver' address is bytes32) - gasZipRouter.deposit{ value: msg.value }( + gasZipRouter.deposit{ value: _amount }( _gasZipData.destinationChains, _gasZipData.receiver ); diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index fb9f55d46..48cd9e4cc 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -94,7 +94,7 @@ contract GasZipPeripheryTest is TestBase { // deposit via GasZip periphery contract gasZipPeriphery.depositToGasZipNative{ value: defaultNativeDepositAmount - }(defaultGasZipData); + }(defaultGasZipData, defaultNativeDepositAmount); } function test_canCollectERC20FeesThenSwapToERC20ThenDepositThenBridge() @@ -239,7 +239,7 @@ contract GasZipPeripheryTest is TestBase { abi.encodeWithSelector( gasZipPeriphery.depositToGasZipNative.selector, defaultGasZipData, - defaultReceiverBytes32 + nativeZipAmount ), false ); From 2ba5c76f32fadb0110a3dcc0005ed7c60d6dcd3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 1 Oct 2024 07:38:47 +0700 Subject: [PATCH 052/100] redeployed GasZipPeriphery to bsc staging --- deployments/_deployments_log_file.json | 6 +++--- deployments/bsc.diamond.staging.json | 2 +- deployments/bsc.staging.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 0a6660b28..06749ade8 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22821,12 +22821,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0xD6bbE41D2d8008377B73d67c247698C4252D08cE", + "ADDRESS": "0x287fb5cBb8E4796e997cC2A92e5b37FD5434f485", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-09-30 16:01:55", + "TIMESTAMP": "2024-10-01 07:36:13", "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd8642", "SALT": "", - "VERIFIED": "true" + "VERIFIED": "false" } ] } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index b5b4a9229..5e164610f 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -91,7 +91,7 @@ "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "GasZipPeriphery": "0xD6bbE41D2d8008377B73d67c247698C4252D08cE", + "GasZipPeriphery": "0x287fb5cBb8E4796e997cC2A92e5b37FD5434f485", "LiFiDEXAggregator": "", "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 50f8185ce..f760adfae 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -32,5 +32,5 @@ "GenericSwapFacetV3": "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", - "GasZipPeriphery": "0xD6bbE41D2d8008377B73d67c247698C4252D08cE" + "GasZipPeriphery": "0x287fb5cBb8E4796e997cC2A92e5b37FD5434f485" } \ No newline at end of file From b87ae5768bda9685209831e441cefdd2219d9727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 2 Oct 2024 16:58:17 +0700 Subject: [PATCH 053/100] returns leftover native funds to msg.sender after deposit plus bsc staging redeployment --- deployments/_deployments_log_file.json | 6 +++--- deployments/bsc.diamond.staging.json | 8 ++------ deployments/bsc.staging.json | 4 ++-- src/Periphery/GasZipPeriphery.sol | 13 +++++++++++++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 7c40fbcd8..fc61a4d14 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22821,12 +22821,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0x287fb5cBb8E4796e997cC2A92e5b37FD5434f485", + "ADDRESS": "0xA4a16ff80E4156dA9d9656Ff83fa40a65e1EcaC9", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-01 07:36:13", + "TIMESTAMP": "2024-10-02 16:49:59", "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd8642", "SALT": "", - "VERIFIED": "true" + "VERIFIED": "false" } ] } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index aea335c6a..431be40ac 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -1,10 +1,6 @@ { "LiFiDiamond": { "Facets": { - "0xf03AFcA857918BE01EBD6C6800Fc2974b8a9eBA2": { - "Name": "", - "Version": "" - }, "0xf03AFcA857918BE01EBD6C6800Fc2974b8a9eBA2": { "Name": "", "Version": "" @@ -95,7 +91,7 @@ "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "GasZipPeriphery": "0x287fb5cBb8E4796e997cC2A92e5b37FD5434f485", + "GasZipPeriphery": "0xA4a16ff80E4156dA9d9656Ff83fa40a65e1EcaC9", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", @@ -105,4 +101,4 @@ "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } -} +} \ No newline at end of file diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 4f53895a3..14826bc55 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -32,5 +32,5 @@ "GenericSwapFacetV3": "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", - "GasZipPeriphery": "0x287fb5cBb8E4796e997cC2A92e5b37FD5434f485" -} + "GasZipPeriphery": "0xA4a16ff80E4156dA9d9656Ff83fa40a65e1EcaC9" +} \ No newline at end of file diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index c39cdacb5..5729471df 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -10,6 +10,7 @@ import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; +import { NativeAssetTransferFailed } from "../Errors/GenericErrors.sol"; /// @title GasZipPeriphery /// @author LI.FI (https://li.fi) @@ -75,6 +76,18 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { _gasZipData.destinationChains, _gasZipData.receiver ); + + // return unused native value to calling contract, if any + // this is required due to LI.FI backend-internal requirements (money flow) + uint256 remainingNativeBalance; + if (remainingNativeBalance > 0) { + (bool success, ) = msg.sender.call{ + value: remainingNativeBalance + }(""); + if (!success) { + revert NativeAssetTransferFailed(); + } + } } /// @dev Returns a value that signals to Gas.zip to which chains gas should be sent in equal parts From 14f43c9ba8e6ed0a2be7b7124f755388c976972c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 2 Oct 2024 20:18:56 +0700 Subject: [PATCH 054/100] bugfix + bsc staging redeploy --- deployments/_deployments_log_file.json | 6 ++--- deployments/bsc.diamond.staging.json | 2 +- deployments/bsc.staging.json | 2 +- src/Periphery/GasZipPeriphery.sol | 4 ++-- test/solidity/Periphery/GasZipPeriphery.t.sol | 22 ++++++++++++++++++- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index fc61a4d14..369199215 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22821,12 +22821,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0xA4a16ff80E4156dA9d9656Ff83fa40a65e1EcaC9", + "ADDRESS": "0xA2e19668bfe28c9bA5e01Ab75407C2627B1d0Ef8", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-02 16:49:59", + "TIMESTAMP": "2024-10-02 20:12:00", "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd8642", "SALT": "", - "VERIFIED": "false" + "VERIFIED": "true" } ] } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 431be40ac..58b7fe02e 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -91,7 +91,7 @@ "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "GasZipPeriphery": "0xA4a16ff80E4156dA9d9656Ff83fa40a65e1EcaC9", + "GasZipPeriphery": "0xA2e19668bfe28c9bA5e01Ab75407C2627B1d0Ef8", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 14826bc55..0075b003b 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -32,5 +32,5 @@ "GenericSwapFacetV3": "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", - "GasZipPeriphery": "0xA4a16ff80E4156dA9d9656Ff83fa40a65e1EcaC9" + "GasZipPeriphery": "0xA2e19668bfe28c9bA5e01Ab75407C2627B1d0Ef8" } \ No newline at end of file diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 5729471df..8f76210c3 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -77,9 +77,9 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { _gasZipData.receiver ); - // return unused native value to calling contract, if any + // return unused native value to msg.sender, if any // this is required due to LI.FI backend-internal requirements (money flow) - uint256 remainingNativeBalance; + uint256 remainingNativeBalance = msg.value - _amount; if (remainingNativeBalance > 0) { (bool success, ) = msg.sender.call{ value: remainingNativeBalance diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index 48cd9e4cc..95e4124b3 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -81,7 +81,7 @@ contract GasZipPeripheryTest is TestBase { vm.label(address(gasZipPeriphery), "GasZipPeriphery"); } - function test_canDepositNatives() public { + function test_CanDepositNatives() public { // set up expected event vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); emit Deposit( @@ -97,6 +97,26 @@ contract GasZipPeripheryTest is TestBase { }(defaultGasZipData, defaultNativeDepositAmount); } + 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( + address(gasZipPeriphery), + defaultDestinationChains, + defaultNativeDepositAmount, + defaultReceiverBytes32 + ); + + // deposit via GasZip periphery contract + gasZipPeriphery.depositToGasZipNative{ + value: defaultNativeDepositAmount * 5 + }(defaultGasZipData, defaultNativeDepositAmount); // sending 5 times the amount, expecting 4 times to be refunded + uint256 balanceAfter = USER_SENDER.balance; + assertEq(balanceBefore - defaultNativeDepositAmount, balanceAfter); + } + function test_canCollectERC20FeesThenSwapToERC20ThenDepositThenBridge() public { From 91d54ac2f74da564c92555c527fee56b3f3e0866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 3 Oct 2024 05:27:54 +0700 Subject: [PATCH 055/100] bugfix#2 + bsc staging redeploy --- deployments/_deployments_log_file.json | 6 +++--- deployments/bsc.diamond.staging.json | 2 +- deployments/bsc.staging.json | 2 +- src/Periphery/GasZipPeriphery.sol | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 369199215..311952342 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -22821,12 +22821,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0xA2e19668bfe28c9bA5e01Ab75407C2627B1d0Ef8", + "ADDRESS": "0x9071e05C7cA160dC4f29C5B8AA8c50b4fabA20aF", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-02 20:12:00", + "TIMESTAMP": "2024-10-03 05:27:08", "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd8642", "SALT": "", - "VERIFIED": "true" + "VERIFIED": "false" } ] } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 58b7fe02e..e39929006 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -91,7 +91,7 @@ "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "GasZipPeriphery": "0xA2e19668bfe28c9bA5e01Ab75407C2627B1d0Ef8", + "GasZipPeriphery": "0x9071e05C7cA160dC4f29C5B8AA8c50b4fabA20aF", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 0075b003b..7b0e0f552 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -32,5 +32,5 @@ "GenericSwapFacetV3": "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", - "GasZipPeriphery": "0xA2e19668bfe28c9bA5e01Ab75407C2627B1d0Ef8" + "GasZipPeriphery": "0x9071e05C7cA160dC4f29C5B8AA8c50b4fabA20aF" } \ No newline at end of file diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 8f76210c3..5ea3fc5d5 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -79,7 +79,7 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { // return unused native value to msg.sender, if any // this is required due to LI.FI backend-internal requirements (money flow) - uint256 remainingNativeBalance = msg.value - _amount; + uint256 remainingNativeBalance = address(this).balance; if (remainingNativeBalance > 0) { (bool success, ) = msg.sender.call{ value: remainingNativeBalance From 3bd0e5abb2117da970f3d2933e6cdce4dcf19acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Fri, 4 Oct 2024 09:40:58 +0700 Subject: [PATCH 056/100] adds a test case to increase coverage to 100% --- package.json | 2 +- test/solidity/Periphery/GasZipPeriphery.t.sol | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f25c73358..2138cd420 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "test": "forge test --evm-version 'shanghai'", "test:fix": "npm run lint:fix; npm run format:fix; npm run test", "gas": "make snapshot", - "coverage": "rm -rf coverage && rm -f lcov-filtered.info && rm -f lcov.info && forge coverage --report lcov && ts-node script/utils/filter_lcov.ts lcov.info lcov-filtered.info 'test/' 'script/' && genhtml lcov-filtered.info --branch-coverage --output-dir coverage && open coverage/index.html", + "coverage": "rm -rf coverage && rm -f lcov-filtered.info && rm -f lcov.info && forge coverage --report lcov --evm-version 'shanghai' && ts-node script/utils/filter_lcov.ts lcov.info lcov-filtered.info 'test/' 'script/' && genhtml lcov-filtered.info --branch-coverage --output-dir coverage --rc derive_function_end_line=0 && open coverage/index.html", "execute": "node ./_scripts.js run", "abi:generate": "make clean && forge build --skip script --skip test --skip Base --skip Test && hardhat diamondABI", "typechain": "make clean && forge build src && typechain --target ethers-v5 'out/*.sol/*.json' --out-dir typechain", diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index 95e4124b3..dd7988bf2 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -10,6 +10,8 @@ import { TestGnosisBridgeFacet } from "test/solidity/Facets/GnosisBridgeFacet.t. import { TestBase, console, ILiFi, ERC20 } from "../utils/TestBase.sol"; import { IXDaiBridge } from "lifi/Interfaces/IXDaiBridge.sol"; import { IGasZip } from "lifi/Interfaces/IGasZip.sol"; +import { NonETHReceiver } from "../utils/TestHelpers.sol"; +import { NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; // Stub GenericSwapFacet Contract contract TestGasZipPeriphery is GasZipPeriphery { @@ -81,7 +83,7 @@ contract GasZipPeripheryTest is TestBase { vm.label(address(gasZipPeriphery), "GasZipPeriphery"); } - function test_CanDepositNatives() public { + function test_CanDepositNative() public { // set up expected event vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); emit Deposit( @@ -117,6 +119,23 @@ contract GasZipPeripheryTest is TestBase { assertEq(balanceBefore - defaultNativeDepositAmount, balanceAfter); } + function testRevert_WillFailIfRemainingNativeCannotBeReturned() public { + // deploy contract that cannot receive ETH + NonETHReceiver nonETHReceiver = new NonETHReceiver(); + + deal(address(nonETHReceiver), 1 ether); + + vm.startPrank(address(nonETHReceiver)); + + // set up expected event + vm.expectRevert(NativeAssetTransferFailed.selector); + + // deposit via GasZip periphery contract + gasZipPeriphery.depositToGasZipNative{ + value: defaultNativeDepositAmount * 2 + }(defaultGasZipData, defaultNativeDepositAmount); // send twice the nativeAmount that is being deposited to trigger a refund + } + function test_canCollectERC20FeesThenSwapToERC20ThenDepositThenBridge() public { From 6bc644642db2d38d2a55a4e18ecd2c478e7b3ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Fri, 4 Oct 2024 09:49:53 +0700 Subject: [PATCH 057/100] minor fixes --- package.json | 2 +- src/Interfaces/IGasZip.sol | 2 +- test/solidity/Periphery/GasZipPeriphery.t.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2138cd420..0ca9feea6 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "hardhat-preprocessor": "^0.1.5", "husky": "^8.0.1", "lint-staged": "^13.2.3", + "node-gyp": "^10.2.0", "patch-package": "^6.4.7", "plop": "^3.1.2", "postinstall-postinstall": "^2.1.0", @@ -104,7 +105,6 @@ "hardhat-ethers": "^1.0.1", "light-spinner": "^1.0.4", "merkletreejs": "^0.3.11", - "node-gyp": "^10.2.0", "notify-send": "^0.1.2", "pino": "^7.9.2", "tsx": "^4.7.2", diff --git a/src/Interfaces/IGasZip.sol b/src/Interfaces/IGasZip.sol index 1d3df7028..2d755fbf3 100644 --- a/src/Interfaces/IGasZip.sol +++ b/src/Interfaces/IGasZip.sol @@ -16,5 +16,5 @@ interface IGasZip { bytes32 receiver; } - function deposit(uint256 chains, bytes32 to) external payable; + function deposit(uint256 destinationChains, bytes32 to) external payable; } diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index dd7988bf2..2db7e7990 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -367,7 +367,7 @@ contract GasZipPeripheryTest is TestBase { ); } - function testRevert_WillFailIfSwapViaLiFiDEXAggregratorIsUnsuccessful() + function testRevert_WillFailIfSwapViaLiFiDEXAggregatorIsUnsuccessful() public { vm.startPrank(USER_SENDER); From 193afc4e5dd2cc8d8b9284e9193cf6caeb458ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 08:34:26 +0700 Subject: [PATCH 058/100] gas optimization of helper function (audit issue #1) --- src/Facets/GasZipFacet.sol | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index b0e1ebeaa..3b0d3eb0d 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -100,11 +100,12 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @dev Returns a value that signals to Gas.zip to which chains gas should be sent in equal parts /// @param _chainIds a list of Gas.zip-specific chainIds (not the original chainIds), see https://dev.gas.zip/gas/chain-support/outbound function getDestinationChainsValue( - uint8[] memory _chainIds - ) public pure returns (uint256 destinationChains) { - require(_chainIds.length <= 32, "Too many chain IDs"); + uint8[] calldata _chainIds + ) external pure returns (uint256 destinationChains) { + uint256 length = _chainIds.length; + require(length <= 32, "Too many chain IDs"); - for (uint256 i = 0; i < _chainIds.length; i++) { + for (uint256 i; i < length; ++i) { // Shift destinationChains left by 8 bits and add the next chainID destinationChains = (destinationChains << 8) | From 883b3f4b2efcb9633044d12c21b82ce8eb1360dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 08:37:47 +0700 Subject: [PATCH 059/100] remove unused error (audit issue #2) --- foundry.toml | 2 +- src/Facets/GasZipFacet.sol | 1 - test/solidity/Facets/GasZipFacet.t.sol | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/foundry.toml b/foundry.toml index 178564553..580b215d1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,7 +1,7 @@ [profile.default] test = 'test/solidity' solc_version = '0.8.17' -evm_version = 'paris' +evm_version = 'shanghai' optimizer = true optimizer_runs = 1000000 sender = '0x00a329c0648769a73afac7f9381e08fb43dbea73' diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 3b0d3eb0d..06e1dfe9f 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -17,7 +17,6 @@ import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; - error OnlySwapsFromERC20ToNativeAllowed(); error OnlyNativeAllowed(); /// State /// diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 577d87f71..dfb8168a8 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -39,7 +39,6 @@ contract GasZipFacetTest is TestBaseFacet { event Deposit(address from, uint256 chains, uint256 amount, bytes32 to); - error OnlySwapsFromERC20ToNativeAllowed(); error OnlyNativeAllowed(); function setUp() public { From eac03e1653fdec1b651e36b7b53ee56b1aa71062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 09:10:07 +0700 Subject: [PATCH 060/100] adds check for msg.value==amount and removes refundExcessNative mod (audit issue#4) --- src/Facets/GasZipFacet.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 06e1dfe9f..7cd12dd46 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -9,6 +9,7 @@ import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; +import { InvalidCallData, CannotBridgeToSameNetwork, InvalidAmount } from "lifi/Errors/GenericErrors.sol"; /// @title GasZipFacet /// @author LI.FI (https://li.fi) @@ -38,7 +39,6 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { external payable nonReentrant - refundExcessNative(payable(msg.sender)) validateBridgeData(_bridgeData) doesNotContainSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) @@ -47,6 +47,9 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { if (!LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) revert OnlyNativeAllowed(); + // make sure that msg.value matches the to-be-deposited amount + if (msg.value != _bridgeData.minAmount) revert InvalidAmount(); + // deposit native to Gas.zip _startBridge(_bridgeData, _gasZipData); } @@ -64,9 +67,9 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { payable nonReentrant refundExcessNative(payable(msg.sender)) + validateBridgeData(_bridgeData) containsSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) - validateBridgeData(_bridgeData) { // deposit and swap ERC20 tokens to native _bridgeData.minAmount = _depositAndSwap( From 098847226ed5bae11ffd372df60035cc5ef56fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 09:19:16 +0700 Subject: [PATCH 061/100] replaces require with custom error (audit issue#6) --- src/Facets/GasZipFacet.sol | 4 +++- src/Periphery/GasZipPeriphery.sol | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 7cd12dd46..821b6985d 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -19,6 +19,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { using SafeTransferLib for address; error OnlyNativeAllowed(); + error TooManyChainIds(); /// State /// IGasZip public immutable gasZipRouter; @@ -105,7 +106,8 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { uint8[] calldata _chainIds ) external pure returns (uint256 destinationChains) { uint256 length = _chainIds.length; - require(length <= 32, "Too many chain IDs"); + + if (length > 32) revert TooManyChainIds(); for (uint256 i; i < length; ++i) { // Shift destinationChains left by 8 bits and add the next chainID diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 5ea3fc5d5..9e4500696 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -23,6 +23,9 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { IGasZip public immutable gasZipRouter; address public immutable liFiDEXAggregator; + /// Errors /// + error TooManyChainIds(); + /// Constructor /// constructor(address _gasZipRouter, address _liFiDEXAggregator) { gasZipRouter = IGasZip(_gasZipRouter); @@ -93,11 +96,13 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// @dev Returns a value that signals to Gas.zip to which chains gas should be sent in equal parts /// @param _chainIds a list of Gas.zip-specific chainIds (not the original chainIds), see https://dev.gas.zip/gas/chain-support/outbound function getDestinationChainsValue( - uint8[] memory _chainIds - ) public pure returns (uint256 destinationChains) { - require(_chainIds.length <= 32, "Too many chain IDs"); + uint8[] calldata _chainIds + ) external pure returns (uint256 destinationChains) { + uint256 length = _chainIds.length; + + if (length > 32) revert TooManyChainIds(); - for (uint256 i = 0; i < _chainIds.length; i++) { + for (uint256 i; i < length; ++i) { // Shift destinationChains left by 8 bits and add the next chainID destinationChains = (destinationChains << 8) | From 26ae13cfb1904403e85a3b1c39f3253720a9452a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 09:40:12 +0700 Subject: [PATCH 062/100] adds WithdrawablePeriphery for stuck token withdrawals (audit issue#8) --- .../deploy/facets/DeployGasZipPeriphery.s.sol | 11 +++++- src/Helpers/WithdrawablePeriphery.sol | 35 +++++++++++++++++++ src/Periphery/GasZipPeriphery.sol | 15 ++++++-- test/solidity/Periphery/GasZipPeriphery.t.sol | 8 +++-- 4 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 src/Helpers/WithdrawablePeriphery.sol diff --git a/script/deploy/facets/DeployGasZipPeriphery.s.sol b/script/deploy/facets/DeployGasZipPeriphery.s.sol index 1a814698a..47d967330 100644 --- a/script/deploy/facets/DeployGasZipPeriphery.s.sol +++ b/script/deploy/facets/DeployGasZipPeriphery.s.sol @@ -45,6 +45,15 @@ contract DeployScript is DeployScriptBase { address liFiDEXAggregator = json.readAddress(".LiFiDEXAggregator"); - return abi.encode(gasZipRouter, liFiDEXAggregator); + // get network's SAFE address to become contract owner for potential fund withdrawals + string memory networks = string.concat(root, "/config/networks.json"); + + string memory networksJson = vm.readFile(networks); + + address safeAddress = networksJson.readAddress( + string.concat(network, ".safeAdress") + ); + + return abi.encode(gasZipRouter, liFiDEXAggregator, safeAddress); } } diff --git a/src/Helpers/WithdrawablePeriphery.sol b/src/Helpers/WithdrawablePeriphery.sol new file mode 100644 index 000000000..66d201cb1 --- /dev/null +++ b/src/Helpers/WithdrawablePeriphery.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { TransferrableOwnership } from "./TransferrableOwnership.sol"; +import { LibAsset } from "../Libraries/LibAsset.sol"; +import { ExternalCallFailed } from "../Errors/GenericErrors.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; + +abstract contract WithdrawablePeriphery is TransferrableOwnership { + using SafeTransferLib for address; + + event TokensWithdrawn( + address assetId, + address payable receiver, + uint256 amount + ); + + constructor(address _owner) TransferrableOwnership(_owner) {} + + function withdrawToken( + address assetId, + address payable receiver, + uint256 amount + ) external onlyOwner { + if (LibAsset.isNativeAsset(assetId)) { + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = receiver.call{ value: amount }(""); + if (!success) revert ExternalCallFailed(); + } else { + assetId.safeTransfer(receiver, amount); + } + + emit TokensWithdrawn(assetId, receiver, amount); + } +} diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 9e4500696..d7fc42290 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -8,6 +8,7 @@ import { LibAsset, IERC20 } from "../Libraries/LibAsset.sol"; import { LibUtil } from "../Libraries/LibUtil.sol"; import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol"; import { SwapperV2 } from "../Helpers/SwapperV2.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { NativeAssetTransferFailed } from "../Errors/GenericErrors.sol"; @@ -16,7 +17,13 @@ import { NativeAssetTransferFailed } from "../Errors/GenericErrors.sol"; /// @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.0 -contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { +contract GasZipPeriphery is + ILiFi, + ReentrancyGuard, + SwapperV2, + Validatable, + WithdrawablePeriphery +{ using SafeTransferLib for address; /// State /// @@ -27,7 +34,11 @@ contract GasZipPeriphery is ILiFi, ReentrancyGuard, SwapperV2, Validatable { error TooManyChainIds(); /// Constructor /// - constructor(address _gasZipRouter, address _liFiDEXAggregator) { + constructor( + address _gasZipRouter, + address _liFiDEXAggregator, + address _owner + ) WithdrawablePeriphery(_owner) { gasZipRouter = IGasZip(_gasZipRouter); liFiDEXAggregator = _liFiDEXAggregator; } diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index 7fd36f08d..d26ba8117 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -17,8 +17,9 @@ import { NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; contract TestGasZipPeriphery is GasZipPeriphery { constructor( address gasZipRouter, - address liFiDEXAggregator - ) GasZipPeriphery(gasZipRouter, liFiDEXAggregator) {} + address liFiDEXAggregator, + address owner + ) GasZipPeriphery(gasZipRouter, liFiDEXAggregator, owner) {} function addDex(address _dex) external { LibAllowList.addAllowedContract(_dex); @@ -60,7 +61,8 @@ contract GasZipPeripheryTest is TestBase { // deploy contracts gasZipPeriphery = new TestGasZipPeriphery( GAS_ZIP_ROUTER_MAINNET, - LIFI_DEX_AGGREGATOR_MAINNET + LIFI_DEX_AGGREGATOR_MAINNET, + USER_DIAMOND_OWNER ); defaultUSDCAmount = 10 * 10 ** usdc.decimals(); // 10 USDC From b03e658b359f1696388e2aaeaf24a7c107ba462a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 09:46:48 +0700 Subject: [PATCH 063/100] adds WithdrawablePeriphery base contract for token withdrawals --- src/Helpers/WithdrawablePeriphery.sol | 35 ++++++ .../Helpers/WithdrawablePeriphery.t.sol | 116 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 src/Helpers/WithdrawablePeriphery.sol create mode 100644 test/solidity/Helpers/WithdrawablePeriphery.t.sol diff --git a/src/Helpers/WithdrawablePeriphery.sol b/src/Helpers/WithdrawablePeriphery.sol new file mode 100644 index 000000000..66d201cb1 --- /dev/null +++ b/src/Helpers/WithdrawablePeriphery.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import { TransferrableOwnership } from "./TransferrableOwnership.sol"; +import { LibAsset } from "../Libraries/LibAsset.sol"; +import { ExternalCallFailed } from "../Errors/GenericErrors.sol"; +import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; + +abstract contract WithdrawablePeriphery is TransferrableOwnership { + using SafeTransferLib for address; + + event TokensWithdrawn( + address assetId, + address payable receiver, + uint256 amount + ); + + constructor(address _owner) TransferrableOwnership(_owner) {} + + function withdrawToken( + address assetId, + address payable receiver, + uint256 amount + ) external onlyOwner { + if (LibAsset.isNativeAsset(assetId)) { + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = receiver.call{ value: amount }(""); + if (!success) revert ExternalCallFailed(); + } else { + assetId.safeTransfer(receiver, amount); + } + + emit TokensWithdrawn(assetId, receiver, amount); + } +} diff --git a/test/solidity/Helpers/WithdrawablePeriphery.t.sol b/test/solidity/Helpers/WithdrawablePeriphery.t.sol new file mode 100644 index 000000000..43e8c5e85 --- /dev/null +++ b/test/solidity/Helpers/WithdrawablePeriphery.t.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +/// @custom:version 1.0.0 +pragma solidity 0.8.17; + +import { WithdrawablePeriphery } from "lifi/Helpers/WithdrawablePeriphery.sol"; +import { TestBase } from "../utils/TestBase.sol"; +import { NonETHReceiver } from "../utils/TestHelpers.sol"; + +contract TestContract is WithdrawablePeriphery { + constructor(address _owner) WithdrawablePeriphery(_owner) {} +} + +contract WithdrawablePeripheryTest is TestBase { + WithdrawablePeriphery internal withdrawable; + event TokensWithdrawn( + address assetId, + address payable receiver, + uint256 amount + ); + error UnAuthorized(); + + function setUp() public { + initTestBase(); + + // deploy contract + withdrawable = new TestContract(USER_DIAMOND_OWNER); + + // fund contract with native and ERC20 + deal( + ADDRESS_USDC, + address(withdrawable), + 100_000 * 10 ** usdc.decimals() + ); + deal(address(withdrawable), 1 ether); + } + + function test_AllowsOwnerToWithdrawNative() public { + uint256 withdrawAmount = 0.1 ether; + + vm.startPrank(USER_DIAMOND_OWNER); + + vm.expectEmit(true, true, true, true, address(withdrawable)); + emit TokensWithdrawn( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + + withdrawable.withdrawToken( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function test_AllowsOwnerToWithdrawERC20() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + vm.startPrank(USER_DIAMOND_OWNER); + + vm.expectEmit(true, true, true, true, address(withdrawable)); + emit TokensWithdrawn( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNonOwnerTriesToWithdrawNative() public { + uint256 withdrawAmount = 0.1 ether; + + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + address(0), + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNonOwnerTriesToWithdrawERC20() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(USER_RECEIVER), + withdrawAmount + ); + } + + function testRevert_FailsIfNativeTokenTransferFails() public { + uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + + address nonETHReceiver = address(new NonETHReceiver()); + + vm.startPrank(USER_SENDER); + + vm.expectRevert(UnAuthorized.selector); + + withdrawable.withdrawToken( + ADDRESS_USDC, + payable(nonETHReceiver), + withdrawAmount + ); + } +} From bfa147fdd1b90a5dcd631d543491e149c17063a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 10:01:04 +0700 Subject: [PATCH 064/100] adds receiverAddress check (audit issue#9) --- src/Periphery/GasZipPeriphery.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index d7fc42290..6fc848e10 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -11,7 +11,7 @@ import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; -import { NativeAssetTransferFailed } from "../Errors/GenericErrors.sol"; +import { NativeAssetTransferFailed, InvalidCallData } from "../Errors/GenericErrors.sol"; /// @title GasZipPeriphery /// @author LI.FI (https://li.fi) @@ -85,6 +85,9 @@ contract GasZipPeriphery is IGasZip.GasZipData calldata _gasZipData, uint256 _amount ) public payable { + // make sure that receiverAddress is not 0 + if (_gasZipData.receiver == bytes32(0)) 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 }( _gasZipData.destinationChains, From bcf24aadc1ae01664f06b1f3088335d584c57f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 10:22:56 +0700 Subject: [PATCH 065/100] fix test --- test/solidity/Helpers/WithdrawablePeriphery.t.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/solidity/Helpers/WithdrawablePeriphery.t.sol b/test/solidity/Helpers/WithdrawablePeriphery.t.sol index 43e8c5e85..c6624bf3d 100644 --- a/test/solidity/Helpers/WithdrawablePeriphery.t.sol +++ b/test/solidity/Helpers/WithdrawablePeriphery.t.sol @@ -99,16 +99,16 @@ contract WithdrawablePeripheryTest is TestBase { } function testRevert_FailsIfNativeTokenTransferFails() public { - uint256 withdrawAmount = 10 * 10 ** usdc.decimals(); + uint256 withdrawAmount = 0.1 ether; address nonETHReceiver = address(new NonETHReceiver()); - vm.startPrank(USER_SENDER); + vm.startPrank(USER_DIAMOND_OWNER); - vm.expectRevert(UnAuthorized.selector); + vm.expectRevert(); withdrawable.withdrawToken( - ADDRESS_USDC, + address(0), payable(nonETHReceiver), withdrawAmount ); From 40bf7ee5ca7cb71048f14cb183d8244afec786c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 10:30:08 +0700 Subject: [PATCH 066/100] adds version tag to contract --- src/Helpers/WithdrawablePeriphery.sol | 1 + test/solidity/Helpers/WithdrawablePeriphery.t.sol | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/Helpers/WithdrawablePeriphery.sol b/src/Helpers/WithdrawablePeriphery.sol index 66d201cb1..9e4a08203 100644 --- a/src/Helpers/WithdrawablePeriphery.sol +++ b/src/Helpers/WithdrawablePeriphery.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MIT +/// @custom:version 1.0.0 pragma solidity 0.8.17; import { TransferrableOwnership } from "./TransferrableOwnership.sol"; diff --git a/test/solidity/Helpers/WithdrawablePeriphery.t.sol b/test/solidity/Helpers/WithdrawablePeriphery.t.sol index c6624bf3d..bba1a0f12 100644 --- a/test/solidity/Helpers/WithdrawablePeriphery.t.sol +++ b/test/solidity/Helpers/WithdrawablePeriphery.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.17; import { WithdrawablePeriphery } from "lifi/Helpers/WithdrawablePeriphery.sol"; + import { TestBase } from "../utils/TestBase.sol"; import { NonETHReceiver } from "../utils/TestHelpers.sol"; @@ -12,11 +13,13 @@ contract TestContract is WithdrawablePeriphery { contract WithdrawablePeripheryTest is TestBase { WithdrawablePeriphery internal withdrawable; + event TokensWithdrawn( address assetId, address payable receiver, uint256 amount ); + error UnAuthorized(); function setUp() public { From 29106c532101d34216ca77376bcf526ae6c7a793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 10 Oct 2024 12:16:31 +0700 Subject: [PATCH 067/100] ensures last swap output is native before bridging (audit issue#5) --- src/Facets/GasZipFacet.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 821b6985d..47d5860c8 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -72,6 +72,13 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { containsSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) { + // make sure that the output of the last swap step is native + if ( + !LibAsset.isNativeAsset( + _swapData[_swapData.length - 1].receivingAssetId + ) + ) revert InvalidCallData(); + // deposit and swap ERC20 tokens to native _bridgeData.minAmount = _depositAndSwap( _bridgeData.transactionId, From c000a46221ccabe51e4f6bd09140b362c6061262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 14 Oct 2024 15:41:58 +0700 Subject: [PATCH 068/100] removes validateBridgeData modifier and adds dedicated bridgeData checks (audit issue#3) --- src/Facets/GasZipFacet.sol | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 47d5860c8..5e7c06733 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -22,6 +22,8 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { error TooManyChainIds(); /// State /// + address public NON_EVM_RECEIVER_IDENTIFIER = + 0x11f111f111f111F111f111f111F111f111f111F1; IGasZip public immutable gasZipRouter; /// Constructor /// @@ -40,7 +42,6 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { external payable nonReentrant - validateBridgeData(_bridgeData) doesNotContainSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) { @@ -68,7 +69,6 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { payable nonReentrant refundExcessNative(payable(msg.sender)) - validateBridgeData(_bridgeData) containsSourceSwaps(_bridgeData) doesNotContainDestinationCalls(_bridgeData) { @@ -98,6 +98,21 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { ILiFi.BridgeData memory _bridgeData, IGasZip.GasZipData calldata _gasZipData ) internal { + // make sure receiver address has a value to prevent potential loss of funds + if (_gasZipData.receiver == bytes32(0)) revert InvalidCallData(); + + // validate that receiverAddress matches with bridgeData in case of EVM target chain + if ( + _bridgeData.receiver != NON_EVM_RECEIVER_IDENTIFIER && + _gasZipData.receiver != + bytes32(uint256(uint160(_bridgeData.receiver))) + ) revert InvalidCallData(); + + // validate bridgeData + // make sure destinationChainId is of a different network + if (_bridgeData.destinationChainId == block.chainid) + revert CannotBridgeToSameNetwork(); + // We are depositing to a new contract that supports deposits for EVM chains + Solana (therefore 'receiver' address is bytes32) gasZipRouter.deposit{ value: _bridgeData.minAmount }( _gasZipData.destinationChains, From 1e7b10dca5a1898f477348747f659f8c725a7285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 16 Oct 2024 16:44:48 +0700 Subject: [PATCH 069/100] redeployed gasZipPeriphery with PROD LiFiDEXAggregator address --- deployments/_deployments_log_file.json | 10 +++++----- deployments/bsc.diamond.staging.json | 2 +- deployments/bsc.staging.json | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 8c0e2b21c..d056a0e99 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -23429,12 +23429,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0x9071e05C7cA160dC4f29C5B8AA8c50b4fabA20aF", + "ADDRESS": "0xa76B7FdA492fA0a936E62C8a4166c407eD853074", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-03 05:27:08", - "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd8642", - "SALT": "", - "VERIFIED": "false" + "TIMESTAMP": "2024-10-16 15:56:52", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc00000000000000000000000020b6b31d76e054c3e4de6154feca385ca58c7c15", + "SALT": "20241016", + "VERIFIED": "true" } ] } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index a63467488..1b34192df 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -91,7 +91,7 @@ "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "GasZipPeriphery": "0x9071e05C7cA160dC4f29C5B8AA8c50b4fabA20aF", + "GasZipPeriphery": "0xa76B7FdA492fA0a936E62C8a4166c407eD853074", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 11a414fd0..093675bc7 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -33,5 +33,5 @@ "EmergencyPauseFacet": "0xf03AFcA857918BE01EBD6C6800Fc2974b8a9eBA2", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", - "GasZipPeriphery": "0x9071e05C7cA160dC4f29C5B8AA8c50b4fabA20aF" -} \ No newline at end of file + "GasZipPeriphery": "0xa76B7FdA492fA0a936E62C8a4166c407eD853074" +} From ca9b659e41ca4cbb32943e88b4dfa95e43330727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Fri, 18 Oct 2024 10:29:02 +0700 Subject: [PATCH 070/100] audit report added --- audit/auditLog.json | 13 +++++++++++++ audit/reports/2024.10.17_GasZip.pdf | Bin 0 -> 83172 bytes 2 files changed, 13 insertions(+) create mode 100644 audit/reports/2024.10.17_GasZip.pdf diff --git a/audit/auditLog.json b/audit/auditLog.json index d321e3a16..edbdea57c 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -13,12 +13,25 @@ "auditorGitHandle": "sujithsomraaj", "auditReportPath": "./audit/reports/2024.09.13_EmergencyPauseFacet.pdf", "auditCommitHash": "77441a088e0789513db4e068f7ef6c5c0988ee42" + }, + "audit20241017": { + "auditCompletedOn": "17.10.2024", + "auditedBy": "Sujith Somraaj (individual security researcher)", + "auditorGitHandle": "sujithsomraaj", + "auditReportPath": "./audit/reports/2024.10.17_GasZip.pdf", + "auditCommitHash": "6bc644642db2d38d2a55a4e18ecd2c478e7b3ff5" } }, "auditedContracts": { "EmergencyPauseFacet": { "1.0.0": ["audit20240913"] }, + "GasZipFacet": { + "2.0.0": ["audit20241017"] + }, + "GasZipPeriphery": { + "1.0.0": ["audit20241017"] + }, "GenericErrors": { "1.0.0": ["audit20240913"] }, diff --git a/audit/reports/2024.10.17_GasZip.pdf b/audit/reports/2024.10.17_GasZip.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f66aa8905b01fa4938599b3ae018c7a4c4193bdd GIT binary patch literal 83172 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 Ks;aL3Zd?Ej<4hy~ literal 0 HcmV?d00001 From be6845addb2c7cac5b013ef5b37b0a7cb8622fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 22 Oct 2024 08:48:34 +0700 Subject: [PATCH 071/100] change facet variable to constant --- package.json | 2 +- script/deploy/safe/confirm-safe-tx.ts | 2 + src/Facets/GasZipFacet.sol | 2 +- test/solidity/Facets/GasZipFacet.t.sol | 156 ++++++++++++++++++++++++- 4 files changed, 158 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f5bcaafea..c8aa99d9e 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "test": "forge test --evm-version 'shanghai'", "test:fix": "npm run lint:fix; npm run format:fix; npm run test", "gas": "forge snapshot --match-path 'test/solidity/Gas/**/*' -vvv", - "coverage": "rm -rf coverage && rm -f lcov-filtered.info && rm -f lcov.info && forge coverage --report lcov && ts-node script/utils/filter_lcov.ts lcov.info lcov-filtered.info 'test/' 'script/' && genhtml lcov-filtered.info --branch-coverage --output-dir coverage && open coverage/index.html", + "coverage": "rm -rf coverage && rm -f lcov-filtered.info && rm -f lcov.info && forge coverage --evm-version 'shanghai' --report lcov && ts-node script/utils/filter_lcov.ts lcov.info lcov-filtered.info 'test/' 'script/' && genhtml lcov-filtered.info --branch-coverage --output-dir coverage && open coverage/index.html && rm -f lcov-filtered.info && rm -f lcov.info", "execute": "node ./_scripts.js run", "abi:generate": "forge clean && rm -fr typechain/* && forge build --skip script --skip test --skip Base --skip Test && hardhat diamondABI", "typechain": "forge clean && rm -rf typechain/* && forge build src && typechain --target ethers-v5 'out/*.sol/*.json' --out-dir typechain", diff --git a/script/deploy/safe/confirm-safe-tx.ts b/script/deploy/safe/confirm-safe-tx.ts index d2c71184c..ee9617ae5 100644 --- a/script/deploy/safe/confirm-safe-tx.ts +++ b/script/deploy/safe/confirm-safe-tx.ts @@ -46,12 +46,14 @@ const skipNetworks: string[] = [ // 'moonbeam', // 'moonriver', // 'optimism', + // 'opbnb', // 'polygon', // 'polygonzkevm', // 'rootstock', // 'scroll', // 'sei', // 'taiko', + // 'xlayer', // 'zksync', ] const defaultNetworks = allNetworks.filter( diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 5e7c06733..315ec0367 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -22,7 +22,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { error TooManyChainIds(); /// State /// - address public NON_EVM_RECEIVER_IDENTIFIER = + address public constant NON_EVM_RECEIVER_IDENTIFIER = 0x11f111f111f111F111f111f111F111f111f111F1; IGasZip public immutable gasZipRouter; diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index dfb8168a8..fc1316fa8 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.17; import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; import { IGasZip } from "lifi/Interfaces/IGasZip.sol"; import { ILiFi, LibSwap, LibAllowList, TestBaseFacet, console, ERC20 } from "../utils/TestBaseFacet.sol"; -import { InvalidCallData } from "lifi/Errors/GenericErrors.sol"; +import { InvalidCallData, CannotBridgeToSameNetwork, InvalidAmount, InvalidReceiver } from "lifi/Errors/GenericErrors.sol"; // Stub GenericSwapFacet Contract contract TestGasZipFacet is GasZipFacet { @@ -25,6 +25,8 @@ contract TestGasZipFacet is GasZipFacet { contract GasZipFacetTest is TestBaseFacet { address public constant GAS_ZIP_ROUTER_MAINNET = 0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762; + address public constant NON_EVM_RECEIVER_IDENTIFIER = + 0x11f111f111f111F111f111f111F111f111f111F1; TestGasZipFacet internal gasZipFacet; IGasZip.GasZipData internal gasZipData; @@ -40,6 +42,7 @@ contract GasZipFacetTest is TestBaseFacet { event Deposit(address from, uint256 chains, uint256 amount, bytes32 to); error OnlyNativeAllowed(); + error TooManyChainIds(); function setUp() public { // set custom block no for mainnet forking @@ -159,7 +162,6 @@ contract GasZipFacetTest is TestBaseFacet { vm.startPrank(USER_SENDER); // customize bridgeData bridgeData.sendingAssetId = address(0); - bridgeData.minAmount = 10000000000000000; // ~2 USD bridgeData.minAmount = defaultNativeDepositAmount; // ~2 USD //prepare check for events @@ -274,6 +276,106 @@ contract GasZipFacetTest is TestBaseFacet { initiateSwapAndBridgeTxWithFacet(false); } + function testBase_Revert_BridgeAndSwapWithInvalidReceiverAddress() + public + override + { + // since the 'validateBridgeData' modifier is not used, a different error is thrown here + + vm.startPrank(USER_SENDER); + // prepare bridgeData + bridgeData.receiver = address(0); + bridgeData.hasSourceSwaps = true; + + setDefaultSwapDataSingleDAItoUSDC(); + + vm.expectRevert(InvalidCallData.selector); + + initiateSwapAndBridgeTxWithFacet(false); + vm.stopPrank(); + } + + function testBase_Revert_SwapAndBridgeWithInvalidAmount() public override { + // since the '' modifier is not used, a different error is thrown here + + vm.startPrank(USER_SENDER); + // prepare bridgeData + bridgeData.hasSourceSwaps = true; + bridgeData.minAmount = 0; + + setDefaultSwapDataSingleDAItoUSDC(); + + vm.expectRevert(InvalidCallData.selector); + + initiateSwapAndBridgeTxWithFacet(false); + vm.stopPrank(); + } + + function testBase_Revert_BridgeToSameChainId() public override { + // we need to test this with native instead of ERC20 for this facet, therefore override + + vm.startPrank(USER_SENDER); + // customize bridgeData + bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = defaultNativeDepositAmount; // ~2 USD + bridgeData.destinationChainId = block.chainid; + + vm.expectRevert(CannotBridgeToSameNetwork.selector); + + initiateBridgeTxWithFacet(true); + vm.stopPrank(); + } + + function testBase_Revert_BridgeWithInvalidAmount() public override { + // we need to test this with native instead of ERC20 for this facet, therefore override + + vm.startPrank(USER_SENDER); + // customize bridgeData + bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = 0; + + // will fail when trying to send value that it doesnt have + vm.expectRevert(); + + initiateBridgeTxWithFacet(true); + vm.stopPrank(); + } + + function testBase_Revert_BridgeWithInvalidReceiverAddress() + public + override + { + // we need to test this with native instead of ERC20 for this facet, therefore override + + vm.startPrank(USER_SENDER); + // prepare bridgeData + bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = defaultNativeDepositAmount; // ~2 USD + gasZipData.receiver = bytes32(0); + + vm.expectRevert(InvalidCallData.selector); + + initiateBridgeTxWithFacet(true); + vm.stopPrank(); + } + + function testBase_Revert_SwapAndBridgeToSameChainId() public override { + // we need to test this with native swap output instead of ERC20 for this facet, therefore override + + vm.startPrank(USER_SENDER); + // prepare bridgeData + bridgeData.destinationChainId = block.chainid; + bridgeData.hasSourceSwaps = true; + + setDefaultSwapDataSingleDAItoETH(); // changed to native output calldata + dai.approve(_facetTestContractAddress, swapData[0].fromAmount); + + vm.expectRevert(CannotBridgeToSameNetwork.selector); + + initiateSwapAndBridgeTxWithFacet(false); + vm.stopPrank(); + } + function test_getDestinationChainsValueReturnsCorrectValues() public { // case 1 uint8[] memory chainIds = new uint8[](1); @@ -298,4 +400,54 @@ contract GasZipFacetTest is TestBaseFacet { assertEq(gasZipFacet.getDestinationChainsValue(chainIds), 65336774203); } + + function testRevert_WillFailIfMsgValueDoesNotMatchBridgeDataAmount() + public + { + vm.startPrank(USER_SENDER); + + // update bridgeData to use native + bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = defaultERC20DepositAmount; + + vm.expectRevert(InvalidAmount.selector); + + gasZipFacet.startBridgeTokensViaGasZip{ + value: bridgeData.minAmount - 1 + }(bridgeData, gasZipData); + } + + function testRevert_WillFailIfMoreThan32ChainIds() public { + vm.startPrank(USER_SENDER); + + uint8[] memory chainIds = new uint8[](33); + + vm.expectRevert(TooManyChainIds.selector); + + gasZipFacet.getDestinationChainsValue(chainIds); + } + + function testRevert_WillFailIfEVMReceiverAddressesDontMatch() public { + vm.startPrank(USER_SENDER); + // customize bridgeData + bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = defaultNativeDepositAmount; // ~2 USD + bridgeData.receiver = USER_PAUSER; + + vm.expectRevert(InvalidCallData.selector); + + initiateBridgeTxWithFacet(true); + vm.stopPrank(); + } + + function test_WillNotFailIfNonEVMReceiverAddressesDontMatch() public { + vm.startPrank(USER_SENDER); + // customize bridgeData + bridgeData.sendingAssetId = address(0); + bridgeData.minAmount = defaultNativeDepositAmount; // ~2 USD + bridgeData.receiver = NON_EVM_RECEIVER_IDENTIFIER; + + initiateBridgeTxWithFacet(true); + vm.stopPrank(); + } } From cd422f7ca131c77f490ea59fbf897b3fdcb96cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 22 Oct 2024 09:19:26 +0700 Subject: [PATCH 072/100] change parameter name and order in GasZipData --- docs/GasZipFacet.md | 6 +++--- docs/GasZipPeriphery.md | 6 +++--- src/Facets/GasZipFacet.sol | 7 ++++--- src/Interfaces/IGasZip.sol | 6 +++--- src/Periphery/GasZipPeriphery.sol | 5 +++-- test/solidity/Facets/GasZipFacet.t.sol | 6 +++--- test/solidity/Periphery/GasZipPeriphery.t.sol | 4 ++-- 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/docs/GasZipFacet.md b/docs/GasZipFacet.md index 153d9711e..e3af721d0 100644 --- a/docs/GasZipFacet.md +++ b/docs/GasZipFacet.md @@ -22,14 +22,14 @@ This data is specific to Gas.Zip and is represented as the following struct type ```solidity /// @dev GasZip-specific bridge data +/// @param receiverAddress the address on destination chain(s) where gas should be sent to /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) -/// @param receiver the address on destination chain(s) where gas should be sent to struct GasZipData { - uint256 destinationChains; + bytes32 receiverAddress; // EVM addresses need to be padded with trailing 0s, e.g.: // 0x391E7C679D29BD940D63BE94AD22A25D25B5A604000000000000000000000000 (correct) // 0x000000000000000000000000391E7C679D29BD940D63BE94AD22A25D25B5A604 (incorrect) - bytes32 receiver; + uint256 destinationChains; } ``` diff --git a/docs/GasZipPeriphery.md b/docs/GasZipPeriphery.md index 215eed607..b4f2edd82 100644 --- a/docs/GasZipPeriphery.md +++ b/docs/GasZipPeriphery.md @@ -45,13 +45,13 @@ This data is specific to Gas.Zip and is represented as the following struct type ```solidity /// @dev GasZip-specific bridge data +/// @param receiverAddress the address on destination chain(s) where gas should be sent to /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) -/// @param receiver the address on destination chain(s) where gas should be sent to struct GasZipData { - uint256 destinationChains; + bytes32 receiverAddress; // EVM addresses need to be padded with trailing 0s, e.g.: // 0x391E7C679D29BD940D63BE94AD22A25D25B5A604000000000000000000000000 (correct) // 0x000000000000000000000000391E7C679D29BD940D63BE94AD22A25D25B5A604 (incorrect) - bytes32 receiver; + uint256 destinationChains; } ``` diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 315ec0367..2198cfe1e 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -99,12 +99,13 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { IGasZip.GasZipData calldata _gasZipData ) internal { // make sure receiver address has a value to prevent potential loss of funds - if (_gasZipData.receiver == bytes32(0)) revert InvalidCallData(); + if (_gasZipData.receiverAddress == bytes32(0)) + revert InvalidCallData(); // validate that receiverAddress matches with bridgeData in case of EVM target chain if ( _bridgeData.receiver != NON_EVM_RECEIVER_IDENTIFIER && - _gasZipData.receiver != + _gasZipData.receiverAddress != bytes32(uint256(uint160(_bridgeData.receiver))) ) revert InvalidCallData(); @@ -116,7 +117,7 @@ contract GasZipFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { // We are depositing to a new contract that supports deposits for EVM chains + Solana (therefore 'receiver' address is bytes32) gasZipRouter.deposit{ value: _bridgeData.minAmount }( _gasZipData.destinationChains, - _gasZipData.receiver + _gasZipData.receiverAddress ); emit LiFiTransferStarted(_bridgeData); diff --git a/src/Interfaces/IGasZip.sol b/src/Interfaces/IGasZip.sol index 2d755fbf3..bc014fd8e 100644 --- a/src/Interfaces/IGasZip.sol +++ b/src/Interfaces/IGasZip.sol @@ -6,14 +6,14 @@ pragma solidity 0.8.17; /// @author LI.FI (https://li.fi) interface IGasZip { /// @dev GasZip-specific bridge data + /// @param receiverAddress the address on destination chain(s) where gas should be sent to /// @param destinationChains a value that represents a list of chains to which gas should be distributed (see https://dev.gas.zip/gas/code-examples/deposit for more details) - /// @param receiver the address on destination chain(s) where gas should be sent to struct GasZipData { - uint256 destinationChains; + bytes32 receiverAddress; // EVM addresses need to be padded with trailing 0s, e.g.: // 0x391E7C679D29BD940D63BE94AD22A25D25B5A604000000000000000000000000 (correct) // 0x000000000000000000000000391E7C679D29BD940D63BE94AD22A25D25B5A604 (incorrect) - bytes32 receiver; + uint256 destinationChains; } function deposit(uint256 destinationChains, bytes32 to) external payable; diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 6fc848e10..e4cfbfc12 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -86,12 +86,13 @@ contract GasZipPeriphery is uint256 _amount ) public payable { // make sure that receiverAddress is not 0 - if (_gasZipData.receiver == bytes32(0)) revert InvalidCallData(); + if (_gasZipData.receiverAddress == bytes32(0)) + 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 }( _gasZipData.destinationChains, - _gasZipData.receiver + _gasZipData.receiverAddress ); // return unused native value to msg.sender, if any diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index fc1316fa8..812102a3c 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -92,8 +92,8 @@ contract GasZipFacetTest is TestBaseFacet { uint8[] memory chainIds = new uint8[](1); chainIds[0] = 17; // polygon gasZipData = IGasZip.GasZipData({ - destinationChains: defaultDestinationChains, - receiver: bytes32(uint256(uint160(USER_RECEIVER))) + receiverAddress: bytes32(uint256(uint160(USER_RECEIVER))), + destinationChains: defaultDestinationChains }); bridgeData.bridge = "GasZip"; @@ -351,7 +351,7 @@ contract GasZipFacetTest is TestBaseFacet { // prepare bridgeData bridgeData.sendingAssetId = address(0); bridgeData.minAmount = defaultNativeDepositAmount; // ~2 USD - gasZipData.receiver = bytes32(0); + gasZipData.receiverAddress = bytes32(0); vm.expectRevert(InvalidCallData.selector); diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index d26ba8117..24b93b98b 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -70,8 +70,8 @@ contract GasZipPeripheryTest is TestBase { gnosisBridgeFacet = _getGnosisBridgeFacet(); defaultGasZipData = IGasZip.GasZipData({ - destinationChains: defaultDestinationChains, - receiver: defaultReceiverBytes32 + receiverAddress: defaultReceiverBytes32, + destinationChains: defaultDestinationChains }); bridgeData.bridge = "gnosis"; From d07da1bafb68e1a01ea8178a84a18496ec45cf14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 22 Oct 2024 09:44:30 +0700 Subject: [PATCH 073/100] redeployed to BSC staging --- deployments/_deployments_log_file.json | 14 +++++++------- deployments/bsc.diamond.staging.json | 16 ++++++++-------- deployments/bsc.staging.json | 6 +++--- script/deploy/facets/DeployGasZipPeriphery.s.sol | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 772f14e82..9d9d538c2 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -23413,9 +23413,9 @@ ], "2.0.0": [ { - "ADDRESS": "0x88c9Ed1C3d723F4F5fe14B18b4C91737208791bD", + "ADDRESS": "0x0DAff7e73fDb2bbaDa232A16a5BEA72463893E35", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-09-30 15:59:21", + "TIMESTAMP": "2024-10-22 09:32:31", "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", "SALT": "", "VERIFIED": "true" @@ -23429,12 +23429,12 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0xa76B7FdA492fA0a936E62C8a4166c407eD853074", + "ADDRESS": "0x0292F451AF707e85a2ada6A72034cA11242E14C6", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-16 15:56:52", - "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc00000000000000000000000020b6b31d76e054c3e4de6154feca385ca58c7c15", - "SALT": "20241016", - "VERIFIED": "true" + "TIMESTAMP": "2024-10-22 09:39:58", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd864200000000000000000000000020b6b31d76e054c3e4de6154feca385ca58c7c15", + "SALT": "", + "VERIFIED": "false" } ] } diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 1b34192df..4fda70e4d 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -2,8 +2,8 @@ "LiFiDiamond": { "Facets": { "0xf03AFcA857918BE01EBD6C6800Fc2974b8a9eBA2": { - "Name": "", - "Version": "" + "Name": "EmergencyPauseFacet", + "Version": "1.0.0" }, "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6": { "Name": "GenericSwapFacetV3", @@ -53,9 +53,9 @@ "Name": "", "Version": "" }, - "0xd596C903d78870786c5DB0E448ce7F87A65A0daD": { - "Name": "MayanFacet", - "Version": "1.0.0" + "0x297aF81049744284874A7e5E90A907bAF6ACbbb5": { + "Name": "", + "Version": "" }, "0xAfcC5c55d5Ec3082675D51331E7Ed9AdE195db48": { "Name": "StargateFacet", @@ -81,7 +81,7 @@ "Name": "StargateFacetV2", "Version": "1.0.1" }, - "0x88c9Ed1C3d723F4F5fe14B18b4C91737208791bD": { + "0x0DAff7e73fDb2bbaDa232A16a5BEA72463893E35": { "Name": "GasZipFacet", "Version": "2.0.0" } @@ -91,7 +91,7 @@ "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "GasZipPeriphery": "0xa76B7FdA492fA0a936E62C8a4166c407eD853074", + "GasZipPeriphery": "0x0292F451AF707e85a2ada6A72034cA11242E14C6", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", @@ -101,4 +101,4 @@ "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } -} +} \ No newline at end of file diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index 093675bc7..a425723e2 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -28,10 +28,10 @@ "GasZip": "0xD9a07743138b6a76A8a56F305048D17f1175B11A", "MayanBridgeFacet": "0x5Ba4FeD1DAd2fD057A9f687B399B8e4cF2368214", "MayanFacet": "0xd596C903d78870786c5DB0E448ce7F87A65A0daD", - "GasZipFacet": "0x88c9Ed1C3d723F4F5fe14B18b4C91737208791bD", + "GasZipFacet": "0x0DAff7e73fDb2bbaDa232A16a5BEA72463893E35", "GenericSwapFacetV3": "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6", "EmergencyPauseFacet": "0xf03AFcA857918BE01EBD6C6800Fc2974b8a9eBA2", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", - "GasZipPeriphery": "0xa76B7FdA492fA0a936E62C8a4166c407eD853074" -} + "GasZipPeriphery": "0x0292F451AF707e85a2ada6A72034cA11242E14C6" +} \ No newline at end of file diff --git a/script/deploy/facets/DeployGasZipPeriphery.s.sol b/script/deploy/facets/DeployGasZipPeriphery.s.sol index 47d967330..0bef977e4 100644 --- a/script/deploy/facets/DeployGasZipPeriphery.s.sol +++ b/script/deploy/facets/DeployGasZipPeriphery.s.sol @@ -51,7 +51,7 @@ contract DeployScript is DeployScriptBase { string memory networksJson = vm.readFile(networks); address safeAddress = networksJson.readAddress( - string.concat(network, ".safeAdress") + string.concat(".", network, ".safeAddress") ); return abi.encode(gasZipRouter, liFiDEXAggregator, safeAddress); From ff8372736f995f7574eeb4421f015c77d070035c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 22 Oct 2024 10:32:30 +0700 Subject: [PATCH 074/100] test coverage back to 100% --- test/solidity/Periphery/GasZipPeriphery.t.sol | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index 24b93b98b..e43aaa436 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -11,7 +11,7 @@ import { TestBase, console, ILiFi, ERC20 } from "../utils/TestBase.sol"; import { IXDaiBridge } from "lifi/Interfaces/IXDaiBridge.sol"; import { IGasZip } from "lifi/Interfaces/IGasZip.sol"; import { NonETHReceiver } from "../utils/TestHelpers.sol"; -import { NativeAssetTransferFailed } from "lifi/Errors/GenericErrors.sol"; +import { NativeAssetTransferFailed, InvalidCallData } from "lifi/Errors/GenericErrors.sol"; // Stub GenericSwapFacet Contract contract TestGasZipPeriphery is GasZipPeriphery { @@ -54,6 +54,8 @@ contract GasZipPeripheryTest is TestBase { event Deposit(address from, uint256 chains, uint256 amount, bytes32 to); + error TooManyChainIds(); + function setUp() public { customBlockNumberForForking = 20931877; initTestBase(); @@ -398,6 +400,29 @@ contract GasZipPeripheryTest is TestBase { ); } + function testRevert_WillFailIfMoreThan32ChainIds() public { + vm.startPrank(USER_SENDER); + + uint8[] memory chainIds = new uint8[](33); + + vm.expectRevert(TooManyChainIds.selector); + + gasZipPeriphery.getDestinationChainsValue(chainIds); + } + + function testRevert_WillFailIfCalledWithInvalidReceiverAddress() public { + vm.startPrank(USER_SENDER); + + defaultGasZipData.receiverAddress = bytes32(0); + + vm.expectRevert(InvalidCallData.selector); + + // deposit via GasZip periphery contract + gasZipPeriphery.depositToGasZipNative{ + value: defaultNativeDepositAmount + }(defaultGasZipData, defaultNativeDepositAmount); + } + function _getGnosisBridgeFacet() internal returns (TestGnosisBridgeFacet _gnosisBridgeFacet) From de380ac79a0ac61617acab7d66b9841950eb21b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 24 Oct 2024 09:10:59 +0700 Subject: [PATCH 075/100] redeployed to BSC staging --- deployments/_deployments_log_file.json | 8 ++++---- deployments/bsc.diamond.staging.json | 2 +- deployments/bsc.staging.json | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index 9d9d538c2..065dafe1a 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -23429,11 +23429,11 @@ "staging": { "1.0.0": [ { - "ADDRESS": "0x0292F451AF707e85a2ada6A72034cA11242E14C6", + "ADDRESS": "0x46d8Aa20D5aD98927Cf885De9eBf9436E8E551c2", "OPTIMIZER_RUNS": "1000000", - "TIMESTAMP": "2024-10-22 09:39:58", - "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000d6f02718b9df9fad2665c7304bc5b26d5bbd864200000000000000000000000020b6b31d76e054c3e4de6154feca385ca58c7c15", - "SALT": "", + "TIMESTAMP": "2024-10-24 08:49:38", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc00000000000000000000000020b6b31d76e054c3e4de6154feca385ca58c7c15", + "SALT": "20241024", "VERIFIED": "false" } ] diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 4fda70e4d..ea806ff4a 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -91,7 +91,7 @@ "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasRebateDistributor": "", - "GasZipPeriphery": "0x0292F451AF707e85a2ada6A72034cA11242E14C6", + "GasZipPeriphery": "0x46d8Aa20D5aD98927Cf885De9eBf9436E8E551c2", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", diff --git a/deployments/bsc.staging.json b/deployments/bsc.staging.json index a425723e2..5b5014a94 100644 --- a/deployments/bsc.staging.json +++ b/deployments/bsc.staging.json @@ -32,6 +32,6 @@ "GenericSwapFacetV3": "0xE871874D8AC30E8aCD0eC67529b4a5dDD73Bf0d6", "EmergencyPauseFacet": "0xf03AFcA857918BE01EBD6C6800Fc2974b8a9eBA2", "StargateFacetV2": "0x089153117bffd37CBbE0c604dAE8e493D4743fA8", - "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", - "GasZipPeriphery": "0x0292F451AF707e85a2ada6A72034cA11242E14C6" + "LiFiDEXAggregator": "0x6140b987d6b51fd75b66c3b07733beb5167c42fc", + "GasZipPeriphery": "0x46d8Aa20D5aD98927Cf885De9eBf9436E8E551c2" } \ No newline at end of file From ae5514470516560f04395f0e4473c06990355d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 24 Oct 2024 10:49:17 +0700 Subject: [PATCH 076/100] add complex testcase for destinationChains value --- test/solidity/Facets/GasZipFacet.t.sol | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 812102a3c..dff8e150d 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -399,6 +399,41 @@ contract GasZipFacetTest is TestBaseFacet { chainIds[4] = 59; // Linea assertEq(gasZipFacet.getDestinationChainsValue(chainIds), 65336774203); + + chainIds = new uint8[](28); + 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 + chainIds[16] = 73; // Chain ID 73 + chainIds[17] = 28; // Chain ID 28 + chainIds[18] = 29; // Chain ID 29 + chainIds[19] = 55; // Chain ID 55 + chainIds[20] = 17; // Chain ID 17 + chainIds[21] = 52; // Chain ID 52 + chainIds[22] = 254; // Chain ID 254 + chainIds[23] = 41; // Chain ID 41 + chainIds[24] = 246; // Chain ID 246 + chainIds[25] = 249; // Chain ID 249 + chainIds[26] = 146; // Chain ID 146 + chainIds[27] = 51; // Chain ID 51 + + assertEq( + gasZipFacet.getDestinationChainsValue(chainIds), + 26878182541072503599461683703464409408182428609391216984945776497203 + ); } function testRevert_WillFailIfMsgValueDoesNotMatchBridgeDataAmount() From 6414ffaef06f3e0a502300a934a4d7fb11612df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 24 Oct 2024 13:16:08 +0700 Subject: [PATCH 077/100] adjust test case --- lib/openzeppelin-contracts | 2 +- test/solidity/Facets/GasZipFacet.t.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index e50c24f58..54b3f1434 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit e50c24f5839db17f46991478384bfda14acfb830 +Subproject commit 54b3f14346da01ba0d159114b399197fea8b7cda diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 812102a3c..cf994d2ea 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -140,14 +140,14 @@ contract GasZipFacetTest is TestBaseFacet { // update bridgeData to use native bridgeData.sendingAssetId = address(0); - bridgeData.minAmount = defaultERC20DepositAmount; + bridgeData.minAmount = defaultNativeDepositAmount; //prepare check for events vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET); emit Deposit( address(gasZipFacet), defaultDestinationChains, - defaultERC20DepositAmount, + defaultNativeDepositAmount, defaultReceiverBytes32 ); vm.expectEmit(true, true, true, true, _facetTestContractAddress); From c501877a69ea95e7b78db288192582ef3bd1978b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 24 Oct 2024 13:18:49 +0700 Subject: [PATCH 078/100] fix staging diamond log --- deployments/bsc.diamond.staging.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/deployments/bsc.diamond.staging.json b/deployments/bsc.diamond.staging.json index 543883e28..f89fccfee 100644 --- a/deployments/bsc.diamond.staging.json +++ b/deployments/bsc.diamond.staging.json @@ -87,17 +87,17 @@ } }, "Periphery": { - "ERC20Proxy": "0xf90a432dD1D0541470BC9C440d9dEc36597552380xf90a432dD1D0541470BC9C440d9dEc3659755238", - "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", - "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB60x7f98D45c7902f079fDb65811B633522e2d227BB6", + "ERC20Proxy": "0xf90a432dD1D0541470BC9C440d9dEc3659755238", + "Executor": "0x4f3B1b1075cC19daA15b7cc681b28e2fB82145eD", + "FeeCollector": "0x7f98D45c7902f079fDb65811B633522e2d227BB6", "GasZipPeriphery": "0x46d8Aa20D5aD98927Cf885De9eBf9436E8E551c2", "LiFiDEXAggregator": "0xD6f02718B9df9FAd2665c7304BC5b26D5bbD8642", - "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", + "LiFuelFeeCollector": "0xc4f7A34b8d283f66925eF0f5CCdFC2AF3030DeaE", "Receiver": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", "ReceiverAcrossV3": "0x76EE0F8fb09047284B6ea89881595Fc6F5B09E12", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "", + "TokenWrapper": "" } } } From 2d8927ababff6ace0c577d92407bcf289ebb89c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Tue, 5 Nov 2024 14:04:24 +0700 Subject: [PATCH 079/100] replaces low-level native transfer with function from solady lib --- src/Periphery/GasZipPeriphery.sol | 7 +------ test/solidity/Periphery/GasZipPeriphery.t.sol | 3 ++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index e4cfbfc12..2cfdf7bbe 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -99,12 +99,7 @@ contract GasZipPeriphery is // this is required due to LI.FI backend-internal requirements (money flow) uint256 remainingNativeBalance = address(this).balance; if (remainingNativeBalance > 0) { - (bool success, ) = msg.sender.call{ - value: remainingNativeBalance - }(""); - if (!success) { - revert NativeAssetTransferFailed(); - } + msg.sender.safeTransferETH(remainingNativeBalance); } } diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index e43aaa436..684cf051c 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -55,6 +55,7 @@ contract GasZipPeripheryTest is TestBase { event Deposit(address from, uint256 chains, uint256 amount, bytes32 to); error TooManyChainIds(); + error ETHTransferFailed(); function setUp() public { customBlockNumberForForking = 20931877; @@ -130,7 +131,7 @@ contract GasZipPeripheryTest is TestBase { vm.startPrank(address(nonETHReceiver)); // set up expected event - vm.expectRevert(NativeAssetTransferFailed.selector); + vm.expectRevert(ETHTransferFailed.selector); // deposit via GasZip periphery contract gasZipPeriphery.depositToGasZipNative{ From 82bcecc36ca83728d914be8af31b023fb79ee7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 7 Nov 2024 10:31:28 +0700 Subject: [PATCH 080/100] removes unused import (re-audit issue#11) --- src/Periphery/GasZipPeriphery.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 2cfdf7bbe..37f79ba5e 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -11,7 +11,7 @@ import { SwapperV2 } from "../Helpers/SwapperV2.sol"; import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { Validatable } from "../Helpers/Validatable.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; -import { NativeAssetTransferFailed, InvalidCallData } from "../Errors/GenericErrors.sol"; +import { InvalidCallData } from "../Errors/GenericErrors.sol"; /// @title GasZipPeriphery /// @author LI.FI (https://li.fi) From 1f4732a27bd74959e3943c20fcded9c3bd3a2cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 11 Nov 2024 18:22:21 +0700 Subject: [PATCH 081/100] update (re-)audit report --- audit/auditLog.json | 14 ++++++++++++-- audit/reports/2024.11.07_GasZip_ReAudit.pdf | Bin 0 -> 86333 bytes 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 audit/reports/2024.11.07_GasZip_ReAudit.pdf diff --git a/audit/auditLog.json b/audit/auditLog.json index 9d57cea89..693bf1c45 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -41,6 +41,13 @@ "auditorGitHandle": "sujithsomraaj", "auditReportPath": "./audit/reports/2024.11.05_EmergencyPauseFacet_ReAudit.pdf", "auditCommitHash": "da61880ba3847c07c35b64a78b957ff845ec18ac" + }, + "audit20241107": { + "auditCompletedOn": "07.11.2024", + "auditedBy": "Sujith Somraaj (individual security researcher)", + "auditorGitHandle": "sujithsomraaj", + "auditReportPath": "./audit/reports/2024.11.07_GasZip_ReAudit.pdf", + "auditCommitHash": "2d8927ababff6ace0c577d92407bcf289ebb89c0" } }, "auditedContracts": { @@ -55,10 +62,13 @@ "1.0.0": ["audit20241007"] }, "GasZipFacet": { - "2.0.0": ["audit20241017"] + "2.0.0": ["audit20241017", "audit20241107"] }, "GasZipPeriphery": { - "1.0.0": ["audit20241017"] + "1.0.0": ["audit20241017", "audit20241107"] + }, + "IGasZip": { + "1.0.0": ["audit20241107"] }, "ReceiverAcrossV3": { "1.0.0": ["audit20241007"] diff --git a/audit/reports/2024.11.07_GasZip_ReAudit.pdf b/audit/reports/2024.11.07_GasZip_ReAudit.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5a08b8cfa1ac7d53b7b16839712fa6ea932f2c9e GIT binary patch literal 86333 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

9+ceX;Vg_w-(7V#zZ|-21aA2 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 Oz_B7wQAw#tBm57$8Q8r5 literal 86333 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

9+ceX;Vg_w-(7V#zZ|-21aA2 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

OP|Pl5@07 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 Date: Wed, 13 Nov 2024 17:01:36 +0300 Subject: [PATCH 087/100] deploy to zksync --- deployments/_deployments_log_file.json | 30 +++++++++++++++++++++++++- deployments/zksync.json | 6 ++++-- src/Interfaces/IGasZip.sol | 2 +- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index a7e7e51a5..dff565fe7 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -24761,6 +24761,20 @@ } ] } + }, + "zksync": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0x313c27Aad40c7e0A0b923b539F05617D8114566D", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-13 16:42:46", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000252fb662e4d7435d2a5ded8ec94d8932cf76c178", + "SALT": "", + "VERIFIED": "true" + } + ] + } } }, "GasZipPeriphery": { @@ -25027,6 +25041,20 @@ } ] } + }, + "zksync": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x4000f76450d9d40558fd60F66863F583B7A5BCCb", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-13 16:47:48", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000252fb662e4d7435d2a5ded8ec94d8932cf76c1780000000000000000000000001f683faf1e2a770aa75f7b2e92117a5c11183e9c00000000000000000000000002f1272aeacaf7bd8b30278bc2aa381cc623a744", + "SALT": "", + "VERIFIED": "true" + } + ] + } } } -} \ No newline at end of file +} diff --git a/deployments/zksync.json b/deployments/zksync.json index 1ee211b4c..5e42ddfec 100644 --- a/deployments/zksync.json +++ b/deployments/zksync.json @@ -21,5 +21,7 @@ "LiFiDEXAggregator": "0x1F683faf1E2a770aa75f7B2e92117A5c11183E9C", "AcrossFacetV3": "0x2e47355B70D6935C6A69d5F67e0aFe437791138e", "ReceiverAcrossV3": "0xFa94c1A99799B3cA89DE6cbB3ccCDEcf1da62aFE", - "AcrossFacetPackedV3": "0x9243578F60a2A3821642481b5851578cE92d9a78" -} + "AcrossFacetPackedV3": "0x9243578F60a2A3821642481b5851578cE92d9a78", + "GasZipFacet": "0x313c27Aad40c7e0A0b923b539F05617D8114566D", + "GasZipPeriphery": "0x4000f76450d9d40558fd60F66863F583B7A5BCCb" +} \ No newline at end of file diff --git a/src/Interfaces/IGasZip.sol b/src/Interfaces/IGasZip.sol index bc014fd8e..5a92a65ea 100644 --- a/src/Interfaces/IGasZip.sol +++ b/src/Interfaces/IGasZip.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT /// @custom:version 1.0.0 -pragma solidity 0.8.17; +pragma solidity ^0.8.17; /// @title Interface for GasZip /// @author LI.FI (https://li.fi) From f5da97dc9bfeaf701a62de776212725159feef6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 20 Nov 2024 09:20:52 +0700 Subject: [PATCH 088/100] adds GasZipPeriphery addresses to dexs.json --- config/dexs.json | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/config/dexs.json b/config/dexs.json index 16e713570..4b5b2e5d7 100644 --- a/config/dexs.json +++ b/config/dexs.json @@ -41,6 +41,7 @@ "0x19dBa5df5383168f760617aaDD23322BC5F9Ff7b", "0x827179dD56d07A7eeA32e3873493835da2866976", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "arbitrum": [ @@ -85,6 +86,7 @@ "0x9c6522117e2ed1fE5bdb72bb0eD5E3f2bdE7DBe0", "0xfc506AaA1340b4dedFfd88bE278bEe058952D674", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "aurora": [ @@ -147,6 +149,7 @@ "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "0x400d75dAb26bBc18D163AEA3e83D9Ea68F6c1804", "0x717b7948AA264DeCf4D780aa6914482e5F46Da3e", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55" ], "base": [ @@ -172,6 +175,7 @@ "0x0000000000001ff3684f28c67538d4d072c22734", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55", "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "blast": [ @@ -187,6 +191,7 @@ "0xCdBCd51a5E8728E0AF4895ce5771b7d17fF71959", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55", "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "boba": [ @@ -255,6 +260,7 @@ "0x7cf167390E2526Bc03F3CF6852A7AF1CEC3e243d", "0x400d75dAb26bBc18D163AEA3e83D9Ea68F6c1804", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "celo": [ @@ -347,6 +353,7 @@ "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "0x3D2f8ae0344d38525d2AE96Ab750B83480c0844F", "0x2214A42d8e2A1d20635c2cb0664422c528B6A432", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55" ], "fuse": [ @@ -397,6 +404,7 @@ "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "0x1e9B24073183d5c6B7aE5FB4b8f0b1dd83FDC77a", "0xBBDe1d67297329148Fe1ED5e6B00114842728e65", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55" ], "gravity": [ @@ -404,6 +412,7 @@ "0x7fA60f4A59Dd8285C5Fcd8fd2A92A2Ca45ef8a0C", "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "0x134f525AC05E4724e55C363A9C4FA35ceB13F88d", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0x6352a56caadc4f1e25cd6c75970fa768a3304e64" ], "immutablezkevm": [ @@ -472,6 +481,7 @@ "0x46b3fdf7b5cde91ac049936bf0bdb12c5d22202e", "0x000000000000175a8b9bc6d539b3708eed92ea6c", "0xcaA342e4f781d63EF41E220D7622B97E66BAEcF3", + "0x0ec6D2eEb94541C51620830D151995fCFf83Aa74", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55" ], "mantle": [ @@ -483,6 +493,7 @@ "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD", "0x0263180888007D45340F86eC0b610d250BbDcB23", "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0x6131b5fae19ea4f9d964eac0408e4408b66337b5" ], "metis": [ @@ -490,6 +501,7 @@ "0x27f0e36dE6B1BA8232f6c2e87E00A50731048C6B", "0xB45e53277a7e0F1D35f2a77160e91e25507f1763", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55", + "0x799525cE72B5cc9eb310dc8c7b9e7A3128a6dA79", "0x9E4c63c9a0EDE2Ca2e772ee48C819Ca5CB4529AC" ], "mode": [ @@ -501,6 +513,7 @@ "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64", "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "moonbeam": [ @@ -616,11 +629,10 @@ "0x96E04591579f298681361C6122Dc4Ef405c19385", "0x4C5D5234f232BD2D76B96aA33F5AE4FCF0E4BFAb", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], - "opbnb": [ - "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc" - ], + "opbnb": ["0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc"], "polygon": [ "0x5215E9fd223BC909083fbdB2860213873046e45d", "0xdFC2983401614118E1F2D5A5FD93C17Fecf8BdC6", @@ -668,6 +680,7 @@ "0x0dc8E47a1196bcB590485eE8bF832c5c68A52f4B", "0x0a6e511Fe663827b9cA7e2D2542b20B37fC217A6", "0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "polygonzkevm": [ @@ -719,6 +732,7 @@ "0xbfe03c9e20a9fc0b37de01a172f207004935e0b1", "0x0000000000005e88410ccdfade4a5efae4b49562", "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "taiko": [ @@ -726,6 +740,7 @@ "0xff2F39692A90262b8Ed4DFD92799bB450425773F", "0xD989E929517B0e5eD0c8EfE7607Fa167B697cBa8", "0xcaA342e4f781d63EF41E220D7622B97E66BAEcF3", + "0x0ec6D2eEb94541C51620830D151995fCFf83Aa74", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "velas": [ @@ -743,6 +758,7 @@ "0xC69994fd72824ca98F8a0B1E2ABc954E65a91cf4", "0x12904D12A84702f9F079E1e393fdAbD313496e97", "0x833Be894C596b15FAe740C2D522d660084c48B05", + "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "0x2321F1a63A683a1F3634Dbe1CbA0d657D5F56d54" ], "zksync": [ @@ -761,6 +777,7 @@ "0xe832e655E4C3c36b2be5256915ECF8536a642f59", "0x6fd4383cb451173d5f9304f041c7bcbf27d561ff", "0x1F683faf1E2a770aa75f7B2e92117A5c11183E9C", + "0x4000f76450d9d40558fd60F66863F583B7A5BCCb", "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F" ], "---------------FROM HERE ON JUST OLD NETWORKS - PLEASE ADD NEW NETWORKS ABOVE IN ALPHABETICAL ORDER ^^^^ ----------": [], @@ -776,9 +793,7 @@ "0xf91bb752490473b8342a3e964e855b9f9a2a668e" ], "avalancheFujiTestnet": [], - "bscTestnet": [ - "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506" - ], + "bscTestnet": ["0x1b02da8cb0d097eb8d57a175b88c7d8b47997506"], "localanvil": [], "mumbai": [ "0x5215E9fd223BC909083fbdB2860213873046e45d", @@ -787,4 +802,4 @@ "0xf471d32cb40837bf24529fcf17418fc1a4807626", "0x427bFc2E0aa683ec43fbF7861d5F4A74147938d8" ] -} \ No newline at end of file +} From 5a191286db90e59c7f868e3f7c7a872af5d028a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 20 Nov 2024 09:38:48 +0700 Subject: [PATCH 089/100] make error message more visible in console output --- script/tasks/diamondSyncDEXs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/tasks/diamondSyncDEXs.sh b/script/tasks/diamondSyncDEXs.sh index 55cca535e..3fd90623c 100755 --- a/script/tasks/diamondSyncDEXs.sh +++ b/script/tasks/diamondSyncDEXs.sh @@ -104,7 +104,7 @@ function diamondSyncDEXs { CHECKSUMMED=$(cast --to-checksum-address "$DEX_ADDRESS") CODE=$(cast code $CHECKSUMMED --rpc-url "$RPC_URL") if [[ "$CODE" == "0x" ]]; then - echo "[warning] DEX $CHECKSUMMED is not deployed on network $NETWORK - skipping" + error "DEX $CHECKSUMMED is not deployed on network $NETWORK - skipping" echo "$NETWORK - $CHECKSUMMED" >>.invalid-dexs continue fi From afa5c59f45c6b611d770f4ed6ac75d577e0e0b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 20 Nov 2024 10:20:59 +0700 Subject: [PATCH 090/100] update some diamond logs --- deployments/arbitrum.diamond.json | 2 +- deployments/avalanche.diamond.json | 2 +- deployments/base.diamond.json | 2 +- deployments/blast.diamond.json | 2 +- deployments/bsc.diamond.json | 2 +- deployments/fantom.diamond.json | 2 +- deployments/gnosis.diamond.json | 2 +- deployments/mainnet.diamond.json | 2 +- deployments/metis.diamond.json | 2 +- deployments/mode.diamond.json | 2 +- deployments/optimism.diamond.json | 2 +- deployments/polygon.diamond.json | 2 +- deployments/scroll.diamond.json | 2 +- deployments/zksync.diamond.json | 3 ++- 14 files changed, 15 insertions(+), 14 deletions(-) diff --git a/deployments/arbitrum.diamond.json b/deployments/arbitrum.diamond.json index cf8d655c7..00de90192 100644 --- a/deployments/arbitrum.diamond.json +++ b/deployments/arbitrum.diamond.json @@ -157,7 +157,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/avalanche.diamond.json b/deployments/avalanche.diamond.json index 85de422d2..fed2b9b42 100644 --- a/deployments/avalanche.diamond.json +++ b/deployments/avalanche.diamond.json @@ -129,7 +129,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/base.diamond.json b/deployments/base.diamond.json index aef717355..86eb67006 100644 --- a/deployments/base.diamond.json +++ b/deployments/base.diamond.json @@ -145,7 +145,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/blast.diamond.json b/deployments/blast.diamond.json index 0575eead4..89c5edca4 100644 --- a/deployments/blast.diamond.json +++ b/deployments/blast.diamond.json @@ -93,7 +93,7 @@ "ReceiverStargateV2": "", "RelayerCelerIM": "", "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/bsc.diamond.json b/deployments/bsc.diamond.json index ccfbc9f64..7e7759a32 100644 --- a/deployments/bsc.diamond.json +++ b/deployments/bsc.diamond.json @@ -133,7 +133,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/fantom.diamond.json b/deployments/fantom.diamond.json index 4113bee8f..63086353f 100644 --- a/deployments/fantom.diamond.json +++ b/deployments/fantom.diamond.json @@ -101,7 +101,7 @@ "ReceiverStargateV2": "", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/gnosis.diamond.json b/deployments/gnosis.diamond.json index 00aac64ab..b76eda2d6 100644 --- a/deployments/gnosis.diamond.json +++ b/deployments/gnosis.diamond.json @@ -105,7 +105,7 @@ "ReceiverStargateV2": "", "RelayerCelerIM": "", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/mainnet.diamond.json b/deployments/mainnet.diamond.json index 04fe84c3c..79db14d0b 100644 --- a/deployments/mainnet.diamond.json +++ b/deployments/mainnet.diamond.json @@ -181,7 +181,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/metis.diamond.json b/deployments/metis.diamond.json index da156315b..baa4bc2a7 100644 --- a/deployments/metis.diamond.json +++ b/deployments/metis.diamond.json @@ -85,7 +85,7 @@ "ReceiverStargateV2": "0xe7392Fc0f61503dB53C70789c6F2c34C0675C929", "RelayerCelerIM": "", "TokenWrapper": "0x01bDf46A673FC3c081ddBD21cb51fBA4972d00aC", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x799525cE72B5cc9eb310dc8c7b9e7A3128a6dA79" } } } \ No newline at end of file diff --git a/deployments/mode.diamond.json b/deployments/mode.diamond.json index ca58f6bbf..1adadb21d 100644 --- a/deployments/mode.diamond.json +++ b/deployments/mode.diamond.json @@ -89,7 +89,7 @@ "ReceiverStargateV2": "", "RelayerCelerIM": "", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/optimism.diamond.json b/deployments/optimism.diamond.json index 74f11ede4..b87ddcbc4 100644 --- a/deployments/optimism.diamond.json +++ b/deployments/optimism.diamond.json @@ -157,7 +157,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/polygon.diamond.json b/deployments/polygon.diamond.json index 6c38044e1..002548179 100644 --- a/deployments/polygon.diamond.json +++ b/deployments/polygon.diamond.json @@ -157,7 +157,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/scroll.diamond.json b/deployments/scroll.diamond.json index b64313d11..c062e32c2 100644 --- a/deployments/scroll.diamond.json +++ b/deployments/scroll.diamond.json @@ -105,7 +105,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } } } \ No newline at end of file diff --git a/deployments/zksync.diamond.json b/deployments/zksync.diamond.json index 94de61cf1..951326876 100644 --- a/deployments/zksync.diamond.json +++ b/deployments/zksync.diamond.json @@ -84,7 +84,8 @@ "ReceiverAcrossV3": "0xFa94c1A99799B3cA89DE6cbB3ccCDEcf1da62aFE", "ReceiverStargateV2": "", "RelayerCelerIM": "0xFf9565e1C4f01C368444D2bE4B9ef36ce7E95541", - "TokenWrapper": "0xf15485ada1a1826fA46225032b13F6A972eC73C1" + "TokenWrapper": "0xf15485ada1a1826fA46225032b13F6A972eC73C1", + "GasZipPeriphery": "" } } } \ No newline at end of file From 9ee21934346b1262dd2e830a6f2d04a6eb4ac93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 20 Nov 2024 10:45:39 +0700 Subject: [PATCH 091/100] remove old openzeppelin lib --- lib/openzeppelin-contracts | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lib/openzeppelin-contracts diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts deleted file mode 160000 index e50c24f58..000000000 --- a/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e50c24f5839db17f46991478384bfda14acfb830 From e93fb21fe334b226e9029e3d4b62c02f0d2a2fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 20 Nov 2024 10:45:45 +0700 Subject: [PATCH 092/100] forge install: openzeppelin-contracts v4.9.2 --- .gitmodules | 2 +- lib/openzeppelin-contracts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 lib/openzeppelin-contracts diff --git a/.gitmodules b/.gitmodules index 9b49e2820..d8d4e175b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,7 +18,7 @@ url = https://github.com/sambacha/safe-tx-cli [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts - url = https://github.com/openzeppelin/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts [submodule "lib/solady"] path = lib/solady url = https://github.com/Vectorized/solady diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 000000000..e50c24f58 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit e50c24f5839db17f46991478384bfda14acfb830 From 9c204faca398968c1139308e847574a2887ae80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 20 Nov 2024 10:47:41 +0700 Subject: [PATCH 093/100] reinstall openzeppelin contracts 4.9.2 --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index d8d4e175b..9b49e2820 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,7 +18,7 @@ url = https://github.com/sambacha/safe-tx-cli [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts - url = https://github.com/OpenZeppelin/openzeppelin-contracts + url = https://github.com/openzeppelin/openzeppelin-contracts [submodule "lib/solady"] path = lib/solady url = https://github.com/Vectorized/solady From 202b5bc9b32d3bbfa86e3179b46c7ee3c4f6ccf7 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 21 Nov 2024 13:34:18 +0300 Subject: [PATCH 094/100] add gasZipPeriphery sigs --- config/sigs.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/sigs.json b/config/sigs.json index 620ec1025..d0d6b2368 100644 --- a/config/sigs.json +++ b/config/sigs.json @@ -149,6 +149,8 @@ "0x47f8bd41", "0x6678ec1f", "0x30eef8bd", - "0x4dcebcba" + "0x4dcebcba", + "0x8b71ae6c", + "0xc4af5a74" ] -} +} \ No newline at end of file From 5ffedebd1c0b783d68b0634708b7eb80cfa7f5ab Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 3 Dec 2024 13:59:57 +0300 Subject: [PATCH 095/100] fix json --- config/sigs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/sigs.json b/config/sigs.json index f577c555c..042d9107b 100644 --- a/config/sigs.json +++ b/config/sigs.json @@ -148,7 +148,7 @@ "0x30eef8bd", "0x4dcebcba", "0x8b71ae6c", - "0xc4af5a74" + "0xc4af5a74", "0x03b87e5f", "0x0d5f0e3b", "0x08298b5a" From 9918960dacc8a81579a3547331e604b8db4de866 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 3 Dec 2024 14:02:16 +0300 Subject: [PATCH 096/100] fix json --- script/deploy/resources/deployRequirements.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/deploy/resources/deployRequirements.json b/script/deploy/resources/deployRequirements.json index 2bafac7a0..2fcb8bd11 100644 --- a/script/deploy/resources/deployRequirements.json +++ b/script/deploy/resources/deployRequirements.json @@ -532,8 +532,8 @@ "allowToDeployWithZeroAddress": "false" } } - } - "GasZipFacet": { + }, + "GasZipFacet": { "configData": { "_gasZipRouter": { "configFileName": "gaszip.json", @@ -552,7 +552,7 @@ }, "contractAddresses": { "LiFiDEXAggregator": { - "allowToDeployWithZeroAddress": "false" + "allowToDeployWithZeroAddress": "false" } } } From 6d7fe0eda297cd16ac8bcd3a7ad17f2f9dc78a6e Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 3 Dec 2024 14:54:03 +0300 Subject: [PATCH 097/100] fix logs --- deployments/arbitrum.diamond.json | 10 +++++----- deployments/avalanche.diamond.json | 10 +++++----- deployments/base.diamond.json | 10 +++++----- deployments/optimism.json | 3 +-- deployments/polygon.diamond.json | 10 +++++----- deployments/zksync.diamond.json | 10 +++++----- 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/deployments/arbitrum.diamond.json b/deployments/arbitrum.diamond.json index a23ec37b3..19e5cceaf 100644 --- a/deployments/arbitrum.diamond.json +++ b/deployments/arbitrum.diamond.json @@ -147,22 +147,22 @@ }, "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { "Name": "GasZipFacet", - "Version": "1.0.0" + "Version": "2.0.0" } }, "Periphery": { "ERC20Proxy": "0x5741A7FfE7c39Ca175546a54985fA79211290b51", "Executor": "0x2dfaDAB8266483beD9Fd9A292Ce56596a2D1378D", "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", - "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", + "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } -} +} \ No newline at end of file diff --git a/deployments/avalanche.diamond.json b/deployments/avalanche.diamond.json index 1920ae7ec..649710359 100644 --- a/deployments/avalanche.diamond.json +++ b/deployments/avalanche.diamond.json @@ -118,23 +118,23 @@ "Version": "1.0.0" }, "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { - "Name": "", - "Version": "" + "Name": "GasZipFacet", + "Version": "2.0.0" } }, "Periphery": { "ERC20Proxy": "0x5741A7FfE7c39Ca175546a54985fA79211290b51", "Executor": "0x2dfaDAB8266483beD9Fd9A292Ce56596a2D1378D", "FeeCollector": "0xB0210dE78E28e2633Ca200609D9f528c13c26cD9", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", - "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "", + "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/base.diamond.json b/deployments/base.diamond.json index bd1863a38..88eb383ab 100644 --- a/deployments/base.diamond.json +++ b/deployments/base.diamond.json @@ -134,8 +134,8 @@ "Version": "1.0.0" }, "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { - "Name": "", - "Version": "" + "Name": "GasZipFacet", + "Version": "2.0.0" }, "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7": { "Name": "AcrossFacetV3", @@ -146,15 +146,15 @@ "ERC20Proxy": "0x74a55CaDb12501A3707E9F3C5dfd8b563C6A5940", "Executor": "0x4DaC9d1769b9b304cb04741DCDEb2FC14aBdF110", "FeeCollector": "0x0A6d96E7f4D7b96CFE42185DF61E64d255c12DFf", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", - "Receiver": "0xeC03B65CbDc5f8858b02F44EBa54C90664249fb1", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", + "Receiver": "0xeC03B65CbDc5f8858b02F44EBa54C90664249fb1", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/optimism.json b/deployments/optimism.json index f6746e702..1e158644e 100644 --- a/deployments/optimism.json +++ b/deployments/optimism.json @@ -53,6 +53,5 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", - "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } \ No newline at end of file diff --git a/deployments/polygon.diamond.json b/deployments/polygon.diamond.json index db3565fcb..44626fb1c 100644 --- a/deployments/polygon.diamond.json +++ b/deployments/polygon.diamond.json @@ -146,8 +146,8 @@ "Version": "1.0.0" }, "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { - "Name": "", - "Version": "" + "Name": "GasZipFacet", + "Version": "2.0.0" }, "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7": { "Name": "AcrossFacetV3", @@ -158,15 +158,15 @@ "ERC20Proxy": "0x5741A7FfE7c39Ca175546a54985fA79211290b51", "Executor": "0x2dfaDAB8266483beD9Fd9A292Ce56596a2D1378D", "FeeCollector": "0xbD6C7B0d2f68c2b7805d88388319cfB6EcB50eA9", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", "Permit2Proxy": "0x6307119078556Fc8aD77781DFC67df20d75FB4f9", - "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", + "Receiver": "0x050e198E36A73a1e32F15C3afC58C4506d82f657", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", - "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" } } } \ No newline at end of file diff --git a/deployments/zksync.diamond.json b/deployments/zksync.diamond.json index 77e2713bb..ae9fc63b8 100644 --- a/deployments/zksync.diamond.json +++ b/deployments/zksync.diamond.json @@ -74,23 +74,23 @@ "Version": "1.0.0" }, "0x313c27Aad40c7e0A0b923b539F05617D8114566D": { - "Name": "", - "Version": "" + "Name": "GasZipFacet", + "Version": "2.0.0" } }, "Periphery": { "ERC20Proxy": "0x8E1cf39Df64D9DCAdA7831d90CC852e02663410a", "Executor": "0xa9bfa49F26733271f4FD34A4b57bB7C563Ae056A", "FeeCollector": "0x8dBf6f59187b2EB36B980F3D8F4cFC6DC4E4642e", + "GasZipPeriphery": "0x4000f76450d9d40558fd60F66863F583B7A5BCCb", "LiFiDEXAggregator": "0x1F683faf1E2a770aa75f7B2e92117A5c11183E9C", "LiFuelFeeCollector": "0xB87C536E048Cfc082187E559fCFeFc3f1c89aEc7", "Permit2Proxy": "0x6275f6631c955DC5dA9fBe8Dc7f24a3A5919443A", - "Receiver": "0xdeDB2DAe4a9BC63910a722a3b7DC930C7E6f6421", "ReceiverAcrossV3": "0xFa94c1A99799B3cA89DE6cbB3ccCDEcf1da62aFE", + "Receiver": "0xdeDB2DAe4a9BC63910a722a3b7DC930C7E6f6421", "ReceiverStargateV2": "", "RelayerCelerIM": "0xFf9565e1C4f01C368444D2bE4B9ef36ce7E95541", - "TokenWrapper": "0xf15485ada1a1826fA46225032b13F6A972eC73C1", - "GasZipPeriphery": "" + "TokenWrapper": "0xf15485ada1a1826fA46225032b13F6A972eC73C1" } } } \ No newline at end of file From ffe260641ea0e994f6f963965015f87dcaba6a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 4 Dec 2024 09:42:00 +0700 Subject: [PATCH 098/100] diamond logs updated --- deployments/gravity.diamond.json | 16 ++++++++++++++-- deployments/linea.diamond.json | 18 +++++++++++++----- deployments/mantle.diamond.json | 12 ++++++++++-- deployments/xlayer.diamond.json | 8 ++++++-- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/deployments/gravity.diamond.json b/deployments/gravity.diamond.json index 99d4c29ff..1c4d27fb3 100644 --- a/deployments/gravity.diamond.json +++ b/deployments/gravity.diamond.json @@ -56,20 +56,32 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "GasZipFacet", + "Version": "2.0.0" + }, + "0x49e93F6A99c590a8E70138D2710B9eDd88C077FF": { + "Name": "", + "Version": "" + }, + "0x325DA62543447A48c7b044C5642B87CeA88B0fd3": { + "Name": "", + "Version": "" } }, "Periphery": { "ERC20Proxy": "0x4307ca7Eca98daF86D4f65b4367B273C628A39B7", "Executor": "0xCdE9376F284d5CA9aaec69dD4D0761278b4ae034", "FeeCollector": "0x79540403cdE176Ca5f1fb95bE84A7ec91fFDEF76", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0x134f525AC05E4724e55C363A9C4FA35ceB13F88d", "Receiver": "0x2DeB3bFa2b19024A0c1Ba299b6b79276f1F77b14", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x6A3d6652fb7be72200a47313C092342218aAeb72", "RelayerCelerIM": "", - "TokenWrapper": "0x7fA60f4A59Dd8285C5Fcd8fd2A92A2Ca45ef8a0C", - "GasZipPeriphery": "" + "TokenWrapper": "0x7fA60f4A59Dd8285C5Fcd8fd2A92A2Ca45ef8a0C" } } } \ No newline at end of file diff --git a/deployments/linea.diamond.json b/deployments/linea.diamond.json index 9f6ba40d3..b1af20641 100644 --- a/deployments/linea.diamond.json +++ b/deployments/linea.diamond.json @@ -106,30 +106,38 @@ "Version": "1.0.1" }, "0x80b96CA9B47aCD6c2a888128fEb9b0F4Ea518FEc": { - "Name": "AcrossFacetV3", - "Version": "1.0.0" + "Name": "", + "Version": "" }, - "0xAfEB7e1DA0Ff4DcD0dbC4De3F51a933E2054B0ed": { + "0x922D1c381Cb5b0AFAAe9E7C86ed34FDE337766b0": { "Name": "AcrossFacetPackedV3", "Version": "1.0.0" }, "0xe07c030dDC7Fb9ca23b633b1028106DAA5fdbF96": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0x75943d7305310635945736D00235d825181018f3": { + "Name": "GasZipFacet", + "Version": "2.0.0" + }, + "0x2531368BAca8c5E85031FC0a9a2f148b65da389A": { + "Name": "AcrossFacetV3", + "Version": "1.0.0" } }, "Periphery": { "ERC20Proxy": "0x57ec2C57fA654aABAeCc63c8dad47cb6efaCCC24", "Executor": "0x2a202Ed587F0BC7dfa80ea1DD943d8470492Dd0F", "FeeCollector": "0xA4A24BdD4608D7dFC496950850f9763B674F0DB2", + "GasZipPeriphery": "0x0ec6D2eEb94541C51620830D151995fCFf83Aa74", "LiFiDEXAggregator": "", "LiFuelFeeCollector": "0x68B21d21509446Bf5449B6F5F8aBD4b3cfcbc3f8", "Receiver": "0xdcBEcDE898c067cA58ABD01a7de51660bBD5A897", "ReceiverAcrossV3": "0x4BB377A1A624bDeF72d352891dc5E64087345fe6", "ReceiverStargateV2": "0x6CA57d9846f9a1fd48368762b743a047eC4f81A6", "RelayerCelerIM": "", - "TokenWrapper": "0xf6C9605c6E231C1547b7a6545d93e7233f97322a", - "GasZipPeriphery": "" + "TokenWrapper": "0xf6C9605c6E231C1547b7a6545d93e7233f97322a" } } } \ No newline at end of file diff --git a/deployments/mantle.diamond.json b/deployments/mantle.diamond.json index c33081241..bb7750935 100644 --- a/deployments/mantle.diamond.json +++ b/deployments/mantle.diamond.json @@ -64,20 +64,28 @@ "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61": { "Name": "StargateFacetV2", "Version": "1.0.1" + }, + "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { + "Name": "EmergencyPauseFacet", + "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "GasZipFacet", + "Version": "2.0.0" } }, "Periphery": { "ERC20Proxy": "0xA950Ac46b0b844c0564d18A54A9685e614B9086C", "Executor": "0x7078d1DE45C7D3e87f71D5DA663db2a8Ee1dfEbe", "FeeCollector": "0xF048e5816B0C7951AC179f656C5B86e5a79Bd7b5", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "LiFuelFeeCollector": "0xc02FFcdD914DbA646704439c6090BAbaD521d04C", "Receiver": "0x2fA14922ABc117c4737260032C8fD6D9Ab35395A", "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", - "TokenWrapper": "0x0263180888007D45340F86eC0b610d250BbDcB23", - "GasZipPeriphery": "" + "TokenWrapper": "0x0263180888007D45340F86eC0b610d250BbDcB23" } } } \ No newline at end of file diff --git a/deployments/xlayer.diamond.json b/deployments/xlayer.diamond.json index 4a0185c40..9e17a038b 100644 --- a/deployments/xlayer.diamond.json +++ b/deployments/xlayer.diamond.json @@ -60,20 +60,24 @@ "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61": { "Name": "EmergencyPauseFacet", "Version": "1.0.0" + }, + "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849": { + "Name": "GasZipFacet", + "Version": "2.0.0" } }, "Periphery": { "ERC20Proxy": "0x32f9EBAEA88bfE6b965108A2315AC2bE6253cC82", "Executor": "0x4c2fd9A794ac2337aD7AD547158B68397B6458A7", "FeeCollector": "0xC69994fd72824ca98F8a0B1E2ABc954E65a91cf4", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", "LiFiDEXAggregator": "0x2321F1a63A683a1F3634Dbe1CbA0d657D5F56d54", "LiFuelFeeCollector": "0x12904D12A84702f9F079E1e393fdAbD313496e97", "Receiver": "0xE33aFf67082eD48A162996670A3FC80AD01C4B5D", "ReceiverAcrossV3": "", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0x833Be894C596b15FAe740C2D522d660084c48B05", - "GasZipPeriphery": "" + "TokenWrapper": "0x833Be894C596b15FAe740C2D522d660084c48B05" } } } \ No newline at end of file From 38c0c13e4958d2a0890644ad2a08d1e1be7f2b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 4 Dec 2024 09:47:53 +0700 Subject: [PATCH 099/100] undo (wording) changes in git action --- .github/workflows/enforceTestCoverage.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/enforceTestCoverage.yml b/.github/workflows/enforceTestCoverage.yml index 0a9377e07..a61a80f18 100644 --- a/.github/workflows/enforceTestCoverage.yml +++ b/.github/workflows/enforceTestCoverage.yml @@ -44,11 +44,9 @@ jobs: - name: Install Dependencies run: forge install - - name: Generate and Filter Coverage Report + - name: Generate Coverage Report run: | - echo "Generating coverage report now" - - forge coverage --report lcov --force --evm-version shanghai --ir-minimum + forge coverage --report lcov --force --evm-version 'shanghai' --ir-minimum echo "Filtering coverage report to only contain coverage info for 'src/'' folder now" From 3e2bf465f6e478ac3e9ae87dae63bdb0830d6fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 4 Dec 2024 10:14:43 +0700 Subject: [PATCH 100/100] added test cases to increase to 100% coverage --- test/solidity/Facets/GasZipFacet.t.sol | 6 ++++++ test/solidity/Periphery/GasZipPeriphery.t.sol | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/test/solidity/Facets/GasZipFacet.t.sol b/test/solidity/Facets/GasZipFacet.t.sol index 94e5bbf96..5973e6787 100644 --- a/test/solidity/Facets/GasZipFacet.t.sol +++ b/test/solidity/Facets/GasZipFacet.t.sol @@ -130,6 +130,12 @@ contract GasZipFacetTest is TestBaseFacet { } } + function test_WillStoreConstructorParametersCorrectly() public { + gasZipFacet = new TestGasZipFacet(GAS_ZIP_ROUTER_MAINNET); + + assertEq(address(gasZipFacet.gasZipRouter()), GAS_ZIP_ROUTER_MAINNET); + } + function testBase_CanBridgeTokens_fuzzed(uint256 amount) public override { // deactivated for this facet since we would have to update the calldata that swaps from ERC20 to native for every amount } diff --git a/test/solidity/Periphery/GasZipPeriphery.t.sol b/test/solidity/Periphery/GasZipPeriphery.t.sol index 684cf051c..2de0cfb02 100644 --- a/test/solidity/Periphery/GasZipPeriphery.t.sol +++ b/test/solidity/Periphery/GasZipPeriphery.t.sol @@ -86,6 +86,23 @@ contract GasZipPeripheryTest is TestBase { vm.label(LIFI_DEX_AGGREGATOR_MAINNET, "LiFiDEXAggregator"); } + function test_WillStoreConstructorParametersCorrectly() public { + gasZipPeriphery = new TestGasZipPeriphery( + GAS_ZIP_ROUTER_MAINNET, + LIFI_DEX_AGGREGATOR_MAINNET, + USER_DIAMOND_OWNER + ); + + assertEq( + address(gasZipPeriphery.gasZipRouter()), + GAS_ZIP_ROUTER_MAINNET + ); + assertEq( + gasZipPeriphery.liFiDEXAggregator(), + LIFI_DEX_AGGREGATOR_MAINNET + ); + } + function test_CanDepositNative() public { // set up expected event vm.expectEmit(true, true, true, true, GAS_ZIP_ROUTER_MAINNET);

OP|Pl5@07 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 Date: Mon, 11 Nov 2024 18:27:08 +0700 Subject: [PATCH 082/100] remove old gaszip audit entry --- audit/auditLog.json | 11 ++--------- audit/reports/2024.10.17_GasZip.pdf | Bin 83172 -> 0 bytes ...GasZip_ReAudit.pdf => 2024.11.07_GasZip.pdf} | Bin 3 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 audit/reports/2024.10.17_GasZip.pdf rename audit/reports/{2024.11.07_GasZip_ReAudit.pdf => 2024.11.07_GasZip.pdf} (100%) diff --git a/audit/auditLog.json b/audit/auditLog.json index 693bf1c45..29c07845b 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -28,13 +28,6 @@ "auditReportPath": "./audit/reports/2024.10.14_WithdrawablePeriphery.pdf", "auditCommitHash": "b03e658b359f1696388e2aaeaf24a7c107ba462a" }, - "audit20241017": { - "auditCompletedOn": "17.10.2024", - "auditedBy": "Sujith Somraaj (individual security researcher)", - "auditorGitHandle": "sujithsomraaj", - "auditReportPath": "./audit/reports/2024.10.17_GasZip.pdf", - "auditCommitHash": "6bc644642db2d38d2a55a4e18ecd2c478e7b3ff5" - }, "audit20241105": { "auditCompletedOn": "05.11.2024", "auditedBy": "Sujith Somraaj (individual security researcher)", @@ -62,10 +55,10 @@ "1.0.0": ["audit20241007"] }, "GasZipFacet": { - "2.0.0": ["audit20241017", "audit20241107"] + "2.0.0": ["audit20241107"] }, "GasZipPeriphery": { - "1.0.0": ["audit20241017", "audit20241107"] + "1.0.0": ["audit20241107"] }, "IGasZip": { "1.0.0": ["audit20241107"] diff --git a/audit/reports/2024.10.17_GasZip.pdf b/audit/reports/2024.10.17_GasZip.pdf deleted file mode 100644 index f66aa8905b01fa4938599b3ae018c7a4c4193bdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83172 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 Ks;aL3Zd?Ej<4hy~ diff --git a/audit/reports/2024.11.07_GasZip_ReAudit.pdf b/audit/reports/2024.11.07_GasZip.pdf similarity index 100% rename from audit/reports/2024.11.07_GasZip_ReAudit.pdf rename to audit/reports/2024.11.07_GasZip.pdf From 7466528f376827223e81cf691b77844a23b29e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 11 Nov 2024 18:28:16 +0700 Subject: [PATCH 083/100] fix incorrect path in audit log --- audit/auditLog.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audit/auditLog.json b/audit/auditLog.json index 29c07845b..a25178863 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -39,7 +39,7 @@ "auditCompletedOn": "07.11.2024", "auditedBy": "Sujith Somraaj (individual security researcher)", "auditorGitHandle": "sujithsomraaj", - "auditReportPath": "./audit/reports/2024.11.07_GasZip_ReAudit.pdf", + "auditReportPath": "./audit/reports/2024.11.07_GasZip.pdf", "auditCommitHash": "2d8927ababff6ace0c577d92407bcf289ebb89c0" } }, From 0ecbba9719b7cb9526783b05d86b3d4d721bb047 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 12 Nov 2024 12:52:49 +0300 Subject: [PATCH 084/100] Redeploy to production (except zksync) --- config/gaszip.json | 5 +- config/global.json | 2 +- config/networks.json | 2 +- deployments/_deployments_log_file.json | 502 +++++++++++++++++- deployments/arbitrum.diamond.json | 3 +- deployments/arbitrum.json | 6 +- deployments/avalanche.diamond.json | 3 +- deployments/avalanche.json | 4 +- deployments/base.diamond.json | 3 +- deployments/base.json | 6 +- deployments/blast.diamond.json | 3 +- deployments/blast.json | 6 +- deployments/bsc.diamond.json | 3 +- deployments/bsc.json | 4 +- deployments/fantom.diamond.json | 3 +- deployments/fantom.json | 4 +- deployments/gnosis.diamond.json | 3 +- deployments/gnosis.json | 4 +- deployments/gravity.diamond.json | 3 +- deployments/gravity.json | 4 +- deployments/linea.diamond.json | 3 +- deployments/linea.json | 6 +- deployments/mainnet.diamond.json | 3 +- deployments/mainnet.json | 6 +- deployments/mantle.diamond.json | 3 +- deployments/mantle.json | 4 +- deployments/metis.diamond.json | 3 +- deployments/metis.json | 4 +- deployments/mode.diamond.json | 3 +- deployments/mode.json | 6 +- deployments/optimism.diamond.json | 3 +- deployments/optimism.json | 6 +- deployments/polygon.diamond.json | 3 +- deployments/polygon.json | 6 +- deployments/scroll.diamond.json | 3 +- deployments/scroll.json | 6 +- deployments/taiko.diamond.json | 3 +- deployments/taiko.json | 4 +- deployments/xlayer.diamond.json | 3 +- deployments/xlayer.json | 4 +- script/deploy/_targetState.json | 57 +- script/deploy/safe/config.ts | 2 +- script/deploy/safe/propose-to-safe.ts | 29 +- script/deploy/zksync/DeployGasZipFacet.s.sol | 36 ++ .../deploy/zksync/DeployGasZipPeriphery.s.sol | 59 ++ 45 files changed, 763 insertions(+), 75 deletions(-) create mode 100644 script/deploy/zksync/DeployGasZipFacet.s.sol create mode 100644 script/deploy/zksync/DeployGasZipPeriphery.s.sol diff --git a/config/gaszip.json b/config/gaszip.json index e656b2235..3b3ce4dac 100644 --- a/config/gaszip.json +++ b/config/gaszip.json @@ -7,7 +7,9 @@ "base": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", "bsc": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", "blast": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "fantom": "0xA60768b03eB14d940F6c9a8553329B7F9037C91b", "gnosis": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "gravity": "0x6Efc6Ead40786bD87A884382b6EA4BcA3C985e99", "linea": "0xA60768b03eB14d940F6c9a8553329B7F9037C91b", "mantle": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", "metis": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", @@ -15,7 +17,8 @@ "optimism": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", "polygon": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", "scroll": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", + "taiko": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", "xlayer": "0x2a37D63EAdFe4b4682a3c28C1c2cD4F109Cc2762", "zksync": "0x252fb662e4d7435d2a5ded8ec94d8932cf76c178" } -} +} \ No newline at end of file diff --git a/config/global.json b/config/global.json index ba7b5afd8..0db13575d 100644 --- a/config/global.json +++ b/config/global.json @@ -83,7 +83,7 @@ "aurora": "https://safe-transaction-aurora.safe.global/api", "avalanche": "https://safe-transaction-avalanche.safe.global/api", "base": "https://safe-transaction-base.safe.global/api", - "blast": "https://transaction.blast-safe.io/api", + "blast": "https://safe-transaction-blast.safe.global/api", "boba": "https://safe-transaction.mainnet.boba.network/api", "bsc": "https://safe-transaction-bsc.safe.global/api", "celo": "https://safe-transaction-celo.safe.global/api", diff --git a/config/networks.json b/config/networks.json index 8a014a0e2..187a52836 100644 --- a/config/networks.json +++ b/config/networks.json @@ -97,7 +97,7 @@ "explorerUrl": "https://blastscan.io", "explorerApiUrl": "https://api.blastscan.io/api", "multicallAddress": "0xcA11bde05977b3631167028862bE2a173976CA11", - "safeApiUrl": "https://transaction.blast-safe.io/api", + "safeApiUrl": "https://safe-transaction-blast.safe.global/api", "safeAddress": "0xdf61270fDC1A892874Fd3C0143A0A4CBA74F4EF1", "gasZipChainId": 96 }, diff --git a/deployments/_deployments_log_file.json b/deployments/_deployments_log_file.json index ebff3bb6d..a7e7e51a5 100644 --- a/deployments/_deployments_log_file.json +++ b/deployments/_deployments_log_file.json @@ -24510,6 +24510,256 @@ "VERIFIED": "true" } ] + }, + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:22:00", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "mainnet": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:18:35", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "arbitrum": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:19:32", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "avalanche": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:19:48", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "base": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:20:08", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "blast": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:20:36", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "gnosis": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:22:31", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "linea": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0x75943d7305310635945736D00235d825181018f3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 16:33:11", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000a60768b03eb14d940f6c9a8553329b7f9037c91b", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "mantle": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 16:33:44", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "metis": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xb518364B2F4e480eCc64998Da12F072A63a25093", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:23:33", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "mode": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:24:55", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "optimism": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:25:37", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "polygon": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:27:27", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "scroll": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:29:08", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "xlayer": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-11 17:29:57", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "gravity": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:01:12", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000006efc6ead40786bd87a884382b6ea4bca3c985e99", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "taiko": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0x75943d7305310635945736D00235d825181018f3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:04:08", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "fantom": { + "production": { + "2.0.0": [ + { + "ADDRESS": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:04:53", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000a60768b03eb14d940f6c9a8553329b7f9037c91b", + "SALT": "", + "VERIFIED": "true" + } + ] } } }, @@ -24526,7 +24776,257 @@ "VERIFIED": "false" } ] + }, + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:28:56", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc00000000000000000000000020b6b31d76e054c3e4de6154feca385ca58c7c15", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "mainnet": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:27:02", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc00000000000000000000000037347dd595c49212c5fc2d95ea10d1085896f51e", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "arbitrum": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:27:13", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc0000000000000000000000009e606d0d2bba344b911e2f4eab95d9235a83fe15", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "avalanche": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:27:25", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc00000000000000000000000027d4eb2854d93a1a7df8e2aed1a535b080a6f6e4", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "base": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:27:34", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc0000000000000000000000001f6974c11b833eb52ea07e0b442510165d87d82e", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "blast": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:27:43", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc000000000000000000000000df61270fdc1a892874fd3c0143a0a4cba74f4ef1", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "fantom": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:29:11", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000a60768b03eb14d940f6c9a8553329b7f9037c91b0000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc0000000000000000000000009b325b1c43bb3c018fcdb24a64e05ef4b8b8057b", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "gnosis": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:29:24", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc0000000000000000000000002bc523875b59a1ddd03ceb1f1b28c5b0e8e6654a", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "gravity": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:29:35", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000006efc6ead40786bd87a884382b6ea4bca3c985e990000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc000000000000000000000000245b16cace8730b009c5352186dce7d73c3037a1", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "linea": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x0ec6D2eEb94541C51620830D151995fCFf83Aa74", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:14:56", + "CONSTRUCTOR_ARGS": "0x000000000000000000000000a60768b03eb14d940f6c9a8553329b7f9037c91b000000000000000000000000caa342e4f781d63ef41e220d7622b97e66baecf3000000000000000000000000df61270fdc1a892874fd3c0143a0a4cba74f4ef1", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "mantle": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:15:29", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc000000000000000000000000a89a87986e8ee1ac8fdacc5ac91627010ec9f772", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "metis": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x799525cE72B5cc9eb310dc8c7b9e7A3128a6dA79", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:22:29", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000009e4c63c9a0ede2ca2e772ee48c819ca5cb4529ac000000000000000000000000925cd8289ac2d617f52974da8338867f3bb62d56", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "mode": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:23:06", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc000000000000000000000000df61270fdc1a892874fd3c0143a0a4cba74f4ef1", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "optimism": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:31:29", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc000000000000000000000000a8892ea3fddef2aa8afb1e3643a3284f978a5114", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "polygon": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:31:44", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc0000000000000000000000008bcc385948c73736423d38cc567cfede0f1826a3", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "scroll": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:32:50", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000006140b987d6b51fd75b66c3b07733beb5167c42fc000000000000000000000000df61270fdc1a892874fd3c0143a0a4cba74f4ef1", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "taiko": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x0ec6D2eEb94541C51620830D151995fCFf83Aa74", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:34:04", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc2762000000000000000000000000caa342e4f781d63ef41e220d7622b97e66baecf3000000000000000000000000a89a87986e8ee1ac8fdacc5ac91627010ec9f772", + "SALT": "", + "VERIFIED": "true" + } + ] + } + }, + "xlayer": { + "production": { + "1.0.0": [ + { + "ADDRESS": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3", + "OPTIMIZER_RUNS": "1000000", + "TIMESTAMP": "2024-11-12 11:34:56", + "CONSTRUCTOR_ARGS": "0x0000000000000000000000002a37d63eadfe4b4682a3c28c1c2cd4f109cc27620000000000000000000000002321f1a63a683a1f3634dbe1cba0d657d5f56d540000000000000000000000003fd21b437b5e0a903a8376d33824f9ba658756c2", + "SALT": "", + "VERIFIED": "true" + } + ] } } } -} +} \ No newline at end of file diff --git a/deployments/arbitrum.diamond.json b/deployments/arbitrum.diamond.json index f3dd714ee..cf8d655c7 100644 --- a/deployments/arbitrum.diamond.json +++ b/deployments/arbitrum.diamond.json @@ -156,7 +156,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/arbitrum.json b/deployments/arbitrum.json index 85d203c12..6880577b2 100644 --- a/deployments/arbitrum.json +++ b/deployments/arbitrum.json @@ -51,5 +51,7 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" +} \ No newline at end of file diff --git a/deployments/avalanche.diamond.json b/deployments/avalanche.diamond.json index 2a38d0dbe..85de422d2 100644 --- a/deployments/avalanche.diamond.json +++ b/deployments/avalanche.diamond.json @@ -128,7 +128,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/avalanche.json b/deployments/avalanche.json index c6378272c..0e8e7495c 100644 --- a/deployments/avalanche.json +++ b/deployments/avalanche.json @@ -45,5 +45,7 @@ "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } \ No newline at end of file diff --git a/deployments/base.diamond.json b/deployments/base.diamond.json index 994fa4e55..aef717355 100644 --- a/deployments/base.diamond.json +++ b/deployments/base.diamond.json @@ -144,7 +144,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/base.json b/deployments/base.json index 8291f7601..552187192 100644 --- a/deployments/base.json +++ b/deployments/base.json @@ -41,5 +41,7 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" +} \ No newline at end of file diff --git a/deployments/blast.diamond.json b/deployments/blast.diamond.json index 24daaddb6..0575eead4 100644 --- a/deployments/blast.diamond.json +++ b/deployments/blast.diamond.json @@ -92,7 +92,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD" + "TokenWrapper": "0xF2ee649caB7a0edEdED7a27821B0aCDF77778aeD", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/blast.json b/deployments/blast.json index 519ddd42b..3c971663f 100644 --- a/deployments/blast.json +++ b/deployments/blast.json @@ -28,5 +28,7 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" +} \ No newline at end of file diff --git a/deployments/bsc.diamond.json b/deployments/bsc.diamond.json index ebbff036e..ccfbc9f64 100644 --- a/deployments/bsc.diamond.json +++ b/deployments/bsc.diamond.json @@ -132,7 +132,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/bsc.json b/deployments/bsc.json index b875005f3..8258b9e94 100644 --- a/deployments/bsc.json +++ b/deployments/bsc.json @@ -46,5 +46,7 @@ "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } \ No newline at end of file diff --git a/deployments/fantom.diamond.json b/deployments/fantom.diamond.json index 0ea49d0a1..4113bee8f 100644 --- a/deployments/fantom.diamond.json +++ b/deployments/fantom.diamond.json @@ -100,7 +100,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/fantom.json b/deployments/fantom.json index d33576473..68c91daa6 100644 --- a/deployments/fantom.json +++ b/deployments/fantom.json @@ -35,5 +35,7 @@ "SquidFacet": "0x5C2C3F56e33F45389aa4e1DA4D3a807A532a910c", "GenericSwapFacetV3": "0x31a9b1835864706Af10103b31Ea2b79bdb995F5F", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } \ No newline at end of file diff --git a/deployments/gnosis.diamond.json b/deployments/gnosis.diamond.json index 2b7b148a8..00aac64ab 100644 --- a/deployments/gnosis.diamond.json +++ b/deployments/gnosis.diamond.json @@ -104,7 +104,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/gnosis.json b/deployments/gnosis.json index 14cb386c5..90e89c8ac 100644 --- a/deployments/gnosis.json +++ b/deployments/gnosis.json @@ -31,5 +31,7 @@ "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", "GenericSwapFacetV3": "0x31a9b1835864706Af10103b31Ea2b79bdb995F5F", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } \ No newline at end of file diff --git a/deployments/gravity.diamond.json b/deployments/gravity.diamond.json index ac7295607..99d4c29ff 100644 --- a/deployments/gravity.diamond.json +++ b/deployments/gravity.diamond.json @@ -68,7 +68,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x6A3d6652fb7be72200a47313C092342218aAeb72", "RelayerCelerIM": "", - "TokenWrapper": "0x7fA60f4A59Dd8285C5Fcd8fd2A92A2Ca45ef8a0C" + "TokenWrapper": "0x7fA60f4A59Dd8285C5Fcd8fd2A92A2Ca45ef8a0C", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/gravity.json b/deployments/gravity.json index 9852a454b..d7707e159 100644 --- a/deployments/gravity.json +++ b/deployments/gravity.json @@ -21,5 +21,7 @@ "ReceiverStargateV2": "0x6A3d6652fb7be72200a47313C092342218aAeb72", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", "Receiver": "0x2DeB3bFa2b19024A0c1Ba299b6b79276f1F77b14", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } \ No newline at end of file diff --git a/deployments/linea.diamond.json b/deployments/linea.diamond.json index 00cad3d95..86cc091b4 100644 --- a/deployments/linea.diamond.json +++ b/deployments/linea.diamond.json @@ -128,7 +128,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x6CA57d9846f9a1fd48368762b743a047eC4f81A6", "RelayerCelerIM": "", - "TokenWrapper": "0xf6C9605c6E231C1547b7a6545d93e7233f97322a" + "TokenWrapper": "0xf6C9605c6E231C1547b7a6545d93e7233f97322a", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/linea.json b/deployments/linea.json index bd43ba3c3..a9b99da9d 100644 --- a/deployments/linea.json +++ b/deployments/linea.json @@ -38,5 +38,7 @@ "EmergencyPauseFacet": "0xe07c030dDC7Fb9ca23b633b1028106DAA5fdbF96", "AcrossFacetV3": "0x80b96CA9B47aCD6c2a888128fEb9b0F4Ea518FEc", "ReceiverAcrossV3": "0x4BB377A1A624bDeF72d352891dc5E64087345fe6", - "AcrossFacetPackedV3": "0xAfEB7e1DA0Ff4DcD0dbC4De3F51a933E2054B0ed" -} + "AcrossFacetPackedV3": "0xAfEB7e1DA0Ff4DcD0dbC4De3F51a933E2054B0ed", + "GasZipFacet": "0x75943d7305310635945736D00235d825181018f3", + "GasZipPeriphery": "0x0ec6D2eEb94541C51620830D151995fCFf83Aa74" +} \ No newline at end of file diff --git a/deployments/mainnet.diamond.json b/deployments/mainnet.diamond.json index 8428ec9f0..04fe84c3c 100644 --- a/deployments/mainnet.diamond.json +++ b/deployments/mainnet.diamond.json @@ -180,7 +180,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/mainnet.json b/deployments/mainnet.json index d28177b4c..20b0c4dcc 100644 --- a/deployments/mainnet.json +++ b/deployments/mainnet.json @@ -59,5 +59,7 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" +} \ No newline at end of file diff --git a/deployments/mantle.diamond.json b/deployments/mantle.diamond.json index ef7fe724e..c33081241 100644 --- a/deployments/mantle.diamond.json +++ b/deployments/mantle.diamond.json @@ -76,7 +76,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", - "TokenWrapper": "0x0263180888007D45340F86eC0b610d250BbDcB23" + "TokenWrapper": "0x0263180888007D45340F86eC0b610d250BbDcB23", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/mantle.json b/deployments/mantle.json index a386b2bd4..e2d6757b9 100644 --- a/deployments/mantle.json +++ b/deployments/mantle.json @@ -25,5 +25,7 @@ "StargateFacetV2": "0x6e378C84e657C57b2a8d183CFf30ee5CC8989b61", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "LiFiDEXAggregator": "0x6140b987d6B51Fd75b66C3B07733Beb5167c42fc", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } \ No newline at end of file diff --git a/deployments/metis.diamond.json b/deployments/metis.diamond.json index d47ff324a..da156315b 100644 --- a/deployments/metis.diamond.json +++ b/deployments/metis.diamond.json @@ -84,7 +84,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0xe7392Fc0f61503dB53C70789c6F2c34C0675C929", "RelayerCelerIM": "", - "TokenWrapper": "0x01bDf46A673FC3c081ddBD21cb51fBA4972d00aC" + "TokenWrapper": "0x01bDf46A673FC3c081ddBD21cb51fBA4972d00aC", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/metis.json b/deployments/metis.json index 0b5d346da..59b8f51b6 100644 --- a/deployments/metis.json +++ b/deployments/metis.json @@ -25,5 +25,7 @@ "StargateFacetV2": "0xCb667deA2894ab64e8e75EACB0d5d027AC672e25", "ReceiverStargateV2": "0xe7392Fc0f61503dB53C70789c6F2c34C0675C929", "LiFiDEXAggregator": "0x9E4c63c9a0EDE2Ca2e772ee48C819Ca5CB4529AC", - "EmergencyPauseFacet": "0xD5734b44Bb7Ada52ea6503088612E70a2a612371" + "EmergencyPauseFacet": "0xD5734b44Bb7Ada52ea6503088612E70a2a612371", + "GasZipFacet": "0xb518364B2F4e480eCc64998Da12F072A63a25093", + "GasZipPeriphery": "0x799525cE72B5cc9eb310dc8c7b9e7A3128a6dA79" } \ No newline at end of file diff --git a/deployments/mode.diamond.json b/deployments/mode.diamond.json index 3605b3b8e..ca58f6bbf 100644 --- a/deployments/mode.diamond.json +++ b/deployments/mode.diamond.json @@ -88,7 +88,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/mode.json b/deployments/mode.json index 5c2cb696c..ccd77f5bc 100644 --- a/deployments/mode.json +++ b/deployments/mode.json @@ -28,5 +28,7 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" +} \ No newline at end of file diff --git a/deployments/optimism.diamond.json b/deployments/optimism.diamond.json index 66cbfe053..74f11ede4 100644 --- a/deployments/optimism.diamond.json +++ b/deployments/optimism.diamond.json @@ -156,7 +156,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/optimism.json b/deployments/optimism.json index de6819514..8b4b12605 100644 --- a/deployments/optimism.json +++ b/deployments/optimism.json @@ -50,5 +50,7 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" +} \ No newline at end of file diff --git a/deployments/polygon.diamond.json b/deployments/polygon.diamond.json index bd7c4285e..6c38044e1 100644 --- a/deployments/polygon.diamond.json +++ b/deployments/polygon.diamond.json @@ -156,7 +156,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "0x6a8b11bF29C0546991DEcD6E0Db8cC7Fda22bA97", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/polygon.json b/deployments/polygon.json index 0a8e98c7f..5db2407d3 100644 --- a/deployments/polygon.json +++ b/deployments/polygon.json @@ -54,5 +54,7 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" +} \ No newline at end of file diff --git a/deployments/scroll.diamond.json b/deployments/scroll.diamond.json index 4a9f50126..b64313d11 100644 --- a/deployments/scroll.diamond.json +++ b/deployments/scroll.diamond.json @@ -104,7 +104,8 @@ "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", "ReceiverStargateV2": "0x1493e7B8d4DfADe0a178dAD9335470337A3a219A", "RelayerCelerIM": "", - "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d" + "TokenWrapper": "0x5215E9fd223BC909083fbdB2860213873046e45d", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/scroll.json b/deployments/scroll.json index d4053ff43..aa461cf09 100644 --- a/deployments/scroll.json +++ b/deployments/scroll.json @@ -32,5 +32,7 @@ "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", "AcrossFacetV3": "0x6e00e0a7685Ca22c288d56D9E7924746B5043Ee7", "ReceiverAcrossV3": "0xB9CEc304899037E661F49DdFa7f64943b5920072", - "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f" -} + "AcrossFacetPackedV3": "0x20F3FFf5A89e988c4109A6e496a839480B1B558f", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" +} \ No newline at end of file diff --git a/deployments/taiko.diamond.json b/deployments/taiko.diamond.json index c8ff2a516..0316024f3 100644 --- a/deployments/taiko.diamond.json +++ b/deployments/taiko.diamond.json @@ -72,7 +72,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "0x6CA57d9846f9a1fd48368762b743a047eC4f81A6", "RelayerCelerIM": "", - "TokenWrapper": "0xD989E929517B0e5eD0c8EfE7607Fa167B697cBa8" + "TokenWrapper": "0xD989E929517B0e5eD0c8EfE7607Fa167B697cBa8", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/taiko.json b/deployments/taiko.json index 65a865d66..b4c0620aa 100644 --- a/deployments/taiko.json +++ b/deployments/taiko.json @@ -22,5 +22,7 @@ "ReceiverStargateV2": "0x6CA57d9846f9a1fd48368762b743a047eC4f81A6", "TokenWrapper": "0xD989E929517B0e5eD0c8EfE7607Fa167B697cBa8", "Receiver": "0xe38326Ae727e3fA6669249063Ce7b8ea1754e756", - "EmergencyPauseFacet": "0xe07c030dDC7Fb9ca23b633b1028106DAA5fdbF96" + "EmergencyPauseFacet": "0xe07c030dDC7Fb9ca23b633b1028106DAA5fdbF96", + "GasZipFacet": "0x75943d7305310635945736D00235d825181018f3", + "GasZipPeriphery": "0x0ec6D2eEb94541C51620830D151995fCFf83Aa74" } \ No newline at end of file diff --git a/deployments/xlayer.diamond.json b/deployments/xlayer.diamond.json index dd7c1a9ce..4a0185c40 100644 --- a/deployments/xlayer.diamond.json +++ b/deployments/xlayer.diamond.json @@ -72,7 +72,8 @@ "ReceiverAcrossV3": "", "ReceiverStargateV2": "", "RelayerCelerIM": "", - "TokenWrapper": "0x833Be894C596b15FAe740C2D522d660084c48B05" + "TokenWrapper": "0x833Be894C596b15FAe740C2D522d660084c48B05", + "GasZipPeriphery": "" } } } \ No newline at end of file diff --git a/deployments/xlayer.json b/deployments/xlayer.json index b7873a0bd..5f7932c95 100644 --- a/deployments/xlayer.json +++ b/deployments/xlayer.json @@ -21,5 +21,7 @@ "LiFuelFeeCollector": "0x12904D12A84702f9F079E1e393fdAbD313496e97", "TokenWrapper": "0x833Be894C596b15FAe740C2D522d660084c48B05", "LiFiDEXAggregator": "0x2321F1a63A683a1F3634Dbe1CbA0d657D5F56d54", - "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61" + "EmergencyPauseFacet": "0x6F2baA7cd5F156CA1B132F7FF11E0fa2aD775F61", + "GasZipFacet": "0xF5c923a087fb3c554579e2DD10AB6E37E0f6F849", + "GasZipPeriphery": "0x9a21E33F1a78b17DAd32010CeDB9Fd2F071C17d3" } \ No newline at end of file diff --git a/script/deploy/_targetState.json b/script/deploy/_targetState.json index d9e5b1ff3..c460d560b 100644 --- a/script/deploy/_targetState.json +++ b/script/deploy/_targetState.json @@ -52,7 +52,8 @@ "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0", - "ThorSwapFacet": "1.2.0" + "ThorSwapFacet": "1.2.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -103,7 +104,8 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -146,7 +148,8 @@ "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0", - "ThorSwapFacet": "1.2.0" + "ThorSwapFacet": "1.2.0", + "GasZipPeriphery": "1.0.0" } }, "staging": { @@ -258,7 +261,8 @@ "HopFacet": "2.0.0", "HopFacetPacked": "1.0.6", "HopFacetOptimized": "2.0.0", - "OmniBridgeFacet": "1.0.0" + "OmniBridgeFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -292,7 +296,8 @@ "CelerIMFacetMutable": "2.0.0", "HyphenFacet": "1.0.0", "SquidFacet": "1.0.0", - "StargateFacet": "2.2.0" + "StargateFacet": "2.2.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -335,7 +340,8 @@ "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", "SymbiosisFacet": "1.0.0", - "ThorSwapFacet": "1.2.0" + "ThorSwapFacet": "1.2.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -386,7 +392,8 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -435,7 +442,8 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -658,7 +666,8 @@ "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "CelerIMFacetMutable": "2.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -730,7 +739,8 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -775,7 +785,8 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -836,7 +847,8 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -875,7 +887,8 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -910,7 +923,8 @@ "AcrossFacetPackedV3": "1.0.0", "ReceiverAcrossV3": "1.0.0", "AmarokFacet": "3.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -943,7 +957,8 @@ "StargateFacet": "2.2.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -1010,7 +1025,8 @@ "CBridgeFacet": "1.0.0", "CBridgeFacetPacked": "1.0.3", "SquidFacet": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -1104,7 +1120,8 @@ "Permit2Proxy": "1.0.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -1138,7 +1155,8 @@ "RelayerCelerIM": "2.0.0", "StargateFacetV2": "1.0.1", "ReceiverStargateV2": "1.0.0", - "SymbiosisFacet": "1.0.0" + "SymbiosisFacet": "1.0.0", + "GasZipPeriphery": "1.0.0" } } }, @@ -1377,7 +1395,8 @@ "LiFiDEXAggregator": "1.0.0", "Permit2Proxy": "1.0.0", "CBridgeFacet": "1.0.0", - "CBridgeFacetPacked": "1.0.3" + "CBridgeFacetPacked": "1.0.3", + "GasZipPeriphery": "1.0.0" } } }, diff --git a/script/deploy/safe/config.ts b/script/deploy/safe/config.ts index a1b22de4f..4bbf85f30 100644 --- a/script/deploy/safe/config.ts +++ b/script/deploy/safe/config.ts @@ -7,7 +7,7 @@ export const safeApiUrls: Record = { aurora: 'https://safe-transaction-aurora.safe.global/api', avalanche: 'https://safe-transaction-avalanche.safe.global/api', base: 'https://safe-transaction-base.safe.global/api', - blast: 'https://transaction.blast-safe.io/api', + blast: 'https://safe-transaction-blast.safe.global/api', boba: 'https://safe-transaction.mainnet.boba.network/api', bsc: 'https://safe-transaction-bsc.safe.global/api', celo: 'https://safe-transaction-celo.safe.global/api', diff --git a/script/deploy/safe/propose-to-safe.ts b/script/deploy/safe/propose-to-safe.ts index 58def0d61..6a3239a02 100644 --- a/script/deploy/safe/propose-to-safe.ts +++ b/script/deploy/safe/propose-to-safe.ts @@ -11,6 +11,21 @@ import { import * as chains from 'viem/chains' import { getSafeUtilityContracts, safeAddresses, safeApiUrls } from './config' import { getViemChainForNetworkName } from '../../utils/viemScriptHelpers' +import consola from 'consola' + +const retry = async (func: () => Promise, retries = 3): Promise => { + try { + const result = await func() + return result + } catch (e) { + if (retries > 0) { + consola.error('Retry after error:', e) + return retry(func, retries - 1) + } + + throw e + } +} const chainMap: Record = {} for (const [k, v] of Object.entries(chains)) { @@ -99,12 +114,14 @@ const main = defineCommand({ console.info('Proposing transaction to', args.to) // Propose transaction to the service - await safeService.proposeTransaction({ - safeAddress: await protocolKit.getAddress(), - safeTransactionData: safeTransaction.data, - safeTxHash, - senderAddress, - senderSignature: signature.data, + await retry(async () => { + safeService.proposeTransaction({ + safeAddress: await protocolKit.getAddress(), + safeTransactionData: safeTransaction.data, + safeTxHash, + senderAddress, + senderSignature: signature.data, + }) }) console.info('Transaction proposed') diff --git a/script/deploy/zksync/DeployGasZipFacet.s.sol b/script/deploy/zksync/DeployGasZipFacet.s.sol new file mode 100644 index 000000000..627bb265f --- /dev/null +++ b/script/deploy/zksync/DeployGasZipFacet.s.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { GasZipFacet } from "lifi/Facets/GasZipFacet.sol"; +import { stdJson } from "forge-std/Script.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("GasZipFacet") {} + + function run() + public + returns (GasZipFacet deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + + deployed = GasZipFacet(deploy(type(GasZipFacet).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + string memory gasZipConfig = string.concat( + root, + "/config/gaszip.json" + ); + + string memory gasZipConfigJson = vm.readFile(gasZipConfig); + + address gasZipRouter = gasZipConfigJson.readAddress( + string.concat(".gasZipRouters.", network) + ); + + return abi.encode(gasZipRouter); + } +} diff --git a/script/deploy/zksync/DeployGasZipPeriphery.s.sol b/script/deploy/zksync/DeployGasZipPeriphery.s.sol new file mode 100644 index 000000000..0bef977e4 --- /dev/null +++ b/script/deploy/zksync/DeployGasZipPeriphery.s.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import { DeployScriptBase } from "./utils/DeployScriptBase.sol"; +import { GasZipPeriphery } from "lifi/Periphery/GasZipPeriphery.sol"; +import { stdJson } from "forge-std/Script.sol"; + +contract DeployScript is DeployScriptBase { + using stdJson for string; + + constructor() DeployScriptBase("GasZipPeriphery") {} + + function run() + public + returns (GasZipPeriphery deployed, bytes memory constructorArgs) + { + constructorArgs = getConstructorArgs(); + + deployed = GasZipPeriphery(deploy(type(GasZipPeriphery).creationCode)); + } + + function getConstructorArgs() internal override returns (bytes memory) { + // get gasZipRouter address + string memory gasZipConfig = string.concat( + root, + "/config/gaszip.json" + ); + + string memory gasZipConfigJson = vm.readFile(gasZipConfig); + + address gasZipRouter = gasZipConfigJson.readAddress( + string.concat(".gasZipRouters.", network) + ); + + // get LiFiDEXAggregator address + string memory deployLog = string.concat( + root, + "/deployments/", + network, + ".", + fileSuffix, + "json" + ); + string memory json = vm.readFile(deployLog); + + address liFiDEXAggregator = json.readAddress(".LiFiDEXAggregator"); + + // get network's SAFE address to become contract owner for potential fund withdrawals + string memory networks = string.concat(root, "/config/networks.json"); + + string memory networksJson = vm.readFile(networks); + + address safeAddress = networksJson.readAddress( + string.concat(".", network, ".safeAddress") + ); + + return abi.encode(gasZipRouter, liFiDEXAggregator, safeAddress); + } +} From f7c2ed7eddb5c9ff6005ecd8501f1c9086616095 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 12 Nov 2024 14:13:57 +0300 Subject: [PATCH 085/100] Update pragma --- src/Facets/GasZipFacet.sol | 2 +- src/Periphery/GasZipPeriphery.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Facets/GasZipFacet.sol b/src/Facets/GasZipFacet.sol index 2198cfe1e..7af03cd92 100644 --- a/src/Facets/GasZipFacet.sol +++ b/src/Facets/GasZipFacet.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.17; +pragma solidity ^0.8.17; import { ILiFi } from "../Interfaces/ILiFi.sol"; import { IGasZip } from "../Interfaces/IGasZip.sol"; diff --git a/src/Periphery/GasZipPeriphery.sol b/src/Periphery/GasZipPeriphery.sol index 37f79ba5e..1c4617597 100644 --- a/src/Periphery/GasZipPeriphery.sol +++ b/src/Periphery/GasZipPeriphery.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.17; +pragma solidity ^0.8.17; import { ILiFi } from "../Interfaces/ILiFi.sol"; import { IGasZip } from "../Interfaces/IGasZip.sol"; From b735e775c9b49e2adc94ab55b56f392bf3e1d9c1 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Wed, 13 Nov 2024 16:32:45 +0300 Subject: [PATCH 086/100] update audit --- audit/reports/2024.11.07_GasZip.pdf | Bin 86333 -> 31309 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/audit/reports/2024.11.07_GasZip.pdf b/audit/reports/2024.11.07_GasZip.pdf index 5a08b8cfa1ac7d53b7b16839712fa6ea932f2c9e..d1d9d46a9332cb51fe7c0ba2c124f2f10d73f3b0 100644 GIT binary patch delta 27173 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