Skip to content

Commit

Permalink
Compiling fully async vault
Browse files Browse the repository at this point in the history
  • Loading branch information
hieronx committed Aug 21, 2024
1 parent caece4b commit 6240b75
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 24 deletions.
29 changes: 18 additions & 11 deletions src/ControlledAsyncDeposits.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {ERC20} from "solmate/tokens/ERC20.sol";
* Conversions between claimable assets/shares should be checked for rounding safety.
*/
abstract contract BaseControlledAsyncDeposits is BaseERC7540, IERC7540Deposit {
uint256 internal _totalPendingAssets;
uint256 internal _totalPendingDepositAssets;
mapping(address => PendingDeposit) internal _pendingDeposit;
mapping(address => ClaimableDeposit) internal _claimableDeposit;

Expand All @@ -32,10 +32,10 @@ abstract contract BaseControlledAsyncDeposits is BaseERC7540, IERC7540Deposit {
uint256 shares;
}

function totalAssets() public view override returns (uint256) {
function totalAssets() public view virtual override returns (uint256) {
// total assets pending redemption must be removed from the reported total assets
// otherwise pending assets would be treated as yield for outstanding shares
return ERC20(asset).balanceOf(address(this)) - _totalPendingAssets;
return ERC20(asset).balanceOf(address(this)) - _totalPendingDepositAssets;
}

/*//////////////////////////////////////////////////////////////
Expand All @@ -53,7 +53,7 @@ abstract contract BaseControlledAsyncDeposits is BaseERC7540, IERC7540Deposit {
uint256 currentPendingAssets = _pendingDeposit[controller].assets;
_pendingDeposit[controller] = PendingDeposit(assets + currentPendingAssets);

_totalPendingAssets += assets;
_totalPendingDepositAssets += assets;

emit DepositRequest(controller, owner, REQUEST_ID, msg.sender, assets);
return REQUEST_ID;
Expand Down Expand Up @@ -84,7 +84,7 @@ abstract contract BaseControlledAsyncDeposits is BaseERC7540, IERC7540Deposit {
ClaimableDeposit(request.assets + currentClaimableAssets, shares + currentClaimableShares);

delete _pendingDeposit[controller];
_totalPendingAssets -= request.assets;
_totalPendingDepositAssets -= request.assets;
}

/*//////////////////////////////////////////////////////////////
Expand All @@ -103,6 +103,10 @@ abstract contract BaseControlledAsyncDeposits is BaseERC7540, IERC7540Deposit {
emit Deposit(receiver, controller, assets, shares);
}

function deposit(uint256 assets, address receiver) public virtual override returns (uint256 shares) {
shares = deposit(assets, receiver, receiver);
}

function mint(uint256 shares, address receiver, address controller) public override returns (uint256 assets) {
require(controller == msg.sender || isOperator[controller][msg.sender], "ERC7540Vault/invalid-caller");
require(shares != 0 && shares == maxMint(controller), "Must claim nonzero maximum");
Expand All @@ -115,28 +119,31 @@ abstract contract BaseControlledAsyncDeposits is BaseERC7540, IERC7540Deposit {
emit Deposit(receiver, controller, assets, shares);
}

function maxDeposit(address controller) public view override returns (uint256) {
function mint(uint256 shares, address receiver) public virtual override returns (uint256 assets) {
assets = mint(shares, receiver, receiver);
}

function maxDeposit(address controller) public view virtual override returns (uint256) {
return _claimableDeposit[controller].assets;
}

function maxMint(address controller) public view override returns (uint256) {
function maxMint(address controller) public view virtual override returns (uint256) {
return _claimableDeposit[controller].shares;
}

// preview functions always revert for async flows
function previewDeposit(uint256) public pure override returns (uint256) {
function previewDeposit(uint256) public pure virtual override returns (uint256) {
revert("ERC7540Vault/async-flow");
}

function previewMint(uint256) public pure override returns (uint256) {
function previewMint(uint256) public pure virtual override returns (uint256) {
revert("ERC7540Vault/async-flow");
}

/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/

function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
return interfaceId == type(IERC7540Deposit).interfaceId || super.supportsInterface(interfaceId);
}
}
Expand Down
128 changes: 128 additions & 0 deletions src/FullyAsyncVault.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {IERC7540Deposit, IERC7540Redeem} from "src/interfaces/IERC7540.sol";
import {BaseERC7540} from "src/BaseERC7540.sol";
import {ERC4626} from "solmate/mixins/ERC4626.sol";
import {BaseControlledAsyncDeposits} from "src/ControlledAsyncDeposits.sol";
import {BaseTimelockedAsyncWithdrawals} from "src/TimelockedAsyncWithdrawals.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
Expand All @@ -12,4 +14,130 @@ contract FullyAsyncVault is BaseControlledAsyncDeposits, BaseTimelockedAsyncWith
BaseTimelockedAsyncWithdrawals()
BaseERC7540(_asset, _name, _symbol)
{}

function totalAssets()
public
view
override(BaseControlledAsyncDeposits, BaseTimelockedAsyncWithdrawals)
returns (uint256)
{
return ERC20(asset).balanceOf(address(this)) - _totalPendingDepositAssets - _totalPendingRedeemAssets;
}

function maxDeposit(address controller)
public
view
override(BaseControlledAsyncDeposits, ERC4626)
returns (uint256)
{
return BaseControlledAsyncDeposits.maxDeposit(controller);
}

function previewDeposit(uint256)
public
pure
virtual
override(BaseControlledAsyncDeposits, ERC4626)
returns (uint256)
{
revert("ERC7540Vault/async-flow");
}

function deposit(uint256 assets, address receiver)
public
virtual
override(BaseControlledAsyncDeposits, ERC4626)
returns (uint256 shares)
{
shares = BaseControlledAsyncDeposits.deposit(assets, receiver, receiver);
}

function maxMint(address controller) public view override(BaseControlledAsyncDeposits, ERC4626) returns (uint256) {
return BaseControlledAsyncDeposits.maxMint(controller);
}

function previewMint(uint256)
public
pure
virtual
override(BaseControlledAsyncDeposits, ERC4626)
returns (uint256)
{
revert("ERC7540Vault/async-flow");
}

function mint(uint256 shares, address receiver)
public
virtual
override(BaseControlledAsyncDeposits, ERC4626)
returns (uint256 assets)
{
assets = BaseControlledAsyncDeposits.mint(shares, receiver, receiver);
}

function maxWithdraw(address controller)
public
view
override(BaseTimelockedAsyncWithdrawals, ERC4626)
returns (uint256)
{
return BaseTimelockedAsyncWithdrawals.maxWithdraw(controller);
}

function previewWithdraw(uint256)
public
pure
virtual
override(BaseTimelockedAsyncWithdrawals, ERC4626)
returns (uint256)
{
revert("ERC7540Vault/async-flow");
}

function withdraw(uint256 assets, address receiver, address controller)
public
virtual
override(BaseTimelockedAsyncWithdrawals, ERC4626)
returns (uint256 shares)
{
shares = BaseTimelockedAsyncWithdrawals.withdraw(assets, receiver, controller);
}

function maxRedeem(address controller)
public
view
override(BaseTimelockedAsyncWithdrawals, ERC4626)
returns (uint256)
{
return BaseTimelockedAsyncWithdrawals.maxRedeem(controller);
}

function previewRedeem(uint256)
public
pure
virtual
override(BaseTimelockedAsyncWithdrawals, ERC4626)
returns (uint256)
{
revert("ERC7540Vault/async-flow");
}

function redeem(uint256 shares, address receiver, address controller)
public
virtual
override(BaseTimelockedAsyncWithdrawals, ERC4626)
returns (uint256 assets)
{
assets = BaseTimelockedAsyncWithdrawals.redeem(shares, receiver, controller);
}

function supportsInterface(bytes4 interfaceId)
public
pure
override(BaseControlledAsyncDeposits, BaseTimelockedAsyncWithdrawals)
returns (bool)
{
return interfaceId == type(IERC7540Deposit).interfaceId || interfaceId == type(IERC7540Redeem).interfaceId
|| super.supportsInterface(interfaceId);
}
}
36 changes: 23 additions & 13 deletions src/TimelockedAsyncWithdrawals.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {ERC20} from "solmate/tokens/ERC20.sol";
abstract contract BaseTimelockedAsyncWithdrawals is BaseERC7540, IERC7540Redeem {
uint32 public constant TIMELOCK = 3 days;

uint256 internal _totalPendingApproxAssets;
uint256 internal _totalPendingRedeemAssets;
mapping(address => RedemptionRequest) internal _pendingRedemption;

struct RedemptionRequest {
Expand All @@ -33,8 +33,8 @@ abstract contract BaseTimelockedAsyncWithdrawals is BaseERC7540, IERC7540Redeem
uint32 claimableTimestamp;
}

function totalAssets() public view override returns (uint256) {
return ERC20(asset).balanceOf(address(this)) - _totalPendingApproxAssets;
function totalAssets() public view virtual override returns (uint256) {
return ERC20(asset).balanceOf(address(this)) - _totalPendingRedeemAssets;
}

/*//////////////////////////////////////////////////////////////
Expand All @@ -53,7 +53,7 @@ abstract contract BaseTimelockedAsyncWithdrawals is BaseERC7540, IERC7540Redeem
_pendingRedemption[controller] =
RedemptionRequest({assets: assets, shares: shares, claimableTimestamp: uint32(block.timestamp) + TIMELOCK});

_totalPendingApproxAssets += assets;
_totalPendingRedeemAssets += assets;

emit RedeemRequest(controller, owner, REQUEST_ID, msg.sender, shares);
return REQUEST_ID;
Expand All @@ -79,7 +79,12 @@ abstract contract BaseTimelockedAsyncWithdrawals is BaseERC7540, IERC7540Redeem
ERC4626 OVERRIDDEN LOGIC
//////////////////////////////////////////////////////////////*/

function withdraw(uint256 assets, address receiver, address controller) public override returns (uint256 shares) {
function withdraw(uint256 assets, address receiver, address controller)
public
virtual
override
returns (uint256 shares)
{
require(controller == msg.sender || isOperator[controller][msg.sender], "ERC7540Vault/invalid-caller");
require(assets != 0 && assets == maxWithdraw(controller), "Must claim nonzero maximum");

Expand All @@ -90,14 +95,19 @@ abstract contract BaseTimelockedAsyncWithdrawals is BaseERC7540, IERC7540Redeem
uint256 claimableAssets = request.assets;

delete _pendingRedemption[controller];
_totalPendingApproxAssets -= claimableAssets;
_totalPendingRedeemAssets -= claimableAssets;

SafeTransferLib.safeTransfer(asset, receiver, claimableAssets);

emit Withdraw(msg.sender, receiver, controller, claimableAssets, shares);
}

function redeem(uint256 shares, address receiver, address controller) public override returns (uint256 assets) {
function redeem(uint256 shares, address receiver, address controller)
public
virtual
override
returns (uint256 assets)
{
require(controller == msg.sender || isOperator[controller][msg.sender], "ERC7540Vault/invalid-caller");
require(shares != 0 && shares == maxRedeem(controller), "Must claim nonzero maximum");

Expand All @@ -107,22 +117,22 @@ abstract contract BaseTimelockedAsyncWithdrawals is BaseERC7540, IERC7540Redeem
assets = request.assets;

delete _pendingRedemption[controller];
_totalPendingApproxAssets -= assets;
_totalPendingRedeemAssets -= assets;

SafeTransferLib.safeTransfer(asset, receiver, assets);

emit Withdraw(msg.sender, receiver, controller, assets, shares);
}

function maxWithdraw(address controller) public view override returns (uint256) {
function maxWithdraw(address controller) public view virtual override returns (uint256) {
RedemptionRequest memory request = _pendingRedemption[controller];
if (request.claimableTimestamp <= block.timestamp) {
return request.assets;
}
return 0;
}

function maxRedeem(address controller) public view override returns (uint256) {
function maxRedeem(address controller) public view virtual override returns (uint256) {
RedemptionRequest memory request = _pendingRedemption[controller];
if (request.claimableTimestamp <= block.timestamp) {
return request.shares;
Expand All @@ -131,19 +141,19 @@ abstract contract BaseTimelockedAsyncWithdrawals is BaseERC7540, IERC7540Redeem
}

// Preview functions always revert for async flows
function previewWithdraw(uint256) public pure override returns (uint256) {
function previewWithdraw(uint256) public pure virtual override returns (uint256) {
revert("ERC7540Vault/async-flow");
}

function previewRedeem(uint256) public pure override returns (uint256) {
function previewRedeem(uint256) public pure virtual override returns (uint256) {
revert("ERC7540Vault/async-flow");
}

/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/

function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
return interfaceId == type(IERC7540Redeem).interfaceId || super.supportsInterface(interfaceId);
}
}
Expand Down

0 comments on commit 6240b75

Please sign in to comment.