From 61313a66f3da04eca4cb8be8c18fcf0b8dee421a Mon Sep 17 00:00:00 2001 From: "Eugene Y. Q. Shen" Date: Wed, 18 Dec 2024 23:52:25 -0500 Subject: [PATCH 1/3] [PDE-303] add NestTeller and NestAtomicQueue --- nest/src/vault/NestAtomicQueue.sol | 0 nest/src/vault/NestTeller.sol | 73 ++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 nest/src/vault/NestAtomicQueue.sol create mode 100644 nest/src/vault/NestTeller.sol diff --git a/nest/src/vault/NestAtomicQueue.sol b/nest/src/vault/NestAtomicQueue.sol new file mode 100644 index 00000000..e69de29b diff --git a/nest/src/vault/NestTeller.sol b/nest/src/vault/NestTeller.sol new file mode 100644 index 00000000..0f23379d --- /dev/null +++ b/nest/src/vault/NestTeller.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { MultiChainLayerZeroTellerWithMultiAssetSupport } from "@nucleus-boring-vault/base/Roles/MultiChainLayerZeroTellerWithMultiAssetSupport.sol"; + +contract NestTeller is MultiChainLayerZeroTellerWithMultiAssetSupport { + + address public asset; + uint256 public minimumMint = _minimumMint; + + constructor( + address _owner, + address _vault, + address _accountant, + address _endpoint, + address _asset, + uint256 _minimumMint + ) MultiChainLayerZeroTellerWithMultiAssetSupport( + _owner, + _vault, + _accountant, + _endpoint + ) { + asset = _asset; + minimumMint = _minimumMint; + } + + error Unimplemented(); + + function setAsset(address _asset) requiresAuth external { + asset = _asset; + } + + function setMinimumMint(uint256 _minimumMint) requiresAuth external { + minimumMint = _minimumMint; + } + + /** + * @notice Transfer assets from the owner into the vault and submit a request to buy shares + * @param assets Amount of `asset` to deposit + * @param controller Controller of the request + * @param owner Source of the assets to deposit + * @return requestId Discriminator between non-fungible requests + */ + function requestDeposit(uint256 assets, address controller, address owner) public returns (uint256 requestId) { + revert Unimplemented(); + } + + /** + * @notice Fulfill a request to buy shares by minting shares to the receiver + * @param assets Amount of `asset` that was deposited by `requestDeposit` + * @param receiver Address to receive the shares + * @param controller Controller of the request + */ + function deposit(uint256 assets, address receiver, address controller) public returns (uint256 shares) { + // Ensure receiver is msg.sender + if (receiver != msg.sender) { + revert InvalidReceiver(); + } + // Ensure controller is msg.sender + if (controller != msg.sender) { + revert InvalidController(); + } + + shares = deposit( + IERC20(this.asset), // depositAsset + assets, // depositAmount + this.minimumMint // minimumMint + ); + emit Deposit(msg.sender, receiver, assets, shares); + return shares; + } +} \ No newline at end of file From 347745ff1d4b6ab25eed8a0689f81a4a94e78bf2 Mon Sep 17 00:00:00 2001 From: "Eugene Y. Q. Shen" Date: Thu, 19 Dec 2024 00:02:17 -0500 Subject: [PATCH 2/3] update minimumMint to percentage --- nest/src/vault/NestTeller.sol | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/nest/src/vault/NestTeller.sol b/nest/src/vault/NestTeller.sol index 0f23379d..59d15cd5 100644 --- a/nest/src/vault/NestTeller.sol +++ b/nest/src/vault/NestTeller.sol @@ -2,11 +2,14 @@ pragma solidity ^0.8.25; import { MultiChainLayerZeroTellerWithMultiAssetSupport } from "@nucleus-boring-vault/base/Roles/MultiChainLayerZeroTellerWithMultiAssetSupport.sol"; +import { FixedPointMathLib } from "@solmate/utils/FixedPointMathLib.sol"; contract NestTeller is MultiChainLayerZeroTellerWithMultiAssetSupport { + using FixedPointMathLib for uint256; + address public asset; - uint256 public minimumMint = _minimumMint; + uint256 public minimumMintPercentage = _minimumMintPercentage; constructor( address _owner, @@ -14,7 +17,7 @@ contract NestTeller is MultiChainLayerZeroTellerWithMultiAssetSupport { address _accountant, address _endpoint, address _asset, - uint256 _minimumMint + uint256 _minimumMintPercentage ) MultiChainLayerZeroTellerWithMultiAssetSupport( _owner, _vault, @@ -22,7 +25,7 @@ contract NestTeller is MultiChainLayerZeroTellerWithMultiAssetSupport { _endpoint ) { asset = _asset; - minimumMint = _minimumMint; + minimumMintPercentage = _minimumMintPercentage; } error Unimplemented(); @@ -31,8 +34,8 @@ contract NestTeller is MultiChainLayerZeroTellerWithMultiAssetSupport { asset = _asset; } - function setMinimumMint(uint256 _minimumMint) requiresAuth external { - minimumMint = _minimumMint; + function setMinimumMintPercentage(uint256 _minimumMintPercentage) requiresAuth external { + minimumMintPercentage = _minimumMintPercentage; } /** @@ -63,9 +66,9 @@ contract NestTeller is MultiChainLayerZeroTellerWithMultiAssetSupport { } shares = deposit( - IERC20(this.asset), // depositAsset - assets, // depositAmount - this.minimumMint // minimumMint + IERC20(this.asset), + assets, + depositAmount.mulDivDown(this.minimumMintPercentage, 100) ); emit Deposit(msg.sender, receiver, assets, shares); return shares; From 4eb2a87b6d17f8c09fa378f1a0c818c8a20c2bd9 Mon Sep 17 00:00:00 2001 From: "Eugene Y. Q. Shen" Date: Thu, 19 Dec 2024 00:17:15 -0500 Subject: [PATCH 3/3] add NestAtomicQueue --- nest/src/interfaces/IComponentToken.sol | 4 +- nest/src/vault/NestAtomicQueue.sol | 216 ++++++++++++++++++++++++ nest/src/vault/NestTeller.sol | 12 +- 3 files changed, 230 insertions(+), 2 deletions(-) diff --git a/nest/src/interfaces/IComponentToken.sol b/nest/src/interfaces/IComponentToken.sol index 0d7e129c..7ba34fe3 100644 --- a/nest/src/interfaces/IComponentToken.sol +++ b/nest/src/interfaces/IComponentToken.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.25; interface IComponentToken { - // User Functions + // Events /** * @notice Emitted when the owner of some assets submits a request to buy shares @@ -29,6 +29,8 @@ interface IComponentToken { address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 shares ); + // User Functions + /** * @notice Transfer assets from the owner into the vault and submit a request to buy shares * @param assets Amount of `asset` to deposit diff --git a/nest/src/vault/NestAtomicQueue.sol b/nest/src/vault/NestAtomicQueue.sol index e69de29b..00206f96 100644 --- a/nest/src/vault/NestAtomicQueue.sol +++ b/nest/src/vault/NestAtomicQueue.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { AtomicQueue } from "@nucleus-boring-vault/base/AtomicQueue.sol"; +import { IComponentToken } from "../interfaces/IComponentToken.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract NestAtomicQueue is AtomicQueue, IComponentToken { + + // Constants + + uint256 public constant REQUEST_ID = 0; + + // Public State + + address public vault; + address public accountant; + uint256 public decimals; + IERC20 public asset; + uint256 public deadlinePeriod; + uint256 public pricePercentage; + + // Errors + + error Unimplemented(); + + // Constructor + + constructor( + address _vault, + address _accountant, + uint256 _decimals, + IERC20 _asset, + uint256 _deadlinePeriod, + uint256 _pricePercentage + ) { + vault = _vault; + accountant = _accountant; + decimals = _decimals; + asset = _asset; + deadlinePeriod = _deadlinePeriod; + pricePercentage = _pricePercentage; + } + + // Admin Setters + + function setVault(address _vault) requiresAuth external { + vault = _vault; + } + + function setAccountant(address _accountant) requiresAuth external { + accountant = _accountant; + } + + function setDecimals(uint256 _decimals) requiresAuth external { + decimals = _decimals; + } + + function setAsset(IERC20 _asset) requiresAuth external { + asset = _asset; + } + + // User Functions + + /** + * @notice Transfer assets from the owner into the vault and submit a request to buy shares + * @param assets Amount of `asset` to deposit + * @param controller Controller of the request + * @param owner Source of the assets to deposit + * @return requestId Discriminator between non-fungible requests + */ + function requestDeposit(uint256 assets, address controller, address owner) public returns (uint256 requestId) { + revert Unimplemented(); + } + + /** + * @notice Fulfill a request to buy shares by minting shares to the receiver + * @param assets Amount of `asset` that was deposited by `requestDeposit` + * @param receiver Address to receive the shares + * @param controller Controller of the request + */ + function deposit(uint256 assets, address receiver, address controller) public returns (uint256 shares) { + revert Unimplemented(); + } + + /** + * @notice Transfer shares from the owner into the vault and submit a request to redeem assets + * @param shares Amount of shares to redeem + * @param controller Controller of the request + * @param owner Source of the shares to redeem + * @return requestId Discriminator between non-fungible requests + */ + function requestRedeem(uint256 shares, address controller, address owner) public returns (uint256 requestId) { + if (owner == address(0)) { + revert InvalidOwner(); + } + if (controller == address(0)) { + revert InvalidController(); + } + + // Create and submit atomic request + IAtomicQueue.AtomicRequest memory request = IAtomicQueue.AtomicRequest({ + deadline: block.timestamp + this.deadlinePeriod, + atomicPrice: uint88(convertToAssets(10 ** this.decimals).mulDivDown(pricePercentage, 100)), // Price per share in terms of asset + offerAmount: uint96(shares), + inSolve: false + }); + + updateAtomicRequest(IERC20(this.vault), this.asset, request); + + emit RequestRedeem(shares, controller, owner); + + return REQUEST_ID; + } + + /** + * @notice Fulfill a request to redeem assets by transferring assets to the receiver + * @param shares Amount of shares that was redeemed by `requestRedeem` + * @param receiver Address to receive the assets + * @param controller Controller of the request + */ + function redeem(uint256 shares, address receiver, address controller) public returns (uint256 assets) { + // Redeem doesn't do anything anymore because as soon as the AtomicQueue request is processed, the msg.sender will receive their this.asset + revert Unimplemented(); + } + + // Getter View Functions + + /// @notice Address of the `asset` token + function asset() public view returns (address assetTokenAddress) { + return address(this.asset); + } + + function totalSupply() public view returns (uint256 totalSupply) { + return vault.totalSupply(); + } + + function balanceOf(address owner) public view returns (uint256 balance) { + return vault.balanceOf(owner); + } + + /** + * @notice Total value held in the vault + * @dev Example ERC20 implementation: return convertToAssets(totalSupply()) + */ + function totalAssets() public view returns (uint256 totalManagedAssets) { + return vault.convertToAssets(vault.totalSupply()); + } + + /** + * @notice Total value held by the given owner + * @dev Example ERC20 implementation: return convertToAssets(balanceOf(owner)) + * @param owner Address to query the balance of + * @return assets Total value held by the owner + */ + function assetsOf( + address owner + ) public view returns (uint256 assets) { + return vault.convertToAssets(vault.balanceOf(owner)); + } + + /// @inheritdoc ERC4626Upgradeable + function convertToShares( + uint256 assets + ) public view virtual override(ComponentToken) returns (uint256 shares) { + return assets.mulDivDown(10 ** this.decimals, this.accountant.getRateInQuote(this.asset)); + } + + /// @inheritdoc ERC4626Upgradeable + function convertToAssets( + uint256 shares + ) public view virtual override(ComponentToken) returns (uint256 assets) { + return shares.mulDivDown(this.accountant.getRateInQuote(this.asset), 10 ** this.decimals); + } + + /** + * @notice Total amount of assets sent to the vault as part of pending deposit requests + * @param requestId Discriminator between non-fungible requests + * @param controller Controller of the requests + * @return assets Amount of pending deposit assets for the given requestId and controller + */ + function pendingDepositRequest(uint256 requestId, address controller) public pure returns (uint256 assets) { + return 0; + } + + /** + * @notice Total amount of assets sitting in the vault as part of claimable deposit requests + * @param requestId Discriminator between non-fungible requests + * @param controller Controller of the requests + * @return assets Amount of claimable deposit assets for the given requestId and controller + */ + function claimableDepositRequest(uint256 requestId, address controller) public pure returns (uint256 assets) { + return 0; + } + + /** + * @notice Total amount of shares sent to the vault as part of pending redeem requests + * @param requestId Discriminator between non-fungible requests + * @param controller Controller of the requests + * @return shares Amount of pending redeem shares for the given requestId and controller + */ + function pendingRedeemRequest(uint256 requestId, address controller) public pure returns (uint256 shares) { + return 0; + } + + /** + * @notice Total amount of assets sitting in the vault as part of claimable redeem requests + * @param requestId Discriminator between non-fungible requests + * @param controller Controller of the requests + * @return shares Amount of claimable redeem shares for the given requestId and controller + */ + function claimableRedeemRequest(uint256 requestId, address controller) public pure returns (uint256 shares) { + return 0; + } + +} diff --git a/nest/src/vault/NestTeller.sol b/nest/src/vault/NestTeller.sol index 59d15cd5..469d7acd 100644 --- a/nest/src/vault/NestTeller.sol +++ b/nest/src/vault/NestTeller.sol @@ -6,11 +6,21 @@ import { FixedPointMathLib } from "@solmate/utils/FixedPointMathLib.sol"; contract NestTeller is MultiChainLayerZeroTellerWithMultiAssetSupport { + // Libraries + using FixedPointMathLib for uint256; + // Public State + address public asset; uint256 public minimumMintPercentage = _minimumMintPercentage; + // Errors + + error Unimplemented(); + + // Constructor + constructor( address _owner, address _vault, @@ -28,7 +38,7 @@ contract NestTeller is MultiChainLayerZeroTellerWithMultiAssetSupport { minimumMintPercentage = _minimumMintPercentage; } - error Unimplemented(); + // Admin Setters function setAsset(address _asset) requiresAuth external { asset = _asset;