Skip to content

Commit

Permalink
tasks: implement erc4626 liquidity tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
lgalende committed Nov 13, 2023
1 parent 6ee4d1d commit fb8152a
Show file tree
Hide file tree
Showing 12 changed files with 967 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ interface IERC4626Connector {
*/
error ERC4626InvalidRedeem(uint256 actual, uint256 expected);

/**
* @dev Tells the underlying token of an ERC4626
*/
function getToken(address erc4626) external view returns (address);

/**
* @dev Deposits assets to the underlying ERC4626
* @param erc4626 Address of the ERC4626 to join
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Contract } from 'ethers'

/* eslint-disable no-secrets/no-secrets */

const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'

const WHALE = '0xf584f8728b874a6a5c7a8d4d387c9aae9172d621'
const MORPHO_AAVE_V3_WETH = '0x39Dd7790e75C6F663731f7E1FdC0f35007D3879b'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

import '../../ITask.sol';

/**
* @dev Base ERC4626 task interface
*/
interface IBaseERC4626Task is ITask {
/**
* @dev The token is zero
*/
error TaskTokenZero();

/**
* @dev The amount is zero
*/
error TaskAmountZero();

/**
* @dev The connector is zero
*/
error TaskConnectorZero();

/**
* @dev Emitted every time the connector is set
*/
event ConnectorSet(address indexed connector);

/**
* @dev Tells the connector tied to the task
*/
function connector() external view returns (address);

/**
* @dev Sets a new connector
* @param newConnector Address of the connector to be set
*/
function setConnector(address newConnector) external;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

import './IBaseERC4626Task.sol';

/**
* @dev ERC4626 exiter task interface
*/
interface IERC4626Exiter is IBaseERC4626Task {
/**
* The token is not the ERC4626 token
*/
error TaskTokenNotERC4626(address token, address erc4626);

/**
* @dev Executes the ERC4626 exiter task
*/
function call(address token, uint256 amount) external;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

import './IBaseERC4626Task.sol';

/**
* @dev ERC4626 joiner task interface
*/
interface IERC4626Joiner is IBaseERC4626Task {
/**
* @dev The token is not the ERC4626 underlying token
*/
error TaskTokenNotUnderlying(address token, address underlying);

/**
* @dev Executes the ERC4626 joiner task
*/
function call(address toke, uint256 amount) external;
}
90 changes: 90 additions & 0 deletions packages/tasks/contracts/liquidity/erc4626/BaseERC4626Task.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

import '../../Task.sol';
import '../../interfaces/liquidity/erc4626/IBaseERC4626Task.sol';

/**
* @title Base ERC4626 task
* @dev Task that offers the basic components for more detailed ERC4626 related tasks
*/
abstract contract BaseERC4626Task is IBaseERC4626Task, Task {
// Task connector address
address public override connector;

/**
* @dev Base ERC4626 config. Only used in the initializer.
*/
struct BaseERC4626Config {
address connector;
TaskConfig taskConfig;
}

/**
* @dev Initializes the base ERC4626 task. It does call upper contracts initializers.
* @param config Base ERC4626 config
*/
function __BaseERC4626Task_init(BaseERC4626Config memory config) internal onlyInitializing {
__Task_init(config.taskConfig);
__BaseERC4626Task_init_unchained(config);
}

/**
* @dev Initializes the base ERC4626 task. It does not call upper contracts initializers.
* @param config Base ERC4626 config
*/
function __BaseERC4626Task_init_unchained(BaseERC4626Config memory config) internal onlyInitializing {
_setConnector(config.connector);
}

/**
* @dev Sets the task connector
* @param newConnector Address of the new connector to be set
*/
function setConnector(address newConnector) external override authP(authParams(newConnector)) {
_setConnector(newConnector);
}

/**
* @dev Before base ERC4626 task hook
*/
function _beforeBaseERC4626Task(address token, uint256 amount) internal virtual {
_beforeTask(token, amount);
if (token == address(0)) revert TaskTokenZero();
if (amount == 0) revert TaskAmountZero();
}

/**
* @dev After base ERC4626 task hook
*/
function _afterBaseERC4626Task(address tokenIn, uint256 amountIn, address tokenOut, uint256 amountOut)
internal
virtual
{
_increaseBalanceConnector(tokenOut, amountOut);
_afterTask(tokenIn, amountIn);
}

/**
* @dev Sets the task connector
* @param newConnector New connector to be set
*/
function _setConnector(address newConnector) internal {
if (newConnector == address(0)) revert TaskConnectorZero();
connector = newConnector;
emit ConnectorSet(newConnector);
}
}
94 changes: 94 additions & 0 deletions packages/tasks/contracts/liquidity/erc4626/ERC4626Exiter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

import '@mimic-fi/v3-connectors/contracts/interfaces/liquidity/erc4626/IERC4626Connector.sol';

import './BaseERC4626Task.sol';
import '../../interfaces/liquidity/erc4626/IERC4626Exiter.sol';

/**
* @title ERC4626 exiter
* @dev Task that extends the base ERC4626 task to exit an ERC4626 vault
*/
contract ERC4626Exiter is IERC4626Exiter, BaseERC4626Task {
// Execution type for relayers
bytes32 public constant override EXECUTION_TYPE = keccak256('ERC4626_EXITER');

/**
* @dev ERC4626 exit config. Only used in the initializer.
*/
struct ERC4626ExitConfig {
BaseERC4626Config baseERC4626Config;
}

/**
* @dev Initializes a ERC4626 exiter
* @param config ERC4626 exit config
*/
function initialize(ERC4626ExitConfig memory config) external virtual initializer {
__ERC4626Exiter_init(config);
}

/**
* @dev Initializes the ERC4626 exiter. It does call upper contracts initializers.
* @param config ERC4626 exit config
*/
function __ERC4626Exiter_init(ERC4626ExitConfig memory config) internal onlyInitializing {
__BaseERC4626Task_init(config.baseERC4626Config);
__ERC4626Exiter_init_unchained(config);
}

/**
* @dev Initializes the ERC4626 exiter. It does not call upper contracts initializers.
* @param config ERC4626 exit config
*/
function __ERC4626Exiter_init_unchained(ERC4626ExitConfig memory config) internal onlyInitializing {
// solhint-disable-previous-line no-empty-blocks
}

/**
* @dev Executes the ERC4626 exiter task
* @param token Address of the token to be exited with
* @param amount Amount of shares to be exited with
*/
function call(address token, uint256 amount) external override authP(authParams(token, amount)) {
if (amount == 0) amount = getTaskAmount(token);
_beforeERC4626Exiter(token, amount);
bytes memory connectorData = abi.encodeWithSelector(IERC4626Connector.exit.selector, amount);
bytes memory result = ISmartVault(smartVault).execute(connector, connectorData);
(address tokenOut, uint256 amountOut) = abi.decode(result, (address, uint256));
_afterERC4626Exiter(token, amount, tokenOut, amountOut);
}

/**
* @dev Before ERC4626 exiter hook
*/
function _beforeERC4626Exiter(address token, uint256 amount) internal virtual {
_beforeBaseERC4626Task(token, amount);
address erc4626 = IERC4626Connector(connector).erc4626();
if (token != erc4626) revert TaskTokenNotERC4626(token, erc4626);
}

/**
* @dev After ERC4626 exiter hook
*/
function _afterERC4626Exiter(address tokenIn, uint256 amountIn, address tokenOut, uint256 amountOut)
internal
virtual
{
_afterBaseERC4626Task(tokenIn, amountIn, tokenOut, amountOut);
}
}
Loading

0 comments on commit fb8152a

Please sign in to comment.