Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
ungaro committed Dec 10, 2024
2 parents 138a1d8 + d6ffde7 commit 7be2f03
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 68 deletions.
10 changes: 5 additions & 5 deletions nest/script/DeployNestContracts.s.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Script } from "forge-std/Script.sol";
import { Test } from "forge-std/Test.sol";
Expand All @@ -13,6 +12,8 @@ import { NestStaking } from "../src/NestStaking.sol";
import { IComponentToken } from "../src/interfaces/IComponentToken.sol";
import { AggregateTokenProxy } from "../src/proxy/AggregateTokenProxy.sol";
import { NestStakingProxy } from "../src/proxy/NestStakingProxy.sol";

import { pUSDProxy } from "../src/proxy/pUSDProxy.sol";
import { pUSD } from "../src/token/pUSD.sol";

// Concrete implementation of ComponentToken
Expand Down Expand Up @@ -43,7 +44,6 @@ contract DeployNestContracts is Script, Test {

function run() external {
vm.startBroadcast(NEST_ADMIN_ADDRESS);
ERC1967Proxy pUSDProxy = ERC1967Proxy(payable(PUSD_ADDRESS));

AggregateToken aggregateToken = new AggregateToken();
AggregateTokenProxy aggregateTokenProxy = new AggregateTokenProxy(
Expand All @@ -52,9 +52,9 @@ contract DeployNestContracts is Script, Test {
AggregateToken.initialize,
(
NEST_ADMIN_ADDRESS,
"Nest Insto Vault",
"NIV",
IComponentToken(address(pUSDProxy)),
"Nest RWA Vault",
"nRWA",
IComponentToken(PUSD_ADDRESS),
1e17, // ask price
1e17 // bid price
)
Expand Down
4 changes: 3 additions & 1 deletion nest/script/UpgradeNestContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import { AggregateTokenProxy } from "../src/proxy/AggregateTokenProxy.sol";
contract UpgradeNestContracts is Script, Test {

address private constant NEST_ADMIN_ADDRESS = 0xb015762405De8fD24d29A6e0799c12e0Ea81c1Ff;
address private constant BORING_VAULT_ADDRESS = 0xe644F07B1316f28a7F134998e021eA9f7135F351;

UUPSUpgradeable private constant AGGREGATE_TOKEN_PROXY =
UUPSUpgradeable(payable(0x659619AEdf381c3739B0375082C2d61eC1fD8835));

// Add the component token addresses
address private constant ASSET_TOKEN = 0xF66DFD0A9304D3D6ba76Ac578c31C84Dc0bd4A00;
address private constant ASSET_TOKEN = 0x2DEc3B6AdFCCC094C31a2DCc83a43b5042220Ea2;

// LiquidContinuousMultiTokenVault - Credbull
address private constant COMPONENT_TOKEN = 0x4B1fC984F324D2A0fDD5cD83925124b61175f5C6;
Expand Down
11 changes: 7 additions & 4 deletions nest/script/UpgradepUSD.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";

import { Script } from "forge-std/Script.sol";
import { Test } from "forge-std/Test.sol";
import { console2 } from "forge-std/console2.sol";

import { pUSDProxy } from "../src/proxy/pUSDProxy.sol";
import { pUSD } from "../src/token/pUSD.sol";

import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";

contract UpgradePUSD is Script, Test {
contract UpgradePUSD is Script {

// Constants
address private constant ADMIN_ADDRESS = 0xb015762405De8fD24d29A6e0799c12e0Ea81c1Ff;
Expand All @@ -35,6 +34,9 @@ contract UpgradePUSD is Script, Test {
uint256 public currentTotalSupply;
bool public isConnected;

// small hack to be excluded from coverage report
function test() public { }

function setUp() public {
// Try to read implementation slot from proxy, this only works with RPC
try vm.load(PUSD_PROXY, ERC1967Utils.IMPLEMENTATION_SLOT) returns (bytes32 implementation) {
Expand All @@ -56,12 +58,13 @@ contract UpgradePUSD is Script, Test {
console2.log("Vault:", currentVault);
console2.log("Total Supply:", currentTotalSupply);
} else {
vm.skip(true);
//TODO: Check this again
vm.skip(false);
isConnected = false;
}
} catch {
console2.log("No implementation found - skipping");
vm.skip(true);
vm.skip(false);
isConnected = false;
}
}
Expand Down
100 changes: 98 additions & 2 deletions nest/src/AggregateToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity ^0.8.25;
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";

import { ComponentToken } from "./ComponentToken.sol";
import { IAggregateToken } from "./interfaces/IAggregateToken.sol";
Expand Down Expand Up @@ -55,6 +57,9 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder {
/// @notice Emitted when the AggregateToken contract is unpaused for deposits
event Unpaused();

/// @notice Emitted when the asset token is updated
event AssetTokenUpdated(IERC20 indexed oldAsset, IERC20 indexed newAsset);

// Errors

/**
Expand All @@ -63,6 +68,9 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder {
*/
error ComponentTokenAlreadyListed(IComponentToken componentToken);

/// @notice Emitted when a ComponentToken is removed from the component token list
event ComponentTokenRemoved(IComponentToken indexed componentToken);

/**
* @notice Indicates a failure because the ComponentToken is not in the component token list
* @param componentToken ComponentToken that is not in the component token list
Expand Down Expand Up @@ -124,7 +132,7 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder {
uint256 askPrice,
uint256 bidPrice
) public initializer {
super.initialize(owner, name, symbol, IERC20(address(asset_)), false, false);
super.initialize(owner, name, symbol, IERC20(address(asset_)), false, true);

AggregateTokenStorage storage $ = _getAggregateTokenStorage();
$.componentTokenList.push(asset_);
Expand Down Expand Up @@ -166,13 +174,66 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder {
uint256 assets,
address receiver,
address controller
) public override(ComponentToken, IComponentToken) returns (uint256 shares) {
) public override(ComponentToken, IComponentToken, ERC4626Upgradeable) returns (uint256 shares) {
if (_getAggregateTokenStorage().paused) {
revert DepositPaused();
}
return super.deposit(assets, receiver, controller);
}

/**
* @inheritdoc ERC4626Upgradeable
* @dev Overridden to add pause check before deposit
* @param assets Amount of assets to deposit
* @param receiver Address that will receive the shares
* @return shares Amount of shares minted
*/
function deposit(
uint256 assets,
address receiver
) public virtual override(ERC4626Upgradeable) returns (uint256 shares) {
if (_getAggregateTokenStorage().paused) {
revert DepositPaused();
}
return super.deposit(assets, receiver);
}

/**
* @inheritdoc ComponentToken
* @dev Overridden to add pause check before minting
* @param shares Amount of shares to mint
* @param receiver Address that will receive the shares
* @param controller Address that controls the minting
* @return assets Amount of assets deposited
*/
function mint(
uint256 shares,
address receiver,
address controller
) public virtual override(ComponentToken) returns (uint256 assets) {
if (_getAggregateTokenStorage().paused) {
revert DepositPaused();
}
return super.mint(shares, receiver, controller);
}

/**
* @inheritdoc ERC4626Upgradeable
* @dev Overridden to add pause check before minting
* @param shares Amount of shares to mint
* @param receiver Address that will receive the shares
* @return assets Amount of assets deposited
*/
function mint(
uint256 shares,
address receiver
) public virtual override(ERC4626Upgradeable) returns (uint256 assets) {
if (_getAggregateTokenStorage().paused) {
revert DepositPaused();
}
return super.mint(shares, receiver);
}

/// @inheritdoc IComponentToken
function redeem(
uint256 shares,
Expand Down Expand Up @@ -223,6 +284,41 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder {
emit ComponentTokenListed(componentToken);
}

/**
* @notice Remove a ComponentToken from the component token list
* @dev Only the owner can call this function. The ComponentToken must have zero balance to be removed.
* @param componentToken ComponentToken to remove
*/
function removeComponentToken(
IComponentToken componentToken
) external nonReentrant onlyRole(ADMIN_ROLE) {
AggregateTokenStorage storage $ = _getAggregateTokenStorage();

// Check if component token exists
if (!$.componentTokenMap[componentToken]) {
revert ComponentTokenNotListed(componentToken);
}

// Check if it's the current asset
if (address(componentToken) == asset()) {
revert ComponentTokenIsAsset(componentToken);
}

// Remove from mapping
$.componentTokenMap[componentToken] = false;

// Remove from array by finding and replacing with last element
for (uint256 i = 0; i < $.componentTokenList.length; i++) {
if ($.componentTokenList[i] == componentToken) {
$.componentTokenList[i] = $.componentTokenList[$.componentTokenList.length - 1];
$.componentTokenList.pop();
break;
}
}

emit ComponentTokenUnlisted(componentToken);
}

/**
* @notice Buy ComponentToken using `asset`
* @dev Only the owner can call this function, will revert if
Expand Down
75 changes: 27 additions & 48 deletions nest/src/ComponentToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/
import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

Expand Down Expand Up @@ -293,9 +294,7 @@ abstract contract ComponentToken is
revert Unimplemented();
}

if (!IERC20(asset()).transferFrom(owner, address(this), assets)) {
revert InsufficientBalance(IERC20(asset()), owner, assets);
}
SafeERC20.safeTransferFrom(IERC20(asset()), owner, address(this), assets);
$.pendingDepositRequest[controller] += assets;

emit DepositRequest(controller, owner, REQUEST_ID, owner, assets);
Expand Down Expand Up @@ -359,10 +358,7 @@ abstract contract ComponentToken is
$.claimableDepositRequest[controller] = 0;
$.sharesDepositRequest[controller] = 0;
} else {
// For sync deposits, process normally
if (!IERC20(asset()).transferFrom(controller, address(this), assets)) {
revert InsufficientBalance(IERC20(asset()), controller, assets);
}
SafeERC20.safeTransferFrom(IERC20(asset()), controller, address(this), assets);
shares = convertToShares(assets);
}

Expand All @@ -385,23 +381,22 @@ abstract contract ComponentToken is
}

ComponentTokenStorage storage $ = _getComponentTokenStorage();
assets = convertToAssets(shares);

if ($.asyncDeposit) {
if ($.claimableDepositRequest[controller] < assets) {
revert InsufficientRequestBalance(controller, assets, 1);
// Check shares directly instead of converting to assets
if ($.sharesDepositRequest[controller] < shares) {
revert InsufficientRequestBalance(controller, shares, 1);
}
$.claimableDepositRequest[controller] -= assets;
$.sharesDepositRequest[controller] -= shares;
// Use the pre-calculated assets amount from when deposit was notified
assets = $.claimableDepositRequest[controller];
$.claimableDepositRequest[controller] = 0;
$.sharesDepositRequest[controller] = 0;
} else {
if (!IERC20(asset()).transferFrom(controller, address(this), assets)) {
revert InsufficientBalance(IERC20(asset()), controller, assets);
}
assets = previewMint(shares);
_deposit(msg.sender, receiver, assets, shares);
}

_mint(receiver, shares);

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

/// @inheritdoc IComponentToken
Expand Down Expand Up @@ -498,9 +493,7 @@ abstract contract ComponentToken is
assets = convertToAssets(shares);
}

if (!IERC20(asset()).transfer(receiver, assets)) {
revert InsufficientBalance(IERC20(asset()), address(this), assets);
}
SafeERC20.safeTransfer(IERC20(asset()), receiver, assets);

emit Withdraw(controller, receiver, controller, assets, shares);
return assets;
Expand Down Expand Up @@ -554,42 +547,28 @@ abstract contract ComponentToken is
address receiver,
address controller
) public virtual override(ERC4626Upgradeable, IERC7540) nonReentrant returns (uint256 shares) {
if (assets == 0) {
if (shares == 0) {
revert ZeroAmount();
}
if (msg.sender != controller) {
revert Unauthorized(msg.sender, controller);
}

ComponentTokenStorage storage $ = _getComponentTokenStorage();

if ($.asyncRedeem) {
// For async redemptions, we must use the full claimable amount
uint256 claimableShares = $.claimableRedeemRequest[controller];
uint256 claimableAssets = $.assetsRedeemRequest[controller];

if (claimableShares == 0 || claimableAssets == 0) {
revert NoClaimableRedeem();
}
if (assets != claimableAssets) {
revert InvalidRedeemAmount(convertToShares(assets), claimableShares);
// Use the pre-calculated assets amount from when redeem was notified
if ($.assetsRedeemRequest[controller] < assets) {
revert InsufficientRequestBalance(controller, assets, 3);
}

shares = claimableShares;

// Reset state atomically
shares = $.claimableRedeemRequest[controller];
$.claimableRedeemRequest[controller] = 0;
$.assetsRedeemRequest[controller] = 0;
} else {
shares = convertToShares(assets);
_burn(controller, shares);
}

if (!IERC20(asset()).transfer(receiver, assets)) {
revert InsufficientBalance(IERC20(asset()), address(this), assets);
shares = previewWithdraw(assets);
_withdraw(msg.sender, receiver, msg.sender, assets, shares);
}

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

Expand Down Expand Up @@ -649,7 +628,7 @@ abstract contract ComponentToken is
if (_getComponentTokenStorage().asyncDeposit) {
revert Unimplemented();
}
assets = super.previewDeposit(shares);
assets = convertToAssets(shares);
}

/**
Expand All @@ -676,7 +655,7 @@ abstract contract ComponentToken is
if (_getComponentTokenStorage().asyncRedeem) {
revert Unimplemented();
}
shares = super.previewWithdraw(assets);
shares = convertToShares(assets);
}

/// @inheritdoc IERC7540
Expand Down
Loading

0 comments on commit 7be2f03

Please sign in to comment.