Skip to content

Commit

Permalink
Fixed issues found during review
Browse files Browse the repository at this point in the history
Fix failing test after updates to repair deflationary transfers
  • Loading branch information
xhad authored and danoctavian committed Mar 26, 2024
1 parent 5c2c675 commit 65f0fdb
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 57 deletions.
9 changes: 6 additions & 3 deletions src/LSDStakingNode.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,15 @@ contract LSDStakingNode is ILSDStakingNode, Initializable, ReentrancyGuardUpgrad
revert UnsupportedAsset(asset);
}

uint256 balanceBefore = asset.balanceOf(address(this));
ynLSD.retrieveAsset(nodeId, assets[i], amount);
uint256 balanceAfter = asset.balanceOf(address(this));
uint256 retrievedAmount = balanceAfter - balanceBefore;

asset.forceApprove(address(strategyManager), amount);
asset.forceApprove(address(strategyManager), retrievedAmount);

uint256 eigenShares = strategyManager.depositIntoStrategy(IStrategy(strategy), asset, amount);
emit DepositToEigenlayer(assets[i], strategy, amount, eigenShares);
uint256 eigenShares = strategyManager.depositIntoStrategy(IStrategy(strategy), asset, retrievedAmount);
emit DepositToEigenlayer(assets[i], strategy, retrievedAmount, eigenShares);
}
}

Expand Down
67 changes: 22 additions & 45 deletions src/ynLSD.sol
Original file line number Diff line number Diff line change
Expand Up @@ -171,39 +171,21 @@ contract ynLSD is IynLSD, ynBase, ReentrancyGuardUpgradeable, IynLSDEvents {
if (amount == 0) {
revert ZeroAmount();
}

uint256 previousTotalAssets = totalAssets();
uint256 assetAmountInETH = _transferAsset(asset, sender, amount);

// Convert the value of the asset deposited to ETH
uint256 assetAmountInETH = convertToETH(asset, amount);
// Calculate how many shares to be minted using the same formula as ynETH
shares = _convertToShares(assetAmountInETH, previousTotalAssets, Math.Rounding.Floor);
shares = _convertToShares(assetAmountInETH, Math.Rounding.Floor);

// Mint the calculated shares to the receiver
_mint(receiver, shares);
emit Deposit(sender, receiver, amount, shares);
}

/**
* @notice safeTransferFrom that returns ETH conversion and handles rebasing token deflation.
* @param asset The ERC20 asset to be transferred.
* @param sender The address of the sender.
* @param amount The amount of the asset to be transferred.
* @return assetAmountInETH The new balance of the users assets converted to ETH.
*/
function _transferAsset(IERC20 asset, address sender, uint256 amount) private returns (uint256 assetAmountInETH) {

uint256 previousBalance = asset.balanceOf(address(this));

// Transfer assets in after shares are computed since _convertToShares relies on totalAssets
// which inspects asset.balanceOf(address(this))
asset.safeTransferFrom(sender, address(this), amount);
uint256 balance = asset.balanceOf(address(this));

if (balance < previousBalance + amount) {
uint256 difference = previousBalance + amount - balance;
assetAmountInETH = convertToETH(asset, amount - difference);
} else {
assetAmountInETH = convertToETH(asset, amount);
}
}
emit Deposit(sender, receiver, amount, shares);
}

/**
* @dev Converts an ETH amount to shares based on the current exchange rate and specified rounding method.
Expand All @@ -212,11 +194,10 @@ contract ynLSD is IynLSD, ynBase, ReentrancyGuardUpgradeable, IynLSDEvents {
* This calculation can result in 0 during the bootstrap phase if `totalControlled` and `ynETHSupply` could be
* manipulated independently, which should not be possible.
* @param ethAmount The amount of ETH to convert to shares.
* @param preTotalAssets The total assets before the deposit.
* @param rounding The rounding method to use for the calculation.
* @return uint256 number of shares equivalent to the given ETH amount.
* @return The number of shares equivalent to the given ETH amount.
*/
function _convertToShares(uint256 ethAmount, uint256 preTotalAssets, Math.Rounding rounding) internal view returns (uint256) {
function _convertToShares(uint256 ethAmount, Math.Rounding rounding) internal view returns (uint256) {
// 1:1 exchange rate on the first stake.
// Use totalSupply to see if this is the bootstrap call, not totalAssets
if (totalSupply() == 0) {
Expand All @@ -227,23 +208,19 @@ contract ynLSD is IynLSD, ynBase, ReentrancyGuardUpgradeable, IynLSDEvents {

// Can only happen in bootstrap phase if `totalControlled` and `ynETHSupply` could be manipulated
// independently. That should not be possible.
uint256 shares = Math.mulDiv(
return Math.mulDiv(
ethAmount,
totalSupply(),
preTotalAssets,
totalAssets(),
rounding
);
}


return shares;
}

/**
* @notice Calculates the amount of shares to be minted for a given deposit.
* @param asset The asset to be deposited.
* @param amount The amount of asset to be deposited.
* @return uint256 of shares to be minted.
**/
/// @notice Calculates the amount of shares to be minted for a given deposit.
/// @param asset The asset to be deposited.
/// @param amount The amount of asset to be deposited.
/// @return The amount of shares to be minted.
function previewDeposit(IERC20 asset, uint256 amount) public view virtual returns (uint256) {
return convertToShares(asset, amount);
}
Expand All @@ -252,7 +229,7 @@ contract ynLSD is IynLSD, ynBase, ReentrancyGuardUpgradeable, IynLSDEvents {
* @notice This function calculates the total assets of the contract
* @dev It iterates over all the assets in the contract, gets the latest price for each asset from the oracle,
* multiplies it with the balance of the asset and adds it to the total
* @return uint256 The total assets of the contract in the form of uint
* @return total The total assets of the contract in the form of uint
*/
function totalAssets() public view returns (uint256) {
uint256 total = 0;
Expand All @@ -265,7 +242,7 @@ contract ynLSD is IynLSD, ynBase, ReentrancyGuardUpgradeable, IynLSDEvents {
return total;
}

/**
/**
* @notice Converts a given amount of a specific asset to shares
* @param asset The ERC-20 asset to be converted
* @param amount The amount of the asset to be converted
Expand All @@ -275,7 +252,7 @@ contract ynLSD is IynLSD, ynBase, ReentrancyGuardUpgradeable, IynLSDEvents {
IStrategy strategy = strategies[asset];
if(address(strategy) != address(0)){
uint256 assetAmountInETH = convertToETH(asset, amount);
shares = _convertToShares(assetAmountInETH, totalAssets(), Math.Rounding.Floor);
shares = _convertToShares(assetAmountInETH, Math.Rounding.Floor);
} else {
revert UnsupportedAsset(asset);
}
Expand Down Expand Up @@ -324,12 +301,12 @@ contract ynLSD is IynLSD, ynBase, ReentrancyGuardUpgradeable, IynLSDEvents {
* @dev This function takes into account the decimal places of the asset to ensure accurate conversion.
* @param asset The ERC20 token to be converted to ETH.
* @param amount The amount of the asset to be converted.
* @return uint256 equivalent amount of the asset in ETH.
* @return The equivalent amount of the asset in ETH.
*/
function convertToETH(IERC20 asset, uint amount) public view returns (uint256) {
uint256 assetPriceInETH = oracle.getLatestPrice(address(asset));
uint8 assetDecimals = IERC20Metadata(address(asset)).decimals();
return assetDecimals != 18
return assetDecimals < 18 || assetDecimals > 18
? assetPriceInETH * amount / (10 ** assetDecimals)
: assetPriceInETH * amount / 1e18;
}
Expand Down Expand Up @@ -462,7 +439,7 @@ contract ynLSD is IynLSD, ynBase, ReentrancyGuardUpgradeable, IynLSDEvents {
}

IERC20(asset).safeTransfer(msg.sender, amount);
emit AssetRetrieved(asset, IERC20(asset).balanceOf(msg.sender), nodeId, msg.sender);
emit AssetRetrieved(asset, amount, nodeId, msg.sender);
}

//--------------------------------------------------------------------------------------
Expand Down
5 changes: 4 additions & 1 deletion test/foundry/integration/ynLSD.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,10 @@ contract ynLSDAssetTest is IntegrationBaseTest {
uint256 expectedBalance = balanceInStrategyForNode * oraclePrice / 1e18;

// Assert that totalAssets reflects the deposit
assertEq(totalAssetsAfterDeposit - totalAssetsBeforeDeposit, expectedBalance, "Total assets do not reflect the deposit");
assertEq(
compareWithThreshold(totalAssetsAfterDeposit - totalAssetsBeforeDeposit,expectedBalance, 1), true,
"Total assets do not reflect the deposit"
);
}

function testPreviewDeposit() public {
Expand Down
8 changes: 0 additions & 8 deletions test/foundry/scenarios/ynLSD.spec.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ contract YnLSDScenarioTest1 is IntegrationBaseTest {
Users deposit random amounts
- Check the total assets of ynLSD
- Check the share balance of each user
- Check the total deposited in the pool
- Check total supply of ynLSD
*/

Expand All @@ -35,7 +34,6 @@ contract YnLSDScenarioTest1 is IntegrationBaseTest {
vm.assume(amount > 1 && amount < 10_000 ether);

uint256 previousTotalShares = ynlsd.totalSupply();
// uint256 previousTotalDeposited = ynlsd.totalAssets();
uint256 previousTotalAssets = ynlsd.getTotalAssets()[0];

vm.startPrank(user);
Expand All @@ -51,16 +49,13 @@ contract YnLSDScenarioTest1 is IntegrationBaseTest {

uint256 userShares = ynlsd.balanceOf(user);

// uint256 currentTotalDeposited = ynlsd.balances(asset);
uint256 currentTotalAssets = ynlsd.getTotalAssets()[0];
uint256 currentTotalShares = ynlsd.totalSupply();

runInvariants(
user,
// previousTotalDeposited,
previousTotalAssets,
previousTotalShares,
// currentTotalDeposited,
currentTotalAssets,
currentTotalShares,
userDeposit,
Expand All @@ -70,16 +65,13 @@ contract YnLSDScenarioTest1 is IntegrationBaseTest {

function runInvariants(
address user,
// uint256 previousTotalDeposited,
uint256 previousTotalAssets,
uint256 previousTotalShares,
// uint256 currentTotalDeposited,
uint256 currentTotalAssets,
uint256 currentTotalShares,
uint256 userDeposit,
uint256 userShares
) public view{
// Invariants.totalDepositIntegrity(currentTotalDeposited, previousTotalDeposited, userDeposit);
Invariants.totalAssetsIntegrity(currentTotalAssets, previousTotalAssets, userDeposit);
Invariants.shareMintIntegrity(currentTotalShares, previousTotalShares, userShares);
Invariants.userSharesIntegrity(ynlsd.balanceOf(user), 0, userShares);
Expand Down

0 comments on commit 65f0fdb

Please sign in to comment.