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

Tasks: Implement ERC4626 liquidity tasks #126

Merged
merged 4 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
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,27 @@
// 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 {
/**
* @dev Executes the ERC4626 exiter task
*/
function call(address erc4626, uint256 amount, uint256 minAmountOut) 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 {
/**
* The ERC4626 reference is zero
*/
error TaskERC4626Zero();

/**
* @dev Executes the ERC4626 joiner task
*/
function call(address token, uint256 amount, address erc4626, uint256 minAmountOut) 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);
}
}
102 changes: 102 additions & 0 deletions packages/tasks/contracts/liquidity/erc4626/ERC4626Exiter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// 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/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. Note that the ERC4626 is also the token.
* @param erc4626 Address of the ERC4626 to be exited
* @param amount Amount of shares to be exited with
* @param minAmountOut Minimum amount of assets willing to receive
*/
function call(address erc4626, uint256 amount, uint256 minAmountOut)
external
override
authP(authParams(erc4626, amount, minAmountOut))
{
if (amount == 0) amount = getTaskAmount(erc4626);
_beforeERC4626Exiter(erc4626, amount);
bytes memory connectorData = abi.encodeWithSelector(
IERC4626Connector.exit.selector,
erc4626,
amount,
minAmountOut
);
bytes memory result = ISmartVault(smartVault).execute(connector, connectorData);
(address tokenOut, uint256 amountOut) = abi.decode(result, (address, uint256));
_afterERC4626Exiter(erc4626, amount, tokenOut, amountOut);
}

/**
* @dev Before ERC4626 exiter hook
*/
function _beforeERC4626Exiter(address token, uint256 amount) internal virtual {
_beforeBaseERC4626Task(token, amount);
}

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