From 770c52e261e20c3d58bd2038f8208669f8f38912 Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:43:54 -0400 Subject: [PATCH 1/6] feat: warp route wrapper contract --- src/helper/WarpRouteWrapper.sol | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/helper/WarpRouteWrapper.sol diff --git a/src/helper/WarpRouteWrapper.sol b/src/helper/WarpRouteWrapper.sol new file mode 100644 index 0000000..d355f7f --- /dev/null +++ b/src/helper/WarpRouteWrapper.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.21; + +import { ERC20 } from "@solmate/tokens/ERC20.sol"; +import { SafeTransferLib } from "@solmate/utils/SafeTransferLib.sol"; +import { BoringVault } from "../base/BoringVault.sol"; +import { TellerWithMultiAssetSupport } from "../base/Roles/TellerWithMultiAssetSupport.sol"; + +interface WarpRoute { + function transferRemote(uint32 _destination, bytes32 _recipient, uint256 _amountOrId) external returns (bytes32); +} + +/** + * @notice A simple wrapper to call both `deposit` on a Teller and + * `transferRemote` on a WarpRoute in one transaction. This contract can only be + * used with a defined Teller. If a new Teller is deployed, a new Wrapper must + * be deployed. + */ +contract WarpRouteWrapper { + using SafeTransferLib for ERC20; + + BoringVault public boringVault; + TellerWithMultiAssetSupport public teller; + WarpRoute public warpRoute; + + constructor(TellerWithMultiAssetSupport _teller, WarpRoute _warpRoute) { + teller = _teller; + warpRoute = _warpRoute; + boringVault = _teller.vault(); + + // Infinite approvals to the warpRoute okay because this contract will + // never hold any balance aside from donations. + boringVault.approve(address(warpRoute), type(uint256).max); + } + + /** + * @dev There's two sets of approvals this contract needs to grant. It needs + * to approve the BoringVault to take its `depositAsset`, and it needs to + * approve the WarpRoute to take the BoringVault shares. The latter is done + * in the constructor. + * + * NOTE that the `depositAsset` can vary as the Teller can add new supported + * assets. + */ + function depositAndBridge( + ERC20 depositAsset, + uint256 depositAmount, + uint256 minimumMint, + uint32 _destination, + bytes32 _recipient + ) + external + returns (uint256 sharesMinted, bytes32 messageId) + { + depositAsset.safeTransferFrom(msg.sender, address(this), depositAmount); + + if (depositAsset.allowance(address(this), address(boringVault)) < depositAmount) { + depositAsset.approve(address(boringVault), type(uint256).max); + } + + sharesMinted = teller.deposit(depositAsset, depositAmount, minimumMint); + + messageId = warpRoute.transferRemote(_destination, _recipient, sharesMinted); + } +} From 30390af84d5d0cc05f249a339555c1a49048ad1d Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:56:12 -0400 Subject: [PATCH 2/6] feat: constrain hyperlane destination id --- src/helper/WarpRouteWrapper.sol | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/helper/WarpRouteWrapper.sol b/src/helper/WarpRouteWrapper.sol index d355f7f..d30da86 100644 --- a/src/helper/WarpRouteWrapper.sol +++ b/src/helper/WarpRouteWrapper.sol @@ -19,13 +19,18 @@ interface WarpRoute { contract WarpRouteWrapper { using SafeTransferLib for ERC20; + error InvalidDestination(); + BoringVault public boringVault; TellerWithMultiAssetSupport public teller; WarpRoute public warpRoute; + uint32 public destination; - constructor(TellerWithMultiAssetSupport _teller, WarpRoute _warpRoute) { + constructor(TellerWithMultiAssetSupport _teller, WarpRoute _warpRoute, uint32 _destination) { teller = _teller; warpRoute = _warpRoute; + destination = _destination; + boringVault = _teller.vault(); // Infinite approvals to the warpRoute okay because this contract will @@ -52,6 +57,8 @@ contract WarpRouteWrapper { external returns (uint256 sharesMinted, bytes32 messageId) { + if (_destination != destination) revert InvalidDestination(); + depositAsset.safeTransferFrom(msg.sender, address(this), depositAmount); if (depositAsset.allowance(address(this), address(boringVault)) < depositAmount) { From 2d77aefefa2cea780a1d2f4b79d3b11b5e155d92 Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Wed, 23 Oct 2024 20:53:26 -0400 Subject: [PATCH 3/6] fix: immutables and don't pass in destination --- src/helper/WarpRouteWrapper.sol | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/helper/WarpRouteWrapper.sol b/src/helper/WarpRouteWrapper.sol index d30da86..29642cf 100644 --- a/src/helper/WarpRouteWrapper.sol +++ b/src/helper/WarpRouteWrapper.sol @@ -21,10 +21,10 @@ contract WarpRouteWrapper { error InvalidDestination(); - BoringVault public boringVault; - TellerWithMultiAssetSupport public teller; - WarpRoute public warpRoute; - uint32 public destination; + BoringVault public immutable boringVault; + TellerWithMultiAssetSupport public immutable teller; + WarpRoute public immutable warpRoute; + uint32 public immutable destination; constructor(TellerWithMultiAssetSupport _teller, WarpRoute _warpRoute, uint32 _destination) { teller = _teller; @@ -51,14 +51,11 @@ contract WarpRouteWrapper { ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint, - uint32 _destination, bytes32 _recipient ) external returns (uint256 sharesMinted, bytes32 messageId) { - if (_destination != destination) revert InvalidDestination(); - depositAsset.safeTransferFrom(msg.sender, address(this), depositAmount); if (depositAsset.allowance(address(this), address(boringVault)) < depositAmount) { @@ -67,6 +64,6 @@ contract WarpRouteWrapper { sharesMinted = teller.deposit(depositAsset, depositAmount, minimumMint); - messageId = warpRoute.transferRemote(_destination, _recipient, sharesMinted); + messageId = warpRoute.transferRemote(destination, _recipient, sharesMinted); } } From ad84295a806703da0a3b39ea8babdbe16a3767fc Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Wed, 23 Oct 2024 20:58:49 -0400 Subject: [PATCH 4/6] chore: security tag --- src/helper/WarpRouteWrapper.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helper/WarpRouteWrapper.sol b/src/helper/WarpRouteWrapper.sol index 29642cf..da42700 100644 --- a/src/helper/WarpRouteWrapper.sol +++ b/src/helper/WarpRouteWrapper.sol @@ -15,6 +15,8 @@ interface WarpRoute { * `transferRemote` on a WarpRoute in one transaction. This contract can only be * used with a defined Teller. If a new Teller is deployed, a new Wrapper must * be deployed. + * + * @custom:security-contact security@molecularlabs.io */ contract WarpRouteWrapper { using SafeTransferLib for ERC20; From 062ef808e4648cd44fd1e3337cf6e1380e15c037 Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Wed, 23 Oct 2024 20:53:26 -0400 Subject: [PATCH 5/6] fix: immutables, no need to pass in destination, styling, security tag --- src/helper/WarpRouteWrapper.sol | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/helper/WarpRouteWrapper.sol b/src/helper/WarpRouteWrapper.sol index d30da86..7d944c5 100644 --- a/src/helper/WarpRouteWrapper.sol +++ b/src/helper/WarpRouteWrapper.sol @@ -15,16 +15,18 @@ interface WarpRoute { * `transferRemote` on a WarpRoute in one transaction. This contract can only be * used with a defined Teller. If a new Teller is deployed, a new Wrapper must * be deployed. + * + * @custom:security-contact security@molecularlabs.io */ contract WarpRouteWrapper { using SafeTransferLib for ERC20; error InvalidDestination(); - BoringVault public boringVault; - TellerWithMultiAssetSupport public teller; - WarpRoute public warpRoute; - uint32 public destination; + BoringVault public immutable boringVault; + TellerWithMultiAssetSupport public immutable teller; + WarpRoute public immutable warpRoute; + uint32 public immutable destination; constructor(TellerWithMultiAssetSupport _teller, WarpRoute _warpRoute, uint32 _destination) { teller = _teller; @@ -51,14 +53,11 @@ contract WarpRouteWrapper { ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint, - uint32 _destination, - bytes32 _recipient + bytes32 recipient ) external returns (uint256 sharesMinted, bytes32 messageId) { - if (_destination != destination) revert InvalidDestination(); - depositAsset.safeTransferFrom(msg.sender, address(this), depositAmount); if (depositAsset.allowance(address(this), address(boringVault)) < depositAmount) { @@ -67,6 +66,6 @@ contract WarpRouteWrapper { sharesMinted = teller.deposit(depositAsset, depositAmount, minimumMint); - messageId = warpRoute.transferRemote(_destination, _recipient, sharesMinted); + messageId = warpRoute.transferRemote(destination, recipient, sharesMinted); } } From 6e4fc6c8d6f9b1889f4f38d38643dd36c8386fc9 Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:19:44 -0400 Subject: [PATCH 6/6] fix: transferRemote requires msg.value gas payments --- src/helper/WarpRouteWrapper.sol | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/helper/WarpRouteWrapper.sol b/src/helper/WarpRouteWrapper.sol index 7d944c5..b6bfb40 100644 --- a/src/helper/WarpRouteWrapper.sol +++ b/src/helper/WarpRouteWrapper.sol @@ -7,7 +7,14 @@ import { BoringVault } from "../base/BoringVault.sol"; import { TellerWithMultiAssetSupport } from "../base/Roles/TellerWithMultiAssetSupport.sol"; interface WarpRoute { - function transferRemote(uint32 _destination, bytes32 _recipient, uint256 _amountOrId) external returns (bytes32); + function transferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amountOrId + ) + external + payable + returns (bytes32); } /** @@ -56,6 +63,7 @@ contract WarpRouteWrapper { bytes32 recipient ) external + payable returns (uint256 sharesMinted, bytes32 messageId) { depositAsset.safeTransferFrom(msg.sender, address(this), depositAmount); @@ -66,6 +74,6 @@ contract WarpRouteWrapper { sharesMinted = teller.deposit(depositAsset, depositAmount, minimumMint); - messageId = warpRoute.transferRemote(destination, recipient, sharesMinted); + messageId = warpRoute.transferRemote{ value: msg.value }(destination, recipient, sharesMinted); } }