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

ERC4626 contracts #40

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions core/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ deployments/
export.json
export/
typechain/
contracts/lib/
1 change: 1 addition & 0 deletions core/.solhintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules/
contracts/lib/
66 changes: 66 additions & 0 deletions core/contracts/lib/ERC4626RouterBase.sol
nkuba marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-or-later

// Copied from https://github.com/ERC4626-Alliance/ERC4626-Contracts based on
// the project commit 643cd04 from Apr 20, 2022

pragma solidity 0.8.10;

import {IERC4626, IERC4626RouterBase, ERC20} from "./interfaces/IERC4626RouterBase.sol";
import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol";

import {SelfPermit} from "./external/SelfPermit.sol";
import {Multicall} from "./external/Multicall.sol";
import {PeripheryPayments, IWETH9} from "./external/PeripheryPayments.sol";

/// @title ERC4626 Router Base Contract
abstract contract ERC4626RouterBase is IERC4626RouterBase, SelfPermit, Multicall, PeripheryPayments {
using SafeTransferLib for ERC20;

/// @inheritdoc IERC4626RouterBase
function mint(
IERC4626 vault,
address to,
uint256 shares,
uint256 maxAmountIn
) public payable virtual override returns (uint256 amountIn) {
if ((amountIn = vault.mint(shares, to)) > maxAmountIn) {
revert MaxAmountError();
}
}

/// @inheritdoc IERC4626RouterBase
function deposit(
IERC4626 vault,
address to,
uint256 amount,
uint256 minSharesOut
) public payable virtual override returns (uint256 sharesOut) {
if ((sharesOut = vault.deposit(amount, to)) < minSharesOut) {
revert MinSharesError();
}
}

/// @inheritdoc IERC4626RouterBase
function withdraw(
IERC4626 vault,
address to,
uint256 amount,
uint256 maxSharesOut
) public payable virtual override returns (uint256 sharesOut) {
if ((sharesOut = vault.withdraw(amount, to, msg.sender)) > maxSharesOut) {
revert MaxSharesError();
}
}

/// @inheritdoc IERC4626RouterBase
function redeem(
IERC4626 vault,
address to,
uint256 shares,
uint256 minAmountOut
) public payable virtual override returns (uint256 amountOut) {
if ((amountOut = vault.redeem(shares, to, msg.sender)) < minAmountOut) {
revert MinAmountError();
}
}
}
33 changes: 33 additions & 0 deletions core/contracts/lib/external/Multicall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/Multicall.sol

// SPDX-License-Identifier: GPL-2.0-or-later

// Copied from https://github.com/ERC4626-Alliance/ERC4626-Contracts based on
// the project commit 643cd04 from Apr 20, 2022

pragma solidity >=0.7.6;

import './interfaces/IMulticall.sol';

/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall is IMulticall {
/// @inheritdoc IMulticall
function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);

if (!success) {
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (result.length < 68) revert();
assembly {
result := add(result, 0x04)
}
revert(abi.decode(result, (string)));
}

results[i] = result;
}
}
}
80 changes: 80 additions & 0 deletions core/contracts/lib/external/PeripheryPayments.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-or-later

// Copied from https://github.com/ERC4626-Alliance/ERC4626-Contracts based on
// the project commit 643cd04 from Apr 20, 2022

pragma solidity >=0.7.5;

import "solmate/src/utils/SafeTransferLib.sol";

/**
@title Periphery Payments
@notice Immutable state used by periphery contracts
Largely Forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/PeripheryPayments.sol
Changes:
* no interface
* no inheritdoc
* add immutable WETH9 in constructor instead of PeripheryImmutableState
* receive from any address
* Solmate interfaces and transfer lib
* casting
* add approve, wrapWETH9 and pullToken
*/
abstract contract PeripheryPayments {
using SafeTransferLib for *;

IWETH9 public immutable WETH9;

constructor(IWETH9 _WETH9) {
WETH9 = _WETH9;
}

receive() external payable {}

function approve(ERC20 token, address to, uint256 amount) public payable {
token.safeApprove(to, amount);
}

function unwrapWETH9(uint256 amountMinimum, address recipient) public payable {
uint256 balanceWETH9 = WETH9.balanceOf(address(this));
require(balanceWETH9 >= amountMinimum, 'Insufficient WETH9');

if (balanceWETH9 > 0) {
WETH9.withdraw(balanceWETH9);
recipient.safeTransferETH(balanceWETH9);
}
}

function wrapWETH9() public payable {
if (address(this).balance > 0) WETH9.deposit{value: address(this).balance}(); // wrap everything
}

function pullToken(ERC20 token, uint256 amount, address recipient) public payable {
token.safeTransferFrom(msg.sender, recipient, amount);
}

function sweepToken(
ERC20 token,
uint256 amountMinimum,
address recipient
) public payable {
uint256 balanceToken = token.balanceOf(address(this));
require(balanceToken >= amountMinimum, 'Insufficient token');

if (balanceToken > 0) {
token.safeTransfer(recipient, balanceToken);
}
}

function refundETH() external payable {
if (address(this).balance > 0) SafeTransferLib.safeTransferETH(msg.sender, address(this).balance);
}
}

abstract contract IWETH9 is ERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable virtual;

/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external virtual;
}
66 changes: 66 additions & 0 deletions core/contracts/lib/external/SelfPermit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-or-later

// Copied from https://github.com/ERC4626-Alliance/ERC4626-Contracts based on
// the project commit 643cd04 from Apr 20, 2022

pragma solidity >=0.5.0;

import {ERC20} from "solmate/src/tokens/ERC20.sol";

import './interfaces/ISelfPermit.sol';
import './interfaces/IERC20PermitAllowed.sol';

/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
/// @dev These functions are expected to be embedded in multicalls to allow EOAs to approve a contract and call a function
/// that requires an approval in a single transaction.
abstract contract SelfPermit is ISelfPermit {
/// @inheritdoc ISelfPermit
function selfPermit(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public payable override {
ERC20(token).permit(msg.sender, address(this), value, deadline, v, r, s);
}

/// @inheritdoc ISelfPermit
function selfPermitIfNecessary(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable override {
if (ERC20(token).allowance(msg.sender, address(this)) < value) selfPermit(token, value, deadline, v, r, s);
}

/// @inheritdoc ISelfPermit
function selfPermitAllowed(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) public payable override {
IERC20PermitAllowed(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s);
}

/// @inheritdoc ISelfPermit
function selfPermitAllowedIfNecessary(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable override {
if (ERC20(token).allowance(msg.sender, address(this)) < type(uint256).max)
selfPermitAllowed(token, nonce, expiry, v, r, s);
}
}
33 changes: 33 additions & 0 deletions core/contracts/lib/external/interfaces/IERC20PermitAllowed.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/external/IERC20PermitAllowed.sol

// SPDX-License-Identifier: GPL-2.0-or-later

// Copied from https://github.com/ERC4626-Alliance/ERC4626-Contracts based on
// the project commit 643cd04 from Apr 20, 2022

pragma solidity >=0.5.0;

/// @title Interface for permit
/// @notice Interface used by DAI/CHAI for permit
interface IERC20PermitAllowed {
/// @notice Approve the spender to spend some tokens via the holder signature
/// @dev This is the permit interface used by DAI and CHAI
/// @param holder The address of the token holder, the token owner
/// @param spender The address of the token spender
/// @param nonce The holder's nonce, increases at each call to permit
/// @param expiry The timestamp at which the permit is no longer valid
/// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
18 changes: 18 additions & 0 deletions core/contracts/lib/external/interfaces/IMulticall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IMulticall.sol

// SPDX-License-Identifier: GPL-2.0-or-later

// Copied from https://github.com/ERC4626-Alliance/ERC4626-Contracts based on
// the project commit 643cd04 from Apr 20, 2022

pragma solidity >=0.7.5;

/// @title Multicall interface
/// @notice Enables calling multiple methods in a single call to the contract
interface IMulticall {
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}
82 changes: 82 additions & 0 deletions core/contracts/lib/external/interfaces/ISelfPermit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// forked from https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/ISelfPermit.sol

// SPDX-License-Identifier: GPL-2.0-or-later

// Copied from https://github.com/ERC4626-Alliance/ERC4626-Contracts based on
// the project commit 643cd04 from Apr 20, 2022

pragma solidity >=0.7.5;

/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
interface ISelfPermit {
/// @notice Permits this contract to spend a given token from `msg.sender`
/// @dev The `owner` is always msg.sender and the `spender` is always address(this).
/// @param token The address of the token spent
/// @param value The amount that can be spent of token
/// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermit(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;

/// @notice Permits this contract to spend a given token from `msg.sender`
/// @dev The `owner` is always msg.sender and the `spender` is always address(this).
/// Can be used instead of #selfPermit to prevent calls from failing due to a frontrun of a call to #selfPermit
/// @param token The address of the token spent
/// @param value The amount that can be spent of token
/// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitIfNecessary(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;

/// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
/// @dev The `owner` is always msg.sender and the `spender` is always address(this)
/// @param token The address of the token spent
/// @param nonce The current nonce of the owner
/// @param expiry The timestamp at which the permit is no longer valid
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitAllowed(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable;

/// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
/// @dev The `owner` is always msg.sender and the `spender` is always address(this)
/// Can be used instead of #selfPermitAllowed to prevent calls from failing due to a frontrun of a call to #selfPermitAllowed.
/// @param token The address of the token spent
/// @param nonce The current nonce of the owner
/// @param expiry The timestamp at which the permit is no longer valid
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitAllowedIfNecessary(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}
Loading
Loading