Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review merlin 3 #59

Merged
merged 3 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions contracts/BaseCallbackReceiver.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.21;

import {Errors} from "./libraries/Errors.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";

/// @title BaseCallbackReceiver
/// @notice Provides utility functions to identify the initiator of callbacks (which cannot be identified using `msg.sender` or `tx.origin`).
Expand All @@ -24,6 +24,6 @@ abstract contract BaseCallbackReceiver {
/* INTERNAL */

function _checkInitiated() internal view {
require(_initiator != address(0), Errors.UNINITIATED);
require(_initiator != address(0), ErrorsLib.UNINITIATED);
}
}
4 changes: 4 additions & 0 deletions contracts/BaseSelfMulticall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
pragma solidity 0.8.21;

/// @title BaseSelfMulticall
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Enables calling multiple functions in a single call to the same contract (self).
/// @dev Based on Uniswap work: https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/Multicall.sol
abstract contract BaseSelfMulticall {
/* INTERNAL */

/// @notice Executes a series of delegate calls to the contract itself.
function _multicall(bytes[] memory data) internal returns (bytes[] memory results) {
results = new bytes[](data.length);

Expand Down
10 changes: 7 additions & 3 deletions contracts/bundlers/BaseBundler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,34 @@ pragma solidity 0.8.21;

import {IMulticall} from "./interfaces/IMulticall.sol";

import {Errors} from "./libraries/Errors.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";

import {BaseSelfMulticall} from "../BaseSelfMulticall.sol";
import {BaseCallbackReceiver} from "../BaseCallbackReceiver.sol";

/// @title BaseBundler
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Enables calling multiple functions in a single call to the same contract (self) as well as calling other Bundler contracts.
/// @dev Every Bundler must inherit from this contract.
abstract contract BaseBundler is BaseSelfMulticall, BaseCallbackReceiver {
/* EXTERNAL */

/// @notice Executes a series of calls in a single transaction to self.
function multicall(uint256 deadline, bytes[] calldata data)
external
payable
lockInitiator
returns (bytes[] memory)
{
require(block.timestamp <= deadline, Errors.DEADLINE_EXPIRED);
require(block.timestamp <= deadline, ErrorsLib.DEADLINE_EXPIRED);

return _multicall(data);
}

/// @notice Executes multiple actions on another `bundler` contract passing along the required `data`.
function callBundler(address bundler, bytes[] calldata data) external {
require(bundler != address(0), Errors.ZERO_ADDRESS);
require(bundler != address(0), ErrorsLib.ZERO_ADDRESS);

IMulticall(bundler).multicall(block.timestamp, data);
}
Expand Down
14 changes: 8 additions & 6 deletions contracts/bundlers/ERC20Bundler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity 0.8.21;

import {Signature} from "@morpho-blue/interfaces/IMorpho.sol";

import {Errors} from "./libraries/Errors.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {Math} from "@morpho-utils/math/Math.sol";
import {SafeTransferLib, ERC20} from "@solmate/utils/SafeTransferLib.sol";
import {ERC20 as ERC20Permit2, Permit2Lib} from "@permit2/libraries/Permit2Lib.sol";
Expand All @@ -13,6 +13,8 @@ import {BaseBundler} from "./BaseBundler.sol";
/// @title ERC20Bundler
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Bundler contract managing interactions with ERC20 compliant tokens.
/// @dev It leverages Uniswap's Permit2 contract.
abstract contract ERC20Bundler is BaseBundler {
using SafeTransferLib for ERC20;
using Permit2Lib for ERC20Permit2;
Expand All @@ -21,19 +23,19 @@ abstract contract ERC20Bundler is BaseBundler {

/// @dev Transfers the minimum between the given `amount` and the bundler balance of `asset` from this contract to `recipient`.
function transfer(address asset, address recipient, uint256 amount) external {
require(recipient != address(0), Errors.ZERO_ADDRESS);
require(recipient != address(this), Errors.BUNDLER_ADDRESS);
require(recipient != address(0), ErrorsLib.ZERO_ADDRESS);
require(recipient != address(this), ErrorsLib.BUNDLER_ADDRESS);

amount = Math.min(amount, ERC20(asset).balanceOf(address(this)));

require(amount != 0, Errors.ZERO_AMOUNT);
require(amount != 0, ErrorsLib.ZERO_AMOUNT);

ERC20(asset).safeTransfer(recipient, amount);
}

/// @dev Approves the given `amount` of `asset` from sender to be spent by this contract via Permit2 with the given `deadline` & EIP712 `signature`.
function approve2(address asset, uint256 amount, uint256 deadline, Signature calldata signature) external {
require(amount != 0, Errors.ZERO_AMOUNT);
require(amount != 0, ErrorsLib.ZERO_AMOUNT);

ERC20Permit2(asset).simplePermit2(
_initiator, address(this), amount, deadline, signature.v, signature.r, signature.s
Expand All @@ -42,7 +44,7 @@ abstract contract ERC20Bundler is BaseBundler {

/// @dev Transfers the given `amount` of `asset` from sender to this contract via ERC20 transfer with Permit2 fallback.
function transferFrom2(address asset, uint256 amount) external {
require(amount != 0, Errors.ZERO_AMOUNT);
require(amount != 0, ErrorsLib.ZERO_AMOUNT);

ERC20Permit2(asset).transferFrom2(_initiator, address(this), amount);
}
Expand Down
19 changes: 10 additions & 9 deletions contracts/bundlers/ERC4626Bundler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity 0.8.21;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";

import {Errors} from "./libraries/Errors.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {Math} from "@morpho-utils/math/Math.sol";
import {SafeTransferLib, ERC20} from "@solmate/utils/SafeTransferLib.sol";

Expand All @@ -12,58 +12,59 @@ import {BaseBundler} from "./BaseBundler.sol";
/// @title ERC4626Bundler.
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Bundler contract managing interactions with ERC4626 compliant tokens.
abstract contract ERC4626Bundler is BaseBundler {
using SafeTransferLib for ERC20;

/* ACTIONS */

function mint(address vault, uint256 shares, address receiver) external {
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);

address asset = IERC4626(vault).asset();
uint256 amount = Math.min(IERC4626(vault).maxDeposit(receiver), ERC20(asset).balanceOf(address(this)));

shares = Math.min(shares, IERC4626(vault).previewDeposit(amount));
amount = IERC4626(vault).previewMint(shares);

require(amount != 0, Errors.ZERO_AMOUNT);
require(amount != 0, ErrorsLib.ZERO_AMOUNT);

ERC20(asset).safeApprove(vault, amount);
IERC4626(vault).mint(shares, receiver);
}

function deposit(address vault, uint256 amount, address receiver) external {
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);

address asset = IERC4626(vault).asset();

amount = Math.min(amount, IERC4626(vault).maxDeposit(receiver));
amount = Math.min(amount, ERC20(asset).balanceOf(address(this)));

require(amount != 0, Errors.ZERO_AMOUNT);
require(amount != 0, ErrorsLib.ZERO_AMOUNT);

ERC20(asset).safeApprove(vault, amount);
IERC4626(vault).deposit(amount, receiver);
}

function withdraw(address vault, uint256 amount, address receiver) external {
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);

address initiator = _initiator;
amount = Math.min(amount, IERC4626(vault).maxWithdraw(initiator));

require(amount != 0, Errors.ZERO_AMOUNT);
require(amount != 0, ErrorsLib.ZERO_AMOUNT);

IERC4626(vault).withdraw(amount, receiver, initiator);
}

function redeem(address vault, uint256 shares, address receiver) external {
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);

address initiator = _initiator;
shares = Math.min(shares, IERC4626(vault).maxRedeem(initiator));

require(shares != 0, Errors.ZERO_SHARES);
require(shares != 0, ErrorsLib.ZERO_SHARES);

IERC4626(vault).redeem(shares, receiver, initiator);
}
Expand Down
15 changes: 9 additions & 6 deletions contracts/bundlers/MorphoBundler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.21;
import {IMorphoBundler} from "./interfaces/IMorphoBundler.sol";
import {Market, Signature, Authorization, IMorpho} from "@morpho-blue/interfaces/IMorpho.sol";

import {Errors} from "./libraries/Errors.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";

import {Math} from "@morpho-utils/math/Math.sol";
import {SafeTransferLib, ERC20} from "@solmate/utils/SafeTransferLib.sol";
Expand All @@ -14,17 +14,19 @@ import {BaseBundler} from "./BaseBundler.sol";
/// @title MorphoBundler
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Bundler contract managing interactions with Morpho.
abstract contract MorphoBundler is BaseBundler, IMorphoBundler {
using SafeTransferLib for ERC20;

/* IMMUTABLES */

/// @notice The Morpho contract address.
IMorpho public immutable MORPHO;

/* CONSTRUCTOR */

constructor(address morpho) {
require(morpho != address(0), Errors.ZERO_ADDRESS);
require(morpho != address(0), ErrorsLib.ZERO_ADDRESS);

MORPHO = IMorpho(morpho);
}
Expand Down Expand Up @@ -53,7 +55,7 @@ abstract contract MorphoBundler is BaseBundler, IMorphoBundler {

/* ACTIONS */

/// @dev Approves this contract to manage the initiator's position via EIP712 `signature`.
/// @dev Approves this contract to manage the `authorization.authorizer`'s position via EIP712 `signature`.
function morphoSetAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature)
external
{
Expand All @@ -66,7 +68,7 @@ abstract contract MorphoBundler is BaseBundler, IMorphoBundler {
function morphoSupply(Market calldata market, uint256 amount, uint256 shares, address onBehalf, bytes calldata data)
external
{
require(onBehalf != address(this), Errors.BUNDLER_ADDRESS);
require(onBehalf != address(this), ErrorsLib.BUNDLER_ADDRESS);

// Don't always cap the amount to the bundler's balance because the liquidity can be transferred inside the supply callback.
if (amount == type(uint256).max) amount = ERC20(market.borrowableToken).balanceOf(address(this));
Expand All @@ -81,7 +83,7 @@ abstract contract MorphoBundler is BaseBundler, IMorphoBundler {
function morphoSupplyCollateral(Market calldata market, uint256 amount, address onBehalf, bytes calldata data)
external
{
require(onBehalf != address(this), Errors.BUNDLER_ADDRESS);
require(onBehalf != address(this), ErrorsLib.BUNDLER_ADDRESS);

// Don't always cap the amount to the bundler's balance because the liquidity can be transferred inside the supply collateral callback.
if (amount == type(uint256).max) amount = ERC20(market.collateralToken).balanceOf(address(this));
Expand All @@ -101,7 +103,7 @@ abstract contract MorphoBundler is BaseBundler, IMorphoBundler {
function morphoRepay(Market calldata market, uint256 amount, uint256 shares, address onBehalf, bytes calldata data)
external
{
require(onBehalf != address(this), Errors.BUNDLER_ADDRESS);
require(onBehalf != address(this), ErrorsLib.BUNDLER_ADDRESS);

// Don't always cap the amount to the bundler's balance because the liquidity can be transferred inside the repay callback.
if (amount == type(uint256).max) amount = ERC20(market.borrowableToken).balanceOf(address(this));
Expand Down Expand Up @@ -137,6 +139,7 @@ abstract contract MorphoBundler is BaseBundler, IMorphoBundler {

/* INTERNAL */

/// @dev Triggers `_multicall` logic during a callback.
function _callback(bytes calldata data) internal {
_checkInitiated();
_multicall(abi.decode(data, (bytes[])));
Expand Down
4 changes: 2 additions & 2 deletions contracts/bundlers/OneInchBundler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity 0.8.21;

import {I1InchAggregationRouterV5} from "./interfaces/I1InchAggregationRouterV5.sol";

import {Errors} from "./libraries/Errors.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {SafeTransferLib, ERC20} from "@solmate/utils/SafeTransferLib.sol";

import {BaseBundler} from "./BaseBundler.sol";
Expand All @@ -18,7 +18,7 @@ abstract contract OneInchBundler is BaseBundler {
/* CONSTRUCTOR */

constructor(address router) {
require(router != address(0), Errors.ZERO_ADDRESS);
require(router != address(0), ErrorsLib.ZERO_ADDRESS);

ONE_INCH_ROUTER = I1InchAggregationRouterV5(router);
}
Expand Down
21 changes: 11 additions & 10 deletions contracts/bundlers/WNativeBundler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity 0.8.21;

import {IWNative} from "./interfaces/IWNative.sol";

import {Errors} from "./libraries/Errors.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {Math} from "@morpho-utils/math/Math.sol";
import {SafeTransferLib, ERC20} from "@solmate/utils/SafeTransferLib.sol";

Expand All @@ -12,6 +12,7 @@ import {BaseBundler} from "./BaseBundler.sol";
/// @title WNativeBundler.
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Bundler contract managing interactions with network's wrapped native token.
abstract contract WNativeBundler is BaseBundler {
using SafeTransferLib for ERC20;

Expand All @@ -23,7 +24,7 @@ abstract contract WNativeBundler is BaseBundler {
/* CONSTRUCTOR */

constructor(address wNative) {
require(wNative != address(0), Errors.ZERO_ADDRESS);
require(wNative != address(0), ErrorsLib.ZERO_ADDRESS);

WRAPPED_NATIVE = wNative;
}
Expand All @@ -32,32 +33,32 @@ abstract contract WNativeBundler is BaseBundler {

/// @dev Only the wNative contract is allowed to transfer the native token to this contract, without any calldata.
receive() external payable {
require(msg.sender == WRAPPED_NATIVE, Errors.ONLY_WNATIVE);
require(msg.sender == WRAPPED_NATIVE, ErrorsLib.ONLY_WNATIVE);
}

/* ACTIONS */

/// @dev Wraps the given `amount` of the native token to wNative and sends it to `receiver`.
/// @dev Wraps the given `amount` of the native token to wNative and transfers it to `receiver`.
function wrapNative(uint256 amount, address receiver) external {
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);

amount = Math.min(amount, address(this).balance);

require(amount != 0, Errors.ZERO_AMOUNT);
require(amount != 0, ErrorsLib.ZERO_AMOUNT);

IWNative(WRAPPED_NATIVE).deposit{value: amount}();

if (receiver != address(this)) ERC20(WRAPPED_NATIVE).safeTransfer(receiver, amount);
}

/// @dev Unwraps the given `amount` of wNative to the native token and sends it to `receiver`.
/// @dev Unwraps the given `amount` of wNative to the native token and transfers it to `receiver`.
function unwrapNative(uint256 amount, address receiver) external {
require(receiver != address(this), Errors.BUNDLER_ADDRESS);
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(receiver != address(this), ErrorsLib.BUNDLER_ADDRESS);
require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);

amount = Math.min(amount, ERC20(WRAPPED_NATIVE).balanceOf(address(this)));

require(amount != 0, Errors.ZERO_AMOUNT);
require(amount != 0, ErrorsLib.ZERO_AMOUNT);

IWNative(WRAPPED_NATIVE).withdraw(amount);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {StEthBundler} from "./StEthBundler.sol";
contract EthereumWrapperBundler is ERC4626Bundler, WNativeBundler, StEthBundler {
/* CONSTANTS */

/// @dev The address of the WETH contract on Ethreum mainnet.
address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

/* CONSTRUCTOR */
Expand Down
Loading
Loading