diff --git a/audit/auditLog.json b/audit/auditLog.json index 27a6a5dec..ccade496c 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -91,6 +91,13 @@ "auditReportPath": "./audit/reports/2025.01.09_ThorSwapFacet(v1.2.1).pdf", "auditCommitHash": "5005bf62858a9cb2a7628976ef870e91bb732a75" }, + "audit20250109_2": { + "auditCompletedOn": "09.01.2025", + "auditedBy": "Sujith Somraaj (individual security researcher)", + "auditorGitHandle": "sujithsomraaj", + "auditReportPath": "./audit/reports/2025.01.09_WithdrawablePeripheryUpdates.pdf", + "auditCommitHash": "ec76b33cedd69338e569c7994b99b03cb7e6d502" + }, "audit20250109_3": { "auditCompletedOn": "09.01.2025", "auditedBy": "Sujith Somraaj (individual security researcher)", @@ -129,6 +136,16 @@ "audit20241105" ] }, + "ERC20Proxy": { + "1.1.0": [ + "audit20250109_2" + ] + }, + "Executor": { + "2.1.0": [ + "audit20250109_2" + ] + }, "FeeCollector": { "1.0.1": [ "audit20250109_3" @@ -167,6 +184,9 @@ "1.5.0": [ "audit20241203" ], + "1.6.0": [ + "audit20250109_2" + ], "1.5.1": [ "audit20250109_3" ] @@ -187,6 +207,9 @@ "Receiver": { "2.0.3": [ "audit20250109_3" + ], + "2.1.0": [ + "audit20250109_2" ] }, "ReceiverAcrossV3": { @@ -196,17 +219,26 @@ "1.0.1": [ "audit20241206" ], - "1.0.3": [ - "audit20250109_3" + "1.1.0": [ + "audit20250109_2" ] }, "ReceiverStargateV2": { "1.0.1": [ "audit20250109_3" + ], + "1.1.0": [ + "audit20250109_2" ] }, "RelayerCelerIM": { - "2.0.1": [ + "1.0.3": [ + "audit20250109_3" + ], + "2.1.0": [ + "audit20250109_2" + ], + "2.1.1": [ "audit20250109_3" ] }, @@ -228,6 +260,9 @@ "TokenWrapper": { "1.0.1": [ "audit20250109_3" + ], + "1.1.0": [ + "audit20250109_2" ] }, "WithdrawablePeriphery": { diff --git a/audit/reports/2025.01.09_WithdrawablePeripheryUpdates.pdf b/audit/reports/2025.01.09_WithdrawablePeripheryUpdates.pdf new file mode 100644 index 000000000..6c55534fa Binary files /dev/null and b/audit/reports/2025.01.09_WithdrawablePeripheryUpdates.pdf differ diff --git a/docs/Receiver.md b/docs/Receiver.md index 46d0b7f8f..420ee1c82 100644 --- a/docs/Receiver.md +++ b/docs/Receiver.md @@ -68,17 +68,3 @@ function swapAndCompleteBridgeTokens( address payable receiver ) ``` - -This method is used to send remaining tokens to receiver. - -```solidity -/// @notice Send remaining token to receiver -/// @param assetId token received from the other chain -/// @param receiver address that will receive tokens in the end -/// @param amount amount of token -function pullToken( - address assetId, - address payable receiver, - uint256 amount -) -``` diff --git a/docs/ReceiverAcrossV3.md b/docs/ReceiverAcrossV3.md index 3ee8cd5cc..51d19a188 100644 --- a/docs/ReceiverAcrossV3.md +++ b/docs/ReceiverAcrossV3.md @@ -23,17 +23,3 @@ The contract has one method which will (and can only) be called through the Acro bytes memory message ) ``` - -Furthermore there is one (admin) method that allows withdrawals of stuck tokens by LI.FI administrators: - -```solidity -/// @notice Send remaining token to receiver -/// @param assetId token received from the other chain -/// @param receiver address that will receive tokens in the end -/// @param amount amount of token -function pullToken( - address assetId, - address payable receiver, - uint256 amount -) -``` diff --git a/docs/ReceiverStargateV2.md b/docs/ReceiverStargateV2.md index c33c96c67..e656c0dab 100644 --- a/docs/ReceiverStargateV2.md +++ b/docs/ReceiverStargateV2.md @@ -24,17 +24,3 @@ The contract has one method which will be called through the LayerZero endpoint: bytes calldata ) ``` - -Furthermore there is one (admin) method that allows withdrawals of stuck tokens by LI.FI administrators: - -```solidity -/// @notice Send remaining token to receiver -/// @param assetId token received from the other chain -/// @param receiver address that will receive tokens in the end -/// @param amount amount of token -function pullToken( - address assetId, - address payable receiver, - uint256 amount -) -``` diff --git a/script/deploy/facets/DeployTokenWrapper.s.sol b/script/deploy/facets/DeployTokenWrapper.s.sol index ff964efc5..72d0806de 100644 --- a/script/deploy/facets/DeployTokenWrapper.s.sol +++ b/script/deploy/facets/DeployTokenWrapper.s.sol @@ -26,6 +26,12 @@ contract DeployScript is DeployScriptBase { "/config/networks.json" ); + // get path of global config file + string memory globalConfigPath = string.concat( + root, + "/config/global.json" + ); + // read file into json variable string memory tokenWrapperConfigJSON = vm.readFile(tokenWrapperConfig); @@ -34,6 +40,14 @@ contract DeployScript is DeployScriptBase { string.concat(".", network, ".wrappedNativeAddress") ); - return abi.encode(wrappedNativeAddress); + // read file into json variable + string memory globalConfigJson = vm.readFile(globalConfigPath); + + // extract refundWallet address + address refundWalletAddress = globalConfigJson.readAddress( + ".refundWallet" + ); + + return abi.encode(wrappedNativeAddress, refundWalletAddress); } } diff --git a/script/deploy/zksync/DeployTokenWrapper.s.sol b/script/deploy/zksync/DeployTokenWrapper.s.sol index 0ce17fcd7..72d0806de 100644 --- a/script/deploy/zksync/DeployTokenWrapper.s.sol +++ b/script/deploy/zksync/DeployTokenWrapper.s.sol @@ -20,20 +20,34 @@ contract DeployScript is DeployScriptBase { } function getConstructorArgs() internal override returns (bytes memory) { - // get path of global network config file - string memory networkConfig = string.concat( + // get path of global config file + string memory tokenWrapperConfig = string.concat( root, "/config/networks.json" ); + // get path of global config file + string memory globalConfigPath = string.concat( + root, + "/config/global.json" + ); + // read file into json variable - string memory networkConfigJSON = vm.readFile(networkConfig); + string memory tokenWrapperConfigJSON = vm.readFile(tokenWrapperConfig); // extract wrapped token address for the given network - address wrappedNativeAddress = networkConfigJSON.readAddress( + address wrappedNativeAddress = tokenWrapperConfigJSON.readAddress( string.concat(".", network, ".wrappedNativeAddress") ); - return abi.encode(wrappedNativeAddress); + // read file into json variable + string memory globalConfigJson = vm.readFile(globalConfigPath); + + // extract refundWallet address + address refundWalletAddress = globalConfigJson.readAddress( + ".refundWallet" + ); + + return abi.encode(wrappedNativeAddress, refundWalletAddress); } } diff --git a/src/Periphery/ERC20Proxy.sol b/src/Periphery/ERC20Proxy.sol index cdbe64abc..dd0318f47 100644 --- a/src/Periphery/ERC20Proxy.sol +++ b/src/Periphery/ERC20Proxy.sol @@ -1,27 +1,22 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { LibAsset } from "../Libraries/LibAsset.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; /// @title ERC20 Proxy /// @author LI.FI (https://li.fi) /// @notice Proxy contract for safely transferring ERC20 tokens for swaps/executions -/// @custom:version 1.0.0 -contract ERC20Proxy is Ownable { +/// @custom:version 1.1.0 +contract ERC20Proxy is WithdrawablePeriphery { /// Storage /// mapping(address => bool) public authorizedCallers; - /// Errors /// - error UnAuthorized(); - /// Events /// event AuthorizationChanged(address indexed caller, bool authorized); /// Constructor - constructor(address _owner) { - transferOwnership(_owner); - } + constructor(address _owner) WithdrawablePeriphery(_owner) {} /// @notice Sets whether or not a specified caller is authorized to call this contract /// @param caller the caller to change authorization for diff --git a/src/Periphery/Executor.sol b/src/Periphery/Executor.sol index 8832b2606..4c7c11bca 100644 --- a/src/Periphery/Executor.sol +++ b/src/Periphery/Executor.sol @@ -7,6 +7,7 @@ import { LibAsset } from "../Libraries/LibAsset.sol"; import { UnAuthorized } from "lifi/Errors/GenericErrors.sol"; import { ILiFi } from "../Interfaces/ILiFi.sol"; import { IERC20Proxy } from "../Interfaces/IERC20Proxy.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -14,8 +15,14 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title Executor /// @author LI.FI (https://li.fi) /// @notice Arbitrary execution contract used for cross-chain swaps and message passing -/// @custom:version 2.0.0 -contract Executor is ILiFi, ReentrancyGuard, ERC1155Holder, ERC721Holder { +/// @custom:version 2.1.0 +contract Executor is + ILiFi, + ReentrancyGuard, + ERC1155Holder, + ERC721Holder, + WithdrawablePeriphery +{ /// Storage /// /// @notice The address of the ERC20Proxy contract @@ -64,7 +71,10 @@ contract Executor is ILiFi, ReentrancyGuard, ERC1155Holder, ERC721Holder { /// Constructor /// @notice Initialize local variables for the Executor /// @param _erc20Proxy The address of the ERC20Proxy contract - constructor(address _erc20Proxy) { + constructor( + address _erc20Proxy, + address _owner + ) WithdrawablePeriphery(_owner) { erc20Proxy = IERC20Proxy(_erc20Proxy); emit ERC20ProxySet(_erc20Proxy); } diff --git a/src/Periphery/LiFiDEXAggregator.sol b/src/Periphery/LiFiDEXAggregator.sol index 0bd818fe6..c8e926c4e 100644 --- a/src/Periphery/LiFiDEXAggregator.sol +++ b/src/Periphery/LiFiDEXAggregator.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.17; import { SafeERC20, IERC20, IERC20Permit } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; address constant NATIVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; @@ -23,8 +23,8 @@ uint160 constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970 /// @title LiFi DEX Aggregator /// @author Ilya Lyalin (contract copied from: https://github.com/sushiswap/sushiswap/blob/c8c80dec821003eb72eb77c7e0446ddde8ca9e1e/protocols/route-processor/contracts/RouteProcessor4.sol) /// @notice Processes calldata to swap using various DEXs -/// @custom:version 1.5.1 -contract LiFiDEXAggregator is Ownable { +/// @custom:version 1.6.0 +contract LiFiDEXAggregator is WithdrawablePeriphery { using SafeERC20 for IERC20; using Approve for IERC20; using SafeERC20 for IERC20Permit; @@ -58,13 +58,17 @@ contract LiFiDEXAggregator is Ownable { modifier onlyOwnerOrPriviledgedUser() { require( - msg.sender == owner() || priviledgedUsers[msg.sender], + msg.sender == owner || priviledgedUsers[msg.sender], "RP: caller is not the owner or a privileged user" ); _; } - constructor(address _bentoBox, address[] memory priviledgedUserList) { + constructor( + address _bentoBox, + address[] memory priviledgedUserList, + address _owner + ) WithdrawablePeriphery(_owner) { bentoBox = IBentoBoxMinimal(_bentoBox); lastCalledPool = IMPOSSIBLE_POOL_ADDRESS; diff --git a/src/Periphery/Receiver.sol b/src/Periphery/Receiver.sol index 86a269c7e..f75f1bb04 100644 --- a/src/Periphery/Receiver.sol +++ b/src/Periphery/Receiver.sol @@ -8,14 +8,14 @@ import { LibSwap } from "../Libraries/LibSwap.sol"; import { LibAsset } from "../Libraries/LibAsset.sol"; import { ILiFi } from "../Interfaces/ILiFi.sol"; import { IExecutor } from "../Interfaces/IExecutor.sol"; -import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { ExternalCallFailed, UnAuthorized } from "../Errors/GenericErrors.sol"; /// @title Receiver /// @author LI.FI (https://li.fi) /// @notice Arbitrary execution contract used for cross-chain swaps and message passing -/// @custom:version 2.0.3 -contract Receiver is ILiFi, ReentrancyGuard, TransferrableOwnership { +/// @custom:version 2.1.0 +contract Receiver is ILiFi, ReentrancyGuard, WithdrawablePeriphery { using SafeERC20 for IERC20; /// Storage /// @@ -51,8 +51,7 @@ contract Receiver is ILiFi, ReentrancyGuard, TransferrableOwnership { address _amarokRouter, address _executor, uint256 _recoverGas - ) TransferrableOwnership(_owner) { - owner = _owner; + ) WithdrawablePeriphery(_owner) { sgRouter = _sgRouter; amarokRouter = _amarokRouter; executor = IExecutor(_executor); @@ -168,22 +167,6 @@ contract Receiver is ILiFi, ReentrancyGuard, TransferrableOwnership { } } - /// @notice Send remaining token to receiver - /// @param assetId token received from the other chain - /// @param receiver address that will receive tokens in the end - /// @param amount amount of token - function pullToken( - address assetId, - address payable receiver, - uint256 amount - ) external onlyOwner { - if (LibAsset.isNativeAsset(assetId)) { - SafeTransferLib.safeTransferETH(receiver, amount); - } else { - IERC20(assetId).safeTransfer(receiver, amount); - } - } - /// Private Methods /// /// @notice Performs a swap before completing a cross-chain transaction diff --git a/src/Periphery/ReceiverAcrossV3.sol b/src/Periphery/ReceiverAcrossV3.sol index 3c3f5b7db..947bbe343 100644 --- a/src/Periphery/ReceiverAcrossV3.sol +++ b/src/Periphery/ReceiverAcrossV3.sol @@ -5,15 +5,15 @@ import { LibSwap } from "../Libraries/LibSwap.sol"; import { LibAsset } from "../Libraries/LibAsset.sol"; import { ILiFi } from "../Interfaces/ILiFi.sol"; import { IExecutor } from "../Interfaces/IExecutor.sol"; -import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { ExternalCallFailed, UnAuthorized } from "../Errors/GenericErrors.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; /// @title ReceiverAcrossV3 /// @author LI.FI (https://li.fi) /// @notice Arbitrary execution contract used for cross-chain swaps and message passing via AcrossV3 -/// @custom:version 1.0.3 -contract ReceiverAcrossV3 is ILiFi, TransferrableOwnership { +/// @custom:version 1.1.0 +contract ReceiverAcrossV3 is ILiFi, WithdrawablePeriphery { using SafeTransferLib for address; /// Storage /// @@ -33,8 +33,7 @@ contract ReceiverAcrossV3 is ILiFi, TransferrableOwnership { address _owner, address _executor, address _spokepool - ) TransferrableOwnership(_owner) { - owner = _owner; + ) WithdrawablePeriphery(_owner) { executor = IExecutor(_executor); spokepool = _spokepool; } @@ -71,27 +70,10 @@ contract ReceiverAcrossV3 is ILiFi, TransferrableOwnership { ); } - /// @notice Send remaining token to receiver - /// @param assetId address of the token to be withdrawn (not to be confused with StargateV2's assetIds which are uint16 values) - /// @param receiver address that will receive tokens in the end - /// @param amount amount of token - function pullToken( - address assetId, - address payable receiver, - uint256 amount - ) external onlyOwner { - if (LibAsset.isNativeAsset(assetId)) { - // solhint-disable-next-line avoid-low-level-calls - SafeTransferLib.safeTransferETH(receiver, amount); - } else { - assetId.safeTransfer(receiver, amount); - } - } - /// Private Methods /// /// @notice Performs a swap before completing a cross-chain transaction - // @notice Since Across will always send wrappedNative to contract, we do not need a native handling here + /// @notice Since Across will always send wrappedNative to contract, we do not need a native handling here /// @param _transactionId the transaction id associated with the operation /// @param _swapData array of data needed for swaps /// @param assetId address of the token received from the source chain (not to be confused with StargateV2's assetIds which are uint16 values) diff --git a/src/Periphery/ReceiverStargateV2.sol b/src/Periphery/ReceiverStargateV2.sol index c75d9d9bd..2f89611a6 100644 --- a/src/Periphery/ReceiverStargateV2.sol +++ b/src/Periphery/ReceiverStargateV2.sol @@ -8,7 +8,7 @@ import { LibAsset } from "../Libraries/LibAsset.sol"; import { OFTComposeMsgCodec } from "../Libraries/OFTComposeMsgCodec.sol"; import { ILiFi } from "../Interfaces/ILiFi.sol"; import { IExecutor } from "../Interfaces/IExecutor.sol"; -import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { ExternalCallFailed, UnAuthorized } from "../Errors/GenericErrors.sol"; import { ITokenMessaging } from "../Interfaces/IStargate.sol"; @@ -35,10 +35,10 @@ interface ILayerZeroComposer { /// @title ReceiverStargateV2 /// @author LI.FI (https://li.fi) /// @notice Arbitrary execution contract used for cross-chain swaps and message passing via Stargate V2 -/// @custom:version 1.0.1 +/// @custom:version 1.1.0 contract ReceiverStargateV2 is ILiFi, - TransferrableOwnership, + WithdrawablePeriphery, ILayerZeroComposer { using SafeERC20 for IERC20; @@ -64,8 +64,7 @@ contract ReceiverStargateV2 is address _tokenMessaging, address _endpointV2, uint256 _recoverGas - ) TransferrableOwnership(_owner) { - owner = _owner; + ) WithdrawablePeriphery(_owner) { executor = IExecutor(_executor); tokenMessaging = ITokenMessaging(_tokenMessaging); endpointV2 = _endpointV2; @@ -115,23 +114,6 @@ contract ReceiverStargateV2 is ); } - /// @notice Send remaining token to receiver - /// @param assetId address of the token to be withdrawn (not to be confused with StargateV2's assetIds which are uint16 values) - /// @param receiver address that will receive tokens in the end - /// @param amount amount of token - function pullToken( - address assetId, - address payable receiver, - uint256 amount - ) external onlyOwner { - if (LibAsset.isNativeAsset(assetId)) { - // solhint-disable-next-line avoid-low-level-calls - SafeTransferLib.safeTransferETH(receiver, amount); - } else { - IERC20(assetId).safeTransfer(receiver, amount); - } - } - /// Private Methods /// /// @notice Performs a swap before completing a cross-chain transaction diff --git a/src/Periphery/RelayerCelerIM.sol b/src/Periphery/RelayerCelerIM.sol index 820051d55..e7dfbc05e 100644 --- a/src/Periphery/RelayerCelerIM.sol +++ b/src/Periphery/RelayerCelerIM.sol @@ -10,7 +10,7 @@ import { LibUtil } from "../Libraries/LibUtil.sol"; import { ILiFi } from "../Interfaces/ILiFi.sol"; import { PeripheryRegistryFacet } from "../Facets/PeripheryRegistryFacet.sol"; import { IExecutor } from "../Interfaces/IExecutor.sol"; -import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { IMessageReceiverApp } from "celer-network/contracts/message/interfaces/IMessageReceiverApp.sol"; import { CelerIM } from "lifi/Helpers/CelerIMFacetBase.sol"; import { MessageSenderLib, MsgDataTypes, IMessageBus, IOriginalTokenVault, IPeggedTokenBridge, IOriginalTokenVaultV2, IPeggedTokenBridgeV2 } from "celer-network/contracts/message/libraries/MessageSenderLib.sol"; @@ -19,8 +19,8 @@ import { IBridge as ICBridge } from "celer-network/contracts/interfaces/IBridge. /// @title RelayerCelerIM /// @author LI.FI (https://li.fi) /// @notice Relayer contract for CelerIM that forwards calls and handles refunds on src side and acts receiver on dest -/// @custom:version 2.0.1 -contract RelayerCelerIM is ILiFi, TransferrableOwnership { +/// @custom:version 2.1.1 +contract RelayerCelerIM is ILiFi, WithdrawablePeriphery { using SafeERC20 for IERC20; /// Storage /// @@ -28,14 +28,6 @@ contract RelayerCelerIM is ILiFi, TransferrableOwnership { IMessageBus public cBridgeMessageBus; address public diamondAddress; - /// Events /// - - event LogWithdraw( - address indexed _assetAddress, - address indexed _to, - uint256 amount - ); - /// Modifiers /// modifier onlyCBridgeMessageBus() { @@ -53,8 +45,7 @@ contract RelayerCelerIM is ILiFi, TransferrableOwnership { address _cBridgeMessageBusAddress, address _owner, address _diamondAddress - ) TransferrableOwnership(_owner) { - owner = _owner; + ) WithdrawablePeriphery(_owner) { cBridgeMessageBus = IMessageBus(_cBridgeMessageBusAddress); diamondAddress = _diamondAddress; } @@ -402,23 +393,6 @@ contract RelayerCelerIM is ILiFi, TransferrableOwnership { } } - /// @notice Sends remaining token to given receiver address (for refund cases) - /// @param assetId Address of the token to be withdrawn - /// @param receiver Address that will receive tokens - /// @param amount Amount of tokens to be withdrawn - function withdraw( - address assetId, - address payable receiver, - uint256 amount - ) external onlyOwner { - if (LibAsset.isNativeAsset(assetId)) { - SafeTransferLib.safeTransferETH(receiver, amount); - } else { - IERC20(assetId).safeTransfer(receiver, amount); - } - emit LogWithdraw(assetId, receiver, amount); - } - /// @notice Triggers a cBridge refund with calldata produced by cBridge API /// @param _callTo The address to execute the calldata on /// @param _callData The data to execute @@ -451,9 +425,11 @@ contract RelayerCelerIM is ILiFi, TransferrableOwnership { // forward funds to _to address and emit event, if cBridge refund successful if (success) { - address sendTo = (LibUtil.isZeroAddress(_to)) ? msg.sender : _to; - LibAsset.transferAsset(_assetAddress, payable(sendTo), _amount); - emit LogWithdraw(_assetAddress, sendTo, _amount); + address payable sendTo = payable( + (LibUtil.isZeroAddress(_to)) ? msg.sender : _to + ); + LibAsset.transferAsset(_assetAddress, sendTo, _amount); + emit TokensWithdrawn(_assetAddress, sendTo, _amount); } else { revert WithdrawFailed(); } diff --git a/src/Periphery/TokenWrapper.sol b/src/Periphery/TokenWrapper.sol index ac72718dd..ffdd15c51 100644 --- a/src/Periphery/TokenWrapper.sol +++ b/src/Periphery/TokenWrapper.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.17; import { LibAsset } from "../Libraries/LibAsset.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { WithdrawablePeriphery } from "../Helpers/WithdrawablePeriphery.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; /// External wrapper interface @@ -15,8 +16,8 @@ interface IWrapper { /// @title TokenWrapper /// @author LI.FI (https://li.fi) /// @notice Provides functionality for wrapping and unwrapping tokens -/// @custom:version 1.0.1 -contract TokenWrapper { +/// @custom:version 1.1.0 +contract TokenWrapper is WithdrawablePeriphery { uint256 private constant MAX_INT = 2 ** 256 - 1; address public wrappedToken; @@ -25,7 +26,10 @@ contract TokenWrapper { /// Constructor /// // solhint-disable-next-line no-empty-blocks - constructor(address _wrappedToken) { + constructor( + address _wrappedToken, + address _owner + ) WithdrawablePeriphery(_owner) { wrappedToken = _wrappedToken; IERC20(wrappedToken).approve(address(this), MAX_INT); } diff --git a/test/solidity/Facets/CelerIMFacet.t.sol b/test/solidity/Facets/CelerIMFacet.t.sol index e164970c4..85ecf72b1 100644 --- a/test/solidity/Facets/CelerIMFacet.t.sol +++ b/test/solidity/Facets/CelerIMFacet.t.sol @@ -101,7 +101,7 @@ contract CelerIMFacetTest is TestBaseFacet { // deploy periphery erc20Proxy = new ERC20Proxy(address(this)); - executor = new Executor(address(erc20Proxy)); + executor = new Executor(address(erc20Proxy), address(this)); celerIMFacet = new TestCelerIMFacet( IMessageBus(CBRIDGE_MESSAGEBUS_ETH), diff --git a/test/solidity/Periphery/Executor.t.sol b/test/solidity/Periphery/Executor.t.sol index 7dc1e51f1..a1c8652c6 100644 --- a/test/solidity/Periphery/Executor.t.sol +++ b/test/solidity/Periphery/Executor.t.sol @@ -70,7 +70,7 @@ contract ExecutorTest is DSTest { function setUp() public { gw = new MockGateway(); erc20Proxy = new ERC20Proxy(address(this)); - executor = new Executor(address(erc20Proxy)); + executor = new Executor(address(erc20Proxy), address(this)); vm.makePersistent(address(executor)); erc20Proxy.setAuthorizedCaller(address(executor), true); amm = new TestAMM(); diff --git a/test/solidity/Periphery/Receiver.t.sol b/test/solidity/Periphery/Receiver.t.sol index a77d24441..19b343d40 100644 --- a/test/solidity/Periphery/Receiver.t.sol +++ b/test/solidity/Periphery/Receiver.t.sol @@ -44,7 +44,7 @@ contract ReceiverTest is TestBase { ); erc20Proxy = new ERC20Proxy(address(this)); - executor = new Executor(address(erc20Proxy)); + executor = new Executor(address(erc20Proxy), address(this)); receiver = new Receiver( address(this), stargateRouter, @@ -61,7 +61,7 @@ contract ReceiverTest is TestBase { transferId = keccak256("123"); } - function test_revert_OwnerCanPullToken() public { + function test_revert_OwnerCanWithdrawToken() public { // send token to receiver vm.startPrank(USER_SENDER); dai.transfer(address(receiver), 1000); @@ -70,15 +70,15 @@ contract ReceiverTest is TestBase { // pull token vm.startPrank(USER_DIAMOND_OWNER); - receiver.pullToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); + receiver.withdrawToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); assertEq(1000, dai.balanceOf(USER_RECEIVER)); } - function test_revert_PullTokenNonOwner() public { + function test_revert_WithdrawTokenNonOwner() public { vm.startPrank(USER_SENDER); vm.expectRevert(UnAuthorized.selector); - receiver.pullToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); + receiver.withdrawToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); } // AMAROK-RELATED TESTS diff --git a/test/solidity/Periphery/ReceiverAcrossV3.t.sol b/test/solidity/Periphery/ReceiverAcrossV3.t.sol index 97d67f284..32f29428c 100644 --- a/test/solidity/Periphery/ReceiverAcrossV3.t.sol +++ b/test/solidity/Periphery/ReceiverAcrossV3.t.sol @@ -30,7 +30,7 @@ contract ReceiverAcrossV3Test is TestBase { initTestBase(); erc20Proxy = new ERC20Proxy(address(this)); - executor = new Executor(address(erc20Proxy)); + executor = new Executor(address(erc20Proxy), address(this)); receiver = new ReceiverAcrossV3( address(this), address(executor), @@ -61,7 +61,7 @@ contract ReceiverAcrossV3Test is TestBase { // pull token vm.startPrank(USER_DIAMOND_OWNER); - receiver.pullToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); + receiver.withdrawToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); assertEq(dai.balanceOf(USER_RECEIVER), initialBalance + 1000); } @@ -75,12 +75,12 @@ contract ReceiverAcrossV3Test is TestBase { // pull token vm.startPrank(USER_DIAMOND_OWNER); - receiver.pullToken(address(0), payable(USER_RECEIVER), 1 ether); + receiver.withdrawToken(address(0), payable(USER_RECEIVER), 1 ether); assertEq(USER_RECEIVER.balance, initialBalance + 1 ether); } - function test_PullTokenWillRevertIfExternalCallFails() public { + function test_WithdrawTokenWillRevertIfExternalCallFails() public { vm.deal(address(receiver), 1 ether); // deploy contract that cannot receive ETH @@ -88,19 +88,19 @@ contract ReceiverAcrossV3Test is TestBase { vm.startPrank(USER_DIAMOND_OWNER); - vm.expectRevert(abi.encodeWithSignature("ETHTransferFailed()")); + vm.expectRevert(abi.encodeWithSignature("ExternalCallFailed()")); - receiver.pullToken( + receiver.withdrawToken( address(0), payable(address(nonETHReceiver)), 1 ether ); } - function test_revert_PullTokenNonOwner() public { + function test_revert_WithdrawTokenNonOwner() public { vm.startPrank(USER_SENDER); vm.expectRevert(UnAuthorized.selector); - receiver.pullToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); + receiver.withdrawToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); } function test_revert_OnlySpokepoolCanCallHandleV3AcrossMessage() public { diff --git a/test/solidity/Periphery/ReceiverStargateV2.t.sol b/test/solidity/Periphery/ReceiverStargateV2.t.sol index 485bd5ace..a6a054caa 100644 --- a/test/solidity/Periphery/ReceiverStargateV2.t.sol +++ b/test/solidity/Periphery/ReceiverStargateV2.t.sol @@ -44,7 +44,7 @@ contract ReceiverStargateV2Test is TestBase { initTestBase(); erc20Proxy = new ERC20Proxy(address(this)); - executor = new Executor(address(erc20Proxy)); + executor = new Executor(address(erc20Proxy), address(this)); receiver = new ReceiverStargateV2( address(this), address(executor), @@ -59,7 +59,7 @@ contract ReceiverStargateV2Test is TestBase { transferId = keccak256("123"); } - function test_OwnerCanPullERC20Token() public { + function test_OwnerCanWithdrawERC20Token() public { // fund receiver with ERC20 tokens deal(ADDRESS_DAI, address(receiver), 1000); @@ -68,12 +68,12 @@ contract ReceiverStargateV2Test is TestBase { // pull token vm.startPrank(USER_DIAMOND_OWNER); - receiver.pullToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); + receiver.withdrawToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); assertEq(dai.balanceOf(USER_RECEIVER), initialBalance + 1000); } - function test_OwnerCanPullNativeToken() public { + function test_OwnerCanWithdrawNativeToken() public { // fund receiver with native tokens vm.deal(address(receiver), 1 ether); @@ -82,12 +82,12 @@ contract ReceiverStargateV2Test is TestBase { // pull token vm.startPrank(USER_DIAMOND_OWNER); - receiver.pullToken(address(0), payable(USER_RECEIVER), 1 ether); + receiver.withdrawToken(address(0), payable(USER_RECEIVER), 1 ether); assertEq(USER_RECEIVER.balance, initialBalance + 1 ether); } - function test_PullTokenWillRevertIfExternalCallFails() public { + function test_WithdrawTokenWillRevertIfExternalCallFails() public { vm.deal(address(receiver), 1 ether); // deploy contract that cannot receive ETH @@ -95,19 +95,19 @@ contract ReceiverStargateV2Test is TestBase { vm.startPrank(USER_DIAMOND_OWNER); - vm.expectRevert(abi.encodeWithSignature("ETHTransferFailed()")); + vm.expectRevert(abi.encodeWithSignature("ExternalCallFailed()")); - receiver.pullToken( + receiver.withdrawToken( address(0), payable(address(nonETHReceiver)), 1 ether ); } - function test_revert_PullTokenNonOwner() public { + function test_revert_WithdrawTokenNonOwner() public { vm.startPrank(USER_SENDER); vm.expectRevert(UnAuthorized.selector); - receiver.pullToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); + receiver.withdrawToken(ADDRESS_DAI, payable(USER_RECEIVER), 1000); } function _getValidLzComposeCalldata( diff --git a/test/solidity/Periphery/RelayerCelerIM.t.sol b/test/solidity/Periphery/RelayerCelerIM.t.sol index 63197ea79..89f98445b 100644 --- a/test/solidity/Periphery/RelayerCelerIM.t.sol +++ b/test/solidity/Periphery/RelayerCelerIM.t.sol @@ -44,7 +44,7 @@ contract RelayerCelerIMTest is TestBase { // deploy CelerIM Receiver erc20Proxy = new ERC20Proxy(address(this)); - executor = new Executor(address(erc20Proxy)); + executor = new Executor(address(erc20Proxy), address(this)); celerIMFacet = new CelerIMFacetMutable( IMessageBus(CBRIDGE_MESSAGEBUS_ETH), REFUND_WALLET, diff --git a/test/solidity/Periphery/TokenWrapper.t.sol b/test/solidity/Periphery/TokenWrapper.t.sol index 2c2e6850f..71df2ada2 100644 --- a/test/solidity/Periphery/TokenWrapper.t.sol +++ b/test/solidity/Periphery/TokenWrapper.t.sol @@ -6,7 +6,7 @@ import { console } from "../utils/Console.sol"; import { Vm } from "forge-std/Vm.sol"; import { TokenWrapper } from "lifi/Periphery/TokenWrapper.sol"; import { TestWrappedToken as ERC20 } from "../utils/TestWrappedToken.sol"; -import { IERC20 } from "lifi/Libraries/LibAsset.sol"; +import { IERC20, LibAsset } from "lifi/Libraries/LibAsset.sol"; contract TokenWrapperTest is DSTest { Vm internal immutable vm = Vm(HEVM_ADDRESS); @@ -15,7 +15,7 @@ contract TokenWrapperTest is DSTest { function setUp() public { wrappedToken = new ERC20("TestWrappedToken", "WTST", 18); - tokenWrapper = new TokenWrapper(address(wrappedToken)); + tokenWrapper = new TokenWrapper(address(wrappedToken), address(this)); vm.deal(address(this), 100 ether); } @@ -28,6 +28,20 @@ contract TokenWrapperTest is DSTest { assert(wrappedToken.balanceOf(address(this)) == 1 ether); } + function testCanWithdrawToken() public { + // Send some ETH to the contract + (bool success, ) = address(tokenWrapper).call{ value: 1 ether }(""); + require(success, "Failed to send ETH"); + + uint256 initialBalance = address(this).balance; + tokenWrapper.withdrawToken( + address(0), + payable(address(this)), + 1 ether + ); + assertEq(address(this).balance - initialBalance, 1 ether); + } + function testCanWithdraw() public { uint256 initialBalance = address(this).balance; vm.deal(address(wrappedToken), 100 ether);