-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix: small typo in AaveSwapper Deployment script * wip: AaveStethWithdrawer * smol improvements * wip: almost ready * fix some issues raised in review * fix: some TODOs & some issues raised in review * fix some issues raised in review * ready for review * add deployed address * fix typo * rename contracts to Wsteth vs Steth added access controls to startWithdraw and finalizeWithdraw redeploy * add guardian permission to startWithdraw redeploy * fix typo & redeploy * fix: small typo in AaveSwapper Deployment script * wip: AaveStethWithdrawer * smol improvements * wip: almost ready * fix some issues raised in review * fix: some TODOs & some issues raised in review * fix some issues raised in review * ready for review * add deployed address * fix typo * rename contracts to Wsteth vs Steth added access controls to startWithdraw and finalizeWithdraw redeploy * add guardian permission to startWithdraw redeploy * fix typo & redeploy --------- Co-authored-by: defijesus.eth <defijesus@duck.com>
- Loading branch information
Showing
7 changed files
with
503 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import {Script} from 'forge-std/Script.sol'; | ||
import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; | ||
import {TransparentProxyFactory} from 'solidity-utils/contracts/transparent-proxy/TransparentProxyFactory.sol'; | ||
import {AaveWstethWithdrawer} from 'src/asset-manager/AaveWstethWithdrawer.sol'; | ||
|
||
contract DeployAaveWithdrawer is Script { | ||
function run() external { | ||
vm.startBroadcast(); | ||
|
||
address aaveWithdrawer = address(new AaveWstethWithdrawer()); | ||
TransparentProxyFactory(MiscEthereum.TRANSPARENT_PROXY_FACTORY).create( | ||
aaveWithdrawer, | ||
MiscEthereum.PROXY_ADMIN, | ||
abi.encodeWithSelector(AaveWstethWithdrawer.initialize.selector) | ||
); | ||
|
||
vm.stopBroadcast(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// SPDX-License-Identifier: MIT | ||
/* | ||
_ ΞΞΞΞ _ | ||
/_;-.__ / _\ _.-;_\ | ||
`-._`'`_/'`.-' | ||
`\ /` | ||
| / | ||
/-.( | ||
\_._\ | ||
\ \`; | ||
> |/ | ||
/ // | ||
|// | ||
\(\ | ||
`` | ||
defijesus.eth | ||
*/ | ||
pragma solidity ^0.8.0; | ||
|
||
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; | ||
import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; | ||
import {OwnableWithGuardian} from 'solidity-utils/contracts/access-control/OwnableWithGuardian.sol'; | ||
import {Initializable} from 'solidity-utils/contracts/transparent-proxy/Initializable.sol'; | ||
import {Rescuable721, Rescuable} from 'solidity-utils/contracts/utils/Rescuable721.sol'; | ||
import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; | ||
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; | ||
import {IAaveWstethWithdrawer, IWithdrawalQueueERC721, IWETH} from './interfaces/IAaveWstethWithdrawer.sol'; | ||
|
||
/** | ||
* @title AaveWstethWithdrawer | ||
* @author defijesus.eth | ||
* @notice Helper contract to natively withdraw wstETH to the collector | ||
*/ | ||
contract AaveWstethWithdrawer is Initializable, OwnableWithGuardian, Rescuable721, IAaveWstethWithdrawer { | ||
using SafeERC20 for IERC20; | ||
|
||
/// auto incrementing index to store requestIds of withdrawals | ||
uint256 public nextIndex; | ||
uint256 public minCheckpointIndex; | ||
|
||
/// stores a mapping of index to arrays of requestIds | ||
mapping(uint256 => uint256[]) public requestIds; | ||
|
||
/// https://etherscan.io/address/0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1 | ||
IWithdrawalQueueERC721 public constant WSTETH_WITHDRAWAL_QUEUE = | ||
IWithdrawalQueueERC721(0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1); | ||
|
||
function initialize() external initializer { | ||
_transferOwnership(GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||
_updateGuardian(0x2cc1ADE245020FC5AAE66Ad443e1F66e01c54Df1); | ||
IERC20(AaveV3EthereumAssets.wstETH_UNDERLYING).approve( | ||
address(WSTETH_WITHDRAWAL_QUEUE), | ||
type(uint256).max | ||
); | ||
minCheckpointIndex = WSTETH_WITHDRAWAL_QUEUE.getLastCheckpointIndex(); | ||
} | ||
|
||
/// @inheritdoc IAaveWstethWithdrawer | ||
function startWithdraw(uint256[] calldata amounts) external onlyOwnerOrGuardian { | ||
uint256 index = nextIndex++; | ||
uint256[] memory rIds = WSTETH_WITHDRAWAL_QUEUE.requestWithdrawalsWstETH(amounts, address(this)); | ||
|
||
requestIds[index] = rIds; | ||
emit StartedWithdrawal(amounts, index); | ||
} | ||
|
||
/// @inheritdoc IAaveWstethWithdrawer | ||
function finalizeWithdraw(uint256 index) external onlyOwnerOrGuardian { | ||
uint256[] memory reqIds = requestIds[index]; | ||
uint256[] memory hintIds = WSTETH_WITHDRAWAL_QUEUE.findCheckpointHints( | ||
reqIds, | ||
minCheckpointIndex, | ||
WSTETH_WITHDRAWAL_QUEUE.getLastCheckpointIndex() | ||
); | ||
|
||
WSTETH_WITHDRAWAL_QUEUE.claimWithdrawalsTo(reqIds, hintIds, address(this)); | ||
|
||
uint256 ethBalance = address(this).balance; | ||
|
||
IWETH(AaveV3EthereumAssets.WETH_UNDERLYING).deposit{value: ethBalance}(); | ||
|
||
IERC20(AaveV3EthereumAssets.WETH_UNDERLYING).transfer( | ||
address(AaveV3Ethereum.COLLECTOR), | ||
ethBalance | ||
); | ||
|
||
emit FinalizedWithdrawal(ethBalance, index); | ||
} | ||
|
||
/// @inheritdoc Rescuable | ||
function whoCanRescue() public view override returns (address) { | ||
return owner(); | ||
} | ||
|
||
fallback() external payable {} | ||
receive() external payable {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
interface IAaveWstethWithdrawer { | ||
/// @notice emitted when a new Withdrawal is requested | ||
/// @param amounts the amounts requested to be withdrawn | ||
/// @param index the storage index of the respective requestIds used to finalize the withdrawal | ||
event StartedWithdrawal(uint256[] amounts, uint256 indexed index); | ||
|
||
/// @notice emitted when a new Withdrawal is requested | ||
/// @param amount the amount of WETH withdrawn to collector | ||
/// @param index the storage index of the respective requestIds used to finalize the withdrawal | ||
event FinalizedWithdrawal(uint256 amount, uint256 indexed index); | ||
|
||
/// @notice Starts a new withdrawal | ||
/// @param amounts a list of amounts to be withdrawn. each amount must be > 100 wei and < 1000 ETH | ||
function startWithdraw(uint256[] calldata amounts) external; | ||
|
||
/// @notice Finalizes a withdrawal | ||
/// @param index the index of the withdrawal request data of the withdrawal to be finalized | ||
function finalizeWithdraw(uint256 index) external; | ||
} | ||
|
||
interface IWithdrawalQueueERC721 { | ||
|
||
/// @notice Request the batch of wstETH for withdrawal. Approvals for the passed amounts should be done before. | ||
/// @param _amounts an array of wstETH amount values. | ||
/// The standalone withdrawal request will be created for each item in the passed list. | ||
/// @param _owner address that will be able to manage the created requests. | ||
/// If `address(0)` is passed, `msg.sender` will be used as an owner. | ||
/// @return requestIds an array of the created withdrawal request ids | ||
function requestWithdrawalsWstETH( | ||
uint256[] calldata _amounts, | ||
address _owner | ||
) external returns (uint256[] memory requestIds); | ||
|
||
/// @notice Claim a batch of withdrawal requests if they are finalized sending ether to `_recipient` | ||
/// @param _requestIds array of request ids to claim | ||
/// @param _hints checkpoint hint for each id. Can be obtained with `findCheckpointHints()` | ||
/// @param _recipient address where claimed ether will be sent to | ||
/// @dev | ||
/// Reverts if recipient is equal to zero | ||
/// Reverts if requestIds and hints arrays length differs | ||
/// Reverts if any requestId or hint in arguments are not valid | ||
/// Reverts if any request is not finalized or already claimed | ||
/// Reverts if msg sender is not an owner of the requests | ||
function claimWithdrawalsTo( | ||
uint256[] calldata _requestIds, | ||
uint256[] calldata _hints, | ||
address _recipient | ||
) external; | ||
|
||
/// @notice Finds the list of hints for the given `_requestIds` searching among the checkpoints with indices | ||
/// in the range `[_firstIndex, _lastIndex]`. | ||
/// NB! Array of request ids should be sorted | ||
/// NB! `_firstIndex` should be greater than 0, because checkpoint list is 1-based array | ||
/// Usage: findCheckpointHints(_requestIds, 1, getLastCheckpointIndex()) | ||
/// @param _requestIds ids of the requests sorted in the ascending order to get hints for | ||
/// @param _firstIndex left boundary of the search range. Should be greater than 0 | ||
/// @param _lastIndex right boundary of the search range. Should be less than or equal to getLastCheckpointIndex() | ||
/// @return hintIds array of hints used to find required checkpoint for the request | ||
function findCheckpointHints( | ||
uint256[] calldata _requestIds, | ||
uint256 _firstIndex, | ||
uint256 _lastIndex | ||
) external view returns (uint256[] memory hintIds); | ||
|
||
/// @notice length of the checkpoint array. Last possible value for the hint. | ||
/// NB! checkpoints are indexed from 1, so it returns 0 if there is no checkpoints | ||
function getLastCheckpointIndex() external view returns (uint256); | ||
} | ||
|
||
interface IWETH { | ||
function deposit() external payable; | ||
} |
Oops, something went wrong.
8f99938
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Foundry report
Build log
Test success 🌈