From f5ff472b2c32354998f1e2352f2a18bc8c2da775 Mon Sep 17 00:00:00 2001 From: ungaro Date: Fri, 17 Jan 2025 10:05:51 -0500 Subject: [PATCH 1/6] YieldToken::redeem yieldbuffer check --- nest/src/ComponentToken.sol | 2 +- smart-wallets/src/token/YieldToken.sol | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/nest/src/ComponentToken.sol b/nest/src/ComponentToken.sol index dcdf578..729a5bf 100644 --- a/nest/src/ComponentToken.sol +++ b/nest/src/ComponentToken.sol @@ -558,8 +558,8 @@ abstract contract ComponentToken is $.assetsRedeemRequest[controller] = 0; } else { // For sync redemptions, process normally - _burn(controller, shares); assets = convertToAssets(shares); + _burn(controller, shares); } SafeERC20.safeTransfer(IERC20(asset()), receiver, assets); diff --git a/smart-wallets/src/token/YieldToken.sol b/smart-wallets/src/token/YieldToken.sol index 4654e2f..b9d8b87 100644 --- a/smart-wallets/src/token/YieldToken.sol +++ b/smart-wallets/src/token/YieldToken.sol @@ -539,6 +539,8 @@ contract YieldToken is // Track managed assets $.totalManagedAssets -= assets; + _beforeWithdraw(assets); // Add this line to check yield buffer + if (!IERC20(asset()).transfer(receiver, assets)) { revert InsufficientBalance(IERC20(asset()), address(this), assets); } From ad1946db25ac3563382803cb6629420e648adc2e Mon Sep 17 00:00:00 2001 From: ungaro Date: Fri, 17 Jan 2025 10:13:09 -0500 Subject: [PATCH 2/6] transfer after converttoassets --- nest/src/ComponentToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nest/src/ComponentToken.sol b/nest/src/ComponentToken.sol index 729a5bf..8f681d9 100644 --- a/nest/src/ComponentToken.sol +++ b/nest/src/ComponentToken.sol @@ -419,8 +419,8 @@ abstract contract ComponentToken is $.claimableDepositRequest[controller] = 0; $.sharesDepositRequest[controller] = 0; } else { - SafeERC20.safeTransferFrom(IERC20(asset()), controller, address(this), assets); shares = convertToShares(assets); + SafeERC20.safeTransferFrom(IERC20(asset()), controller, address(this), assets); } _mint(receiver, shares); From efb3903aa8c51c54b7a32f3d1dfcdd665196c315 Mon Sep 17 00:00:00 2001 From: ungaro Date: Fri, 17 Jan 2025 10:24:06 -0500 Subject: [PATCH 3/6] burn shares on redeem --- smart-wallets/src/token/YieldToken.sol | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/smart-wallets/src/token/YieldToken.sol b/smart-wallets/src/token/YieldToken.sol index b9d8b87..d3acdbf 100644 --- a/smart-wallets/src/token/YieldToken.sol +++ b/smart-wallets/src/token/YieldToken.sol @@ -479,7 +479,7 @@ contract YieldToken is YieldTokenStorage storage $ = _getYieldTokenStorage(); - _burn(msg.sender, shares); + //_burn(msg.sender, shares); $.pendingRedeemRequest[controller] += shares; emit RedeemRequest(controller, owner, REQUEST_ID, owner, shares); @@ -539,7 +539,11 @@ contract YieldToken is // Track managed assets $.totalManagedAssets -= assets; - _beforeWithdraw(assets); // Add this line to check yield buffer + // Check yield buffer + _beforeWithdraw(assets); + + // Burn the shares, when we actually process the redemption + _burn(controller, shares); if (!IERC20(asset()).transfer(receiver, assets)) { revert InsufficientBalance(IERC20(asset()), address(this), assets); From 2b5cefb79554a04ad34690397fd2898290ac0953 Mon Sep 17 00:00:00 2001 From: ungaro Date: Mon, 20 Jan 2025 09:41:50 -0500 Subject: [PATCH 4/6] fix mint/withdraw functions --- smart-wallets/src/token/YieldToken.sol | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/smart-wallets/src/token/YieldToken.sol b/smart-wallets/src/token/YieldToken.sol index d3acdbf..609b3e8 100644 --- a/smart-wallets/src/token/YieldToken.sol +++ b/smart-wallets/src/token/YieldToken.sol @@ -348,8 +348,8 @@ contract YieldToken is revert InvalidCurrencyToken(currencyToken, _getYieldDistributionTokenStorage().currencyToken); } - $.yieldBuffer += amount; _depositYield(currencyTokenAmount); + $.yieldBuffer += amount; } /// @inheritdoc IComponentToken @@ -451,14 +451,20 @@ contract YieldToken is } YieldTokenStorage storage $ = _getYieldTokenStorage(); - assets = convertToAssets(shares); - if ($.claimableDepositRequest[controller] < assets) { - revert InsufficientRequestBalance(controller, assets, 1); + if ($.sharesDepositRequest[controller] < shares) { + revert InsufficientRequestBalance(controller, shares, 1); } + + // Calculate proportional assets based on requested shares + assets = $.claimableDepositRequest[controller].mulDivDown(shares, $.sharesDepositRequest[controller]); + $.claimableDepositRequest[controller] -= assets; $.sharesDepositRequest[controller] -= shares; + // Track managed assets + $.totalManagedAssets += assets; + _mint(receiver, shares); emit Deposit(controller, receiver, assets, shares); @@ -540,7 +546,7 @@ contract YieldToken is $.totalManagedAssets -= assets; // Check yield buffer - _beforeWithdraw(assets); + _beforeWithdraw(assets); // Burn the shares, when we actually process the redemption _burn(controller, shares); @@ -594,6 +600,9 @@ contract YieldToken is _beforeWithdraw(assets); + // Burn the shares when we actually process the withdrawal + _burn(controller, shares); + if (!IERC20(asset()).transfer(receiver, assets)) { revert InsufficientBalance(IERC20(asset()), address(this), assets); } From 11665a0a19c0f00fe415bf449f8a33f4ebe52476 Mon Sep 17 00:00:00 2001 From: ungaro Date: Mon, 20 Jan 2025 10:03:02 -0500 Subject: [PATCH 5/6] formatting & docs --- nest/src/AggregateToken.sol | 4 ++++ smart-wallets/src/token/YieldToken.sol | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/nest/src/AggregateToken.sol b/nest/src/AggregateToken.sol index 74f93e8..5ef364c 100644 --- a/nest/src/AggregateToken.sol +++ b/nest/src/AggregateToken.sol @@ -160,20 +160,24 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { /** * @inheritdoc IERC4626 * @dev 1:1 conversion rate between USDT and base asset + * @dev Always round DOWN for convertToShares for user safety */ function convertToShares( uint256 assets ) public view override(ComponentToken, IComponentToken) returns (uint256 shares) { + // Division rounds down by default return assets * _BASE / _getAggregateTokenStorage().askPrice; } /** * @inheritdoc IERC4626 * @dev 1:1 conversion rate between USDT and base asset + * @dev Always round DOWN for convertToAssets for user safety */ function convertToAssets( uint256 shares ) public view override(ComponentToken, IComponentToken) returns (uint256 assets) { + // Division rounds down by default return shares * _getAggregateTokenStorage().bidPrice / _BASE; } diff --git a/smart-wallets/src/token/YieldToken.sol b/smart-wallets/src/token/YieldToken.sol index 609b3e8..8936c7a 100644 --- a/smart-wallets/src/token/YieldToken.sol +++ b/smart-wallets/src/token/YieldToken.sol @@ -290,7 +290,10 @@ contract YieldToken is return convertToAssets(balanceOf(owner)); } - /// @inheritdoc IERC4626 + /** + * @inheritdoc IERC4626 + * @dev Always rounds down for user safety when converting assets to shares + */ function convertToShares( uint256 assets ) public view override(ERC4626Upgradeable, IComponentToken) returns (uint256 shares) { @@ -302,7 +305,10 @@ contract YieldToken is return (assets * supply) / totalAssets_; } - /// @inheritdoc IERC4626 + /** + * @inheritdoc IERC4626 + * @dev Always rounds down for user safety when converting shares to assets + */ function convertToAssets( uint256 shares ) public view override(ERC4626Upgradeable, IComponentToken) returns (uint256 assets) { From 15ac29b7781c75e9b00927cca4b5db0999dc8bc0 Mon Sep 17 00:00:00 2001 From: ungaro Date: Mon, 20 Jan 2025 10:05:14 -0500 Subject: [PATCH 6/6] remove comment --- smart-wallets/src/token/YieldToken.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/smart-wallets/src/token/YieldToken.sol b/smart-wallets/src/token/YieldToken.sol index 8936c7a..8bba99e 100644 --- a/smart-wallets/src/token/YieldToken.sol +++ b/smart-wallets/src/token/YieldToken.sol @@ -491,7 +491,6 @@ contract YieldToken is YieldTokenStorage storage $ = _getYieldTokenStorage(); - //_burn(msg.sender, shares); $.pendingRedeemRequest[controller] += shares; emit RedeemRequest(controller, owner, REQUEST_ID, owner, shares);