Skip to content

Commit

Permalink
Tasks: Implement ERC4626 liquidity tasks (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
lgalende authored Nov 28, 2023
1 parent 2f7408f commit cdea1c3
Show file tree
Hide file tree
Showing 13 changed files with 992 additions and 0 deletions.
8 changes: 8 additions & 0 deletions packages/authorizer/contracts/AuthorizedHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ contract AuthorizedHelpers {
r[3] = p4;
}

function authParams(address p1, uint256 p2, address p3, uint256 p4) internal pure returns (uint256[] memory r) {
r = new uint256[](4);
r[0] = uint256(uint160(p1));
r[1] = p2;
r[2] = uint256(uint160(p3));
r[3] = p4;
}

function authParams(address p1, uint256 p2, uint256 p3, uint256 p4) internal pure returns (uint256[] memory r) {
r = new uint256[](4);
r[0] = uint256(uint160(p1));
Expand Down
4 changes: 4 additions & 0 deletions packages/authorizer/contracts/test/AuthorizedHelpersMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ contract AuthorizedHelpersMock is AuthorizedHelpers {
return authParams(p1, p2, p3, p4);
}

function getAuthParams(address p1, uint256 p2, address p3, uint256 p4) external pure returns (uint256[] memory r) {
return authParams(p1, p2, p3, p4);
}

function getAuthParams(address p1, uint256 p2, uint256 p3, uint256 p4) external pure returns (uint256[] memory r) {
return authParams(p1, p2, p3, p4);
}
Expand Down
1 change: 1 addition & 0 deletions packages/authorizer/test/AuthorizedHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('AuthorizedHelpers', () => {

context('when the number of arguments is 4', () => {
itBehavesLikeAuthParams('address,address,uint256,uint256')
itBehavesLikeAuthParams('address,uint256,address,uint256')
itBehavesLikeAuthParams('address,uint256,uint256,uint256')
itBehavesLikeAuthParams('bytes32,address,uint256,bool')
})
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,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

0 comments on commit cdea1c3

Please sign in to comment.