Skip to content

Commit

Permalink
Merge pull request #63 from yieldnest/fix/erc4646-maxWithdraw
Browse files Browse the repository at this point in the history
Fixes YINE-c7850b7c-M02 and YINE-c7850b7c-M07
  • Loading branch information
danoctavian authored Jan 4, 2025
2 parents 9d9667c + 5cea29d commit 62f2505
Show file tree
Hide file tree
Showing 14 changed files with 680 additions and 190 deletions.
36 changes: 23 additions & 13 deletions script/bnb/deploy_ynbnbk.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,12 @@ contract DeployYnBNBkStrategy is BaseKernelScript, BatchScript {
function deployMigrateVault(bool isSafeTx) internal {
implementation = Vault(payable(address(new MigratedKernelStrategy())));

MigratedKernelStrategy.Asset[] memory assets = new MigratedKernelStrategy.Asset[](3);

assets[0] = MigratedKernelStrategy.Asset({asset: contracts.WBNB(), active: false});
assets[1] = MigratedKernelStrategy.Asset({asset: contracts.SLISBNB(), active: true});
assets[2] = MigratedKernelStrategy.Asset({asset: contracts.BNBX(), active: true});

if (isSafeTx) {
bytes memory initData = abi.encodeWithSelector(
MigratedKernelStrategy.initializeAndMigrate.selector,
actors_.ADMIN(),
"YieldNest Restaked BNB - Kernel",
symbol(),
assets,
contracts.STAKER_GATEWAY(),
0
);

Expand All @@ -115,8 +107,6 @@ contract DeployYnBNBkStrategy is BaseKernelScript, BatchScript {
msg.sender,
"YieldNest Restaked BNB - Kernel",
symbol(),
assets,
contracts.STAKER_GATEWAY(),
0
);

Expand Down Expand Up @@ -186,6 +176,14 @@ contract DeployYnBNBkStrategy is BaseKernelScript, BatchScript {

IStakerGateway stakerGateway = IStakerGateway(contracts.STAKER_GATEWAY());

vault_.setStakerGateway(contracts.STAKER_GATEWAY());
vault_.setSyncDeposit(true);
vault_.setSyncWithdraw(true);

vault_.addAsset(contracts.WBNB(), false);
vault_.addAsset(contracts.SLISBNB(), true);
vault_.addAsset(contracts.BNBX(), true);

vault_.addAssetWithDecimals(stakerGateway.getVault(contracts.WBNB()), 18, false);
vault_.addAssetWithDecimals(stakerGateway.getVault(contracts.SLISBNB()), 18, false);
vault_.addAssetWithDecimals(stakerGateway.getVault(contracts.BNBX()), 18, false);
Expand All @@ -206,9 +204,7 @@ contract DeployYnBNBkStrategy is BaseKernelScript, BatchScript {
addToBatch(
address(vault),
0,
abi.encodeWithSelector(
AccessControlUpgradeable.grantRole.selector, keccak256("DEFAULT_ADMIN_ROLE"), actors_.ADMIN()
)
abi.encodeWithSelector(AccessControlUpgradeable.grantRole.selector, bytes32(0), actors_.ADMIN())
);
addToBatch(
address(vault),
Expand Down Expand Up @@ -305,6 +301,15 @@ contract DeployYnBNBkStrategy is BaseKernelScript, BatchScript {
)
);
addToBatch(address(vault), 0, abi.encodeWithSelector(IVault.setProvider.selector, address(rateProvider)));

addToBatch(
address(vault),
0,
abi.encodeWithSelector(KernelStrategy.setStakerGateway.selector, contracts.STAKER_GATEWAY())
);
addToBatch(address(vault), 0, abi.encodeWithSelector(KernelStrategy.setSyncDeposit.selector, true));
addToBatch(address(vault), 0, abi.encodeWithSelector(KernelStrategy.setSyncWithdraw.selector, true));

addToBatch(
address(vault),
0,
Expand All @@ -323,6 +328,11 @@ contract DeployYnBNBkStrategy is BaseKernelScript, BatchScript {
AccessControlUpgradeable.grantRole.selector, keccak256("ASSET_MANAGER_ROLE"), actors_.ADMIN()
)
);

addToBatch(address(vault), 0, abi.encodeWithSelector(IVault.addAsset.selector, contracts.WBNB(), false));
addToBatch(address(vault), 0, abi.encodeWithSelector(IVault.addAsset.selector, contracts.SLISBNB(), true));
addToBatch(address(vault), 0, abi.encodeWithSelector(IVault.addAsset.selector, contracts.BNBX(), true));

addToBatch(
address(vault),
0,
Expand Down
2 changes: 1 addition & 1 deletion script/bnb/deploy_ynclisbnbk.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {BaseKernelScript} from "script/BaseKernelScript.sol";

import {console} from "lib/forge-std/src/console.sol";
import {KernelClisVaultViewer} from "src/utils/KernelClisVaultViewer.sol";
import {BaseVaultViewer, KernelVaultViewer} from "src/utils/KernelVaultViewer.sol";
import {KernelVaultViewer} from "src/utils/KernelVaultViewer.sol";

// FOUNDRY_PROFILE=mainnet forge script DeployYnclisBNBkStrategy --sender 0xd53044093F757E8a56fED3CCFD0AF5Ad67AeaD4a
contract DeployYnclisBNBkStrategy is BaseKernelScript {
Expand Down
16 changes: 16 additions & 0 deletions src/KernelClisStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ pragma solidity ^0.8.24;

import {KernelStrategy} from "./KernelStrategy.sol";

import {IERC20} from "lib/yieldnest-vault/src/Common.sol";
import {IWBNB} from "src/interface/external/IWBNB.sol";

import {IKernelConfig} from "src/interface/external/kernel/IKernelConfig.sol";
import {IStakerGateway} from "src/interface/external/kernel/IStakerGateway.sol";

/**
Expand Down Expand Up @@ -38,4 +41,17 @@ contract KernelClisStrategy is KernelStrategy {
//wrap native token
IWBNB(asset_).deposit{value: assets}();
}

function _availableAssets(address asset_) internal view virtual override returns (uint256 availableAssets) {
availableAssets = IERC20(asset_).balanceOf(address(this));

StrategyStorage storage strategyStorage = _getStrategyStorage();

if (strategyStorage.syncWithdraw && asset_ == asset()) {
IStakerGateway stakerGateway = IStakerGateway(strategyStorage.stakerGateway);
address clisbnb = IKernelConfig(stakerGateway.getConfig()).getClisBnbAddress();
uint256 availableAssetsInKernel = stakerGateway.balanceOf(clisbnb, address(this));
availableAssets += _convertAssetToBase(clisbnb, availableAssetsInKernel);
}
}
}
152 changes: 139 additions & 13 deletions src/KernelStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,52 @@ contract KernelStrategy is Vault {
* @return maxAssets The maximum amount of assets.
*/
function maxWithdraw(address owner) public view override returns (uint256 maxAssets) {
if (paused()) {
maxAssets = _maxWithdrawAsset(asset(), owner);
}

/**
* @notice Returns the maximum amount of assets that can be withdrawn for a specific asset by a given owner.
* @param asset_ The address of the asset.
* @param owner The address of the owner.
* @return maxAssets The maximum amount of assets.
*/
function maxWithdrawAsset(address asset_, address owner) public view returns (uint256 maxAssets) {
maxAssets = _maxWithdrawAsset(asset_, owner);
}

/**
* @notice Internal function to get the maximum amount of assets that can be withdrawn by a given owner.
* @param asset_ The address of the asset.
* @param owner The address of the owner.
* @return maxAssets The maximum amount of assets.
*/
function _maxWithdrawAsset(address asset_, address owner) internal view virtual returns (uint256 maxAssets) {
if (paused() || !_getAssetStorage().assets[asset_].active) {
return 0;
}

(maxAssets,) = _convertToAssets(asset(), balanceOf(owner), Math.Rounding.Floor);
uint256 availableAssets = _availableAssets(asset_);

maxAssets = previewRedeemAsset(asset_, balanceOf(owner));

maxAssets = availableAssets < maxAssets ? availableAssets : maxAssets;
}

/**
* @notice Internal function to get the available amount of assets.
* @param asset_ The address of the asset.
* @return availableAssets The available amount of assets.
*/
function _availableAssets(address asset_) internal view virtual returns (uint256 availableAssets) {
availableAssets = IERC20(asset_).balanceOf(address(this));

StrategyStorage storage strategyStorage = _getStrategyStorage();

if (strategyStorage.syncWithdraw) {
uint256 availableAssetsInKernel =
IStakerGateway(strategyStorage.stakerGateway).balanceOf(asset_, address(this));
availableAssets += availableAssetsInKernel;
}
}

/**
Expand All @@ -105,25 +146,47 @@ contract KernelStrategy is Vault {
* @return maxShares The maximum amount of shares.
*/
function maxRedeem(address owner) public view override returns (uint256 maxShares) {
if (paused()) {
return 0;
}
maxShares = _maxRedeemAsset(asset(), owner);
}

return balanceOf(owner);
/**
* @notice Returns the maximum amount of shares that can be redeemed by a given owner.
* @param asset_ The address of the asset.
* @param owner The address of the owner.
* @return maxShares The maximum amount of shares.
*/
function maxRedeemAsset(address asset_, address owner) public view returns (uint256 maxShares) {
maxShares = _maxRedeemAsset(asset_, owner);
}

/**
* @notice Returns the maximum amount of assets that can be withdrawn for a specific asset by a given owner.
* @notice Internal function to get the maximum amount of shares that can be redeemed by a given owner.
* @param asset_ The address of the asset.
* @param owner The address of the owner.
* @return maxAssets The maximum amount of assets.
* @return maxShares The maximum amount of shares.
*/
function maxWithdrawAsset(address asset_, address owner) public view returns (uint256 maxAssets) {
if (paused()) {
function _maxRedeemAsset(address asset_, address owner) internal view virtual returns (uint256 maxShares) {
if (paused() || !_getAssetStorage().assets[asset_].active) {
return 0;
}

(maxAssets,) = _convertToAssets(asset_, balanceOf(owner), Math.Rounding.Floor);
uint256 availableAssets = _availableAssets(asset_);

maxShares = balanceOf(owner);

maxShares = availableAssets < previewRedeemAsset(asset_, maxShares)
? previewWithdrawAsset(asset_, availableAssets)
: maxShares;
}

/**
* @notice Previews the amount of assets that would be required to mint a given amount of shares.
* @param asset_ The address of the asset.
* @param shares The amount of shares to mint.
* @return assets The equivalent amount of assets.
*/
function previewMintAsset(address asset_, uint256 shares) public view virtual returns (uint256 assets) {
(assets,) = _convertToAssets(asset_, shares, Math.Rounding.Ceil);
}

/**
Expand All @@ -145,8 +208,24 @@ contract KernelStrategy is Vault {
*/
function previewRedeemAsset(address asset_, uint256 shares) public view virtual returns (uint256 assets) {
(assets,) = _convertToAssets(asset_, shares, Math.Rounding.Floor);
assets = assets - _feeOnTotal(assets);
}

return assets - _feeOnTotal(assets);
/**
* @notice Withdraws a given amount of assets and burns the equivalent amount of shares from the owner.
* @param assets The amount of assets to withdraw.
* @param receiver The address of the receiver.
* @param owner The address of the owner.
* @return shares The equivalent amount of shares.
*/
function withdraw(uint256 assets, address receiver, address owner)
public
virtual
override
nonReentrant
returns (uint256 shares)
{
shares = _withdrawAsset(asset(), assets, receiver, owner);
}

/**
Expand All @@ -162,6 +241,21 @@ contract KernelStrategy is Vault {
virtual
nonReentrant
returns (uint256 shares)
{
shares = _withdrawAsset(asset_, assets, receiver, owner);
}

/**
* @notice Internal function for withdraws assets and burns equivalent shares from the owner.
* @param asset_ The address of the asset.
* @param assets The amount of assets to withdraw.
* @param receiver The address of the receiver.
* @param owner The address of the owner.
* @return shares The equivalent amount of shares burned.
*/
function _withdrawAsset(address asset_, uint256 assets, address receiver, address owner)
internal
returns (uint256 shares)
{
if (paused()) {
revert Paused();
Expand All @@ -174,6 +268,23 @@ contract KernelStrategy is Vault {
_withdrawAsset(asset_, _msgSender(), receiver, owner, assets, shares);
}

/**
* @notice Redeems a given amount of shares and transfers the equivalent amount of assets to the receiver.
* @param shares The amount of shares to redeem.
* @param receiver The address of the receiver.
* @param owner The address of the owner.
* @return assets The equivalent amount of assets.
*/
function redeem(uint256 shares, address receiver, address owner)
public
virtual
override
nonReentrant
returns (uint256 assets)
{
assets = _redeemAsset(asset(), shares, receiver, owner);
}

/**
* @notice Redeems shares and transfers equivalent assets to the receiver.
* @param asset_ The address of the asset.
Expand All @@ -187,11 +298,26 @@ contract KernelStrategy is Vault {
virtual
nonReentrant
returns (uint256 assets)
{
assets = _redeemAsset(asset_, shares, receiver, owner);
}

/**
* @notice Internal function for redeems shares and transfers equivalent assets to the receiver.
* @param asset_ The address of the asset.
* @param shares The amount of shares to redeem.
* @param receiver The address of the receiver.
* @param owner The address of the owner.
* @return assets The equivalent amount of assets.
*/
function _redeemAsset(address asset_, uint256 shares, address receiver, address owner)
internal
returns (uint256 assets)
{
if (paused()) {
revert Paused();
}
uint256 maxShares = maxRedeem(owner);
uint256 maxShares = maxRedeemAsset(asset_, owner);
if (shares > maxShares) {
revert ExceededMaxRedeem(owner, shares, maxShares);
}
Expand Down
Loading

0 comments on commit 62f2505

Please sign in to comment.