Platform: Code4rena
Start Date: 24/03/2023
Pot Size: $49,200 USDC
Total HM: 20
Participants: 246
Period: 6 days
Judge: Picodes
Total Solo HM: 1
Id: 226
League: ETH
Rank: 36/246
Findings: 6
Award: $194.69
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: ladboy233
Also found by: 0xkazim, 0xnev, Bauer, J4de, Matin, UniversalCrypto, cryptothemex, jasonxiale, juancito, koxuan, latt1ce, neumo
63.2128 USDC - $63.21
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L173 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L74
When Calcuting the minOut before doing trade,
Division before multiplication truncate minOut and incurs heavy precision loss, then very sub-optimal amount of the trade output can result in loss of fund from user because of the insufficient slippage protection
In the current implementation,
slippage can be set by calling
/** @notice - Sets the max slippage for a certain derivative index @param _derivativeIndex - index of the derivative you want to update the slippage @param _slippage - new slippage amount in wei */ function setMaxSlippage( uint _derivativeIndex, uint _slippage ) external onlyOwner { derivatives[_derivativeIndex].setMaxSlippage(_slippage); emit SetMaxSlippage(_derivativeIndex, _slippage); }
which calls the corresponding derivative contract
/** @notice - Owner only function to set max slippage for derivative @param _slippage - new slippage amount in wei */ function setMaxSlippage(uint256 _slippage) external onlyOwner { maxSlippage = _slippage; }
calling
uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) * ((10 ** 18 - maxSlippage))) / 10 ** 18); IWETH(W_ETH_ADDRESS).deposit{value: msg.value}(); uint256 amountSwapped = swapExactInputSingleHop( W_ETH_ADDRESS, rethAddress(), 500, msg.value, minOut );
As we can see, the division before multiplication happens in the line of code
uint256 minOut = ((((rethPerEth * msg.value) / 10 ** 18) * ((10 ** 18 - maxSlippage))) / 10 ** 18);
for example, if maxSlippage is 10 ** 17
(10 ** 18 - 10 ** 17) / (10 ** 18) = 0
then minOut is 0, slippage control is disabled because of the division before multipcation.
/** @notice - Owner only function to set max slippage for derivative */ function setMaxSlippage(uint256 _slippage) external onlyOwner { maxSlippage = _slippage; }
the divison before multipilcation happens below
uint256 minOut = (((ethPerDerivative(_amount) * _amount) / 10 ** 18) * (10 ** 18 - maxSlippage)) / 10 ** 18; IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange( 1, 0, frxEthBalance, minOut );
for example, if maxSlippage is 10 ** 17
(10 ** 18 - 10 ** 17) / (10 ** 18) = 0
then minOut is 0, slippage control is disabled because of the division before multipcation.
Manual Review
We recommend the protocol avoid division before multiplcation when calcaluting the minOut to enable slippage protection and avoid front-running.
#0 - c4-pre-sort
2023-04-02T09:23:06Z
0xSorryNotSorry marked the issue as high quality report
#1 - c4-pre-sort
2023-04-04T16:37:51Z
0xSorryNotSorry marked the issue as duplicate of #1044
#2 - c4-judge
2023-04-22T10:27:52Z
Picodes marked the issue as selected for report
#3 - Picodes
2023-04-22T10:31:38Z
Note that there is a multiplication before the division, so the loss of precision is significant only if msg.value
is small
🌟 Selected for report: brgltd
Also found by: 0xbepresent, 0xepley, 0xnev, BPZ, Breeje, Polaris_tow, SadBase, SaeedAlipoor01988, eyexploit, ladboy233, latt1ce, peanuts, rbserver
40.6368 USDC - $40.64
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L91 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L118
Lack of slippage control and deadline check when staking and unstaking
The protocol implemented slippage control but the slippage control is used when swapping asset either via uniswap pool or curve pool and does not has deadline check or slippage protection when staking and unstaking.
This is problematic because since the protocol is deployed to mainnet ethereum,
let us say for example, the user call stake function and want to deposit 1 ETH, he submitted a transaction with reasonable gas price, and the transaction is pending in the mempool to be executed.
Then there is a gas spike and high gas usage and the user's staking transaction is pending in the mempool for a long time.
assume the user will deposit the fund into LIDO finance, we are calling:
(bool sent, ) = WST_ETH.call{value: msg.value}(""); require(sent, "Failed to send Ether");
calling
/** * @notice Shortcut to stake ETH and auto-wrap returned stETH */ receive() external payable { uint256 shares = stETH.submit{value: msg.value}(address(0)); _mint(msg.sender, shares); }
calling stETH.submit
/** * @notice Send funds to the pool with optional _referral parameter * @dev This function is alternative way to submit funds. Supports optional referral address. * @return Amount of StETH shares generated */ function submit(address _referral) external payable returns (uint256) { return _submit(_referral); }
calling
function _submit(address _referral) internal returns (uint256) { require(msg.value != 0, "ZERO_DEPOSIT"); StakeLimitState.Data memory stakeLimitData = STAKING_STATE_POSITION.getStorageStakeLimitStruct(); require(!stakeLimitData.isStakingPaused(), "STAKING_PAUSED"); if (stakeLimitData.isStakingLimitSet()) { uint256 currentStakeLimit = stakeLimitData.calculateCurrentStakeLimit(); require(msg.value <= currentStakeLimit, "STAKE_LIMIT"); STAKING_STATE_POSITION.setStorageStakeLimitStruct( stakeLimitData.updatePrevStakeLimit(currentStakeLimit - msg.value) ); } uint256 sharesAmount = getSharesByPooledEth(msg.value); if (sharesAmount == 0) { // totalControlledEther is 0: either the first-ever deposit or complete slashing // assume that shares correspond to Ether 1-to-1 sharesAmount = msg.value; } _mintShares(msg.sender, sharesAmount); BUFFERED_ETHER_POSITION.setStorageUint256(_getBufferedEther().add(msg.value)); emit Submitted(msg.sender, msg.value, _referral); _emitTransferAfterMintingShares(msg.sender, sharesAmount); return sharesAmount; }
note the code section:
uint256 sharesAmount = getSharesByPooledEth(msg.value); if (sharesAmount == 0) { // totalControlledEther is 0: either the first-ever deposit or complete slashing // assume that shares correspond to Ether 1-to-1 sharesAmount = msg.value; } _mintShares(msg.sender, sharesAmount);
which calls:
/** * @return the amount of shares that corresponds to `_ethAmount` protocol-controlled Ether. */ function getSharesByPooledEth(uint256 _ethAmount) public view returns (uint256) { uint256 totalPooledEther = _getTotalPooledEther(); if (totalPooledEther == 0) { return 0; } else { return _ethAmount .mul(_getTotalShares()) .div(totalPooledEther); } }
as we can see the share minted is dependent on total shares and total pooled ether.
because it is possible the user's staking transaction is pending in the mempool for a long time and there can be a transaction landed that inflate the totalPooledEther to a high number because of the math ethAmount * totalShares / totalpooledEther, user's minted share via stake is greatedly diluted.
This is acutally the same case for withdraw transaction, the slippage control only applies to swap function but does not apply to how many share user get when depositing or how much asset user redeem via shares.
Manual Review
We recommend the protocol add deadline check for deposit and withdraw and add slippage control to let user specify the minShareReceived and minAssetReceived when deposit or withdraw from the underlying protocol.
#0 - c4-pre-sort
2023-04-04T21:12:20Z
0xSorryNotSorry marked the issue as primary issue
#1 - elmutt
2023-04-07T20:26:09Z
user specifying minOut is a good idea. We have seen this suggestion a couple times
#2 - c4-sponsor
2023-04-07T20:26:19Z
elmutt marked the issue as sponsor confirmed
#3 - c4-judge
2023-04-24T18:25:00Z
Picodes marked the issue as duplicate of #932
#4 - c4-judge
2023-04-24T21:20:59Z
Picodes marked the issue as satisfactory
🌟 Selected for report: silviaxyz
Also found by: 0xMirce, 0xbepresent, CodingNameKiki, Franfran, HollaDieWaldfee, MiloTruck, Tricko, adriro, codeislight, cryptonue, d3e4, ladboy233, rbserver, shaka, volodya
28.8013 USDC - $28.80
Lack of consideration for LIDO finance deposit limit cap
In the current integration with Rocket Protocol, the code consider the deposit limit cap in Reth#deposit
If the logic is, if the deposit limit cap is reached, the code will aim to swap token for RETH in the Uniswap Trading pool, otherwise, deposit can getting the share token.
However, the current integration with LIDO finance WstETH failed to consider the deposit cap
LIDO finance introduces the deposit cap as well
In implementation detail of the deposit cap limit is shown below
This is the token address for stETH
https://etherscan.io/token/0xae7ab96520de3a18e5e111b5eaab095312d7fe84#writeProxyContract
which is a proxy contract and the implementation address point to
https://etherscan.io/address/0x47ebab13b806773ec2a2d16873e2df770d130b50#code
We can see there is a function
https://etherscan.io/address/0x47ebab13b806773ec2a2d16873e2df770d130b50#code#F1#L250
/** * @notice Returns full info about current stake limit params and state * @dev Might be used for the advanced integration requests. * @return isStakingPaused staking pause state (equivalent to return of isStakingPaused()) * @return isStakingLimitSet whether the stake limit is set * @return currentStakeLimit current stake limit (equivalent to return of getCurrentStakeLimit()) * @return maxStakeLimit max stake limit * @return maxStakeLimitGrowthBlocks blocks needed to restore max stake limit from the fully exhausted state * @return prevStakeLimit previously reached stake limit * @return prevStakeBlockNumber previously seen block number */ function getStakeLimitFullInfo() external view returns ( bool isStakingPaused, bool isStakingLimitSet, uint256 currentStakeLimit, uint256 maxStakeLimit, uint256 maxStakeLimitGrowthBlocks, uint256 prevStakeLimit, uint256 prevStakeBlockNumber )
the deposit limit cap refers to maxStakeLimit and currentStakeLimit and maxStakeLimitGrowthBlocks
if the deposit limit is breached, transaction will revert
function _submit(address _referral) internal returns (uint256) { require(msg.value != 0, "ZERO_DEPOSIT"); StakeLimitState.Data memory stakeLimitData = STAKING_STATE_POSITION.getStorageStakeLimitStruct(); require(!stakeLimitData.isStakingPaused(), "STAKING_PAUSED"); if (stakeLimitData.isStakingLimitSet()) { uint256 currentStakeLimit = stakeLimitData.calculateCurrentStakeLimit(); require(msg.value <= currentStakeLimit, "STAKE_LIMIT"); STAKING_STATE_POSITION.setStorageStakeLimitStruct( stakeLimitData.updatePrevStakeLimit(currentStakeLimit - msg.value) ); } uint256 sharesAmount = getSharesByPooledEth(msg.value); if (sharesAmount == 0) { // totalControlledEther is 0: either the first-ever deposit or complete slashing // assume that shares correspond to Ether 1-to-1 sharesAmount = msg.value; } _mintShares(msg.sender, sharesAmount); BUFFERED_ETHER_POSITION.setStorageUint256(_getBufferedEther().add(msg.value)); emit Submitted(msg.sender, msg.value, _referral); _emitTransferAfterMintingShares(msg.sender, sharesAmount); return sharesAmount; }
note the check that revert the transaction.
if (stakeLimitData.isStakingLimitSet()) { uint256 currentStakeLimit = stakeLimitData.calculateCurrentStakeLimit(); require(msg.value <= currentStakeLimit, "STAKE_LIMIT"); STAKING_STATE_POSITION.setStorageStakeLimitStruct( stakeLimitData.updatePrevStakeLimit(currentStakeLimit - msg.value) ); }
Manual Review
We recommend the protocol consider the deposit limit cap for LIDO finance integration by implmenting the same approach to handle the Rocket protocol deposit cap, if the LIDO finance deposit limit is breached, swap token in external Uniswap pool to get the share token for user instead of letting the transaction revert.
#0 - c4-pre-sort
2023-04-04T18:57:49Z
0xSorryNotSorry marked the issue as duplicate of #812
#1 - c4-judge
2023-04-24T19:43:35Z
Picodes marked the issue as satisfactory
🌟 Selected for report: __141345__
Also found by: 0xWaitress, 0xbepresent, AkshaySrivastav, Bauer, Haipls, HollaDieWaldfee, Lirios, MiloTruck, SaeedAlipoor01988, UdarTeam, adriro, bytes032, ck, d3e4, hihen, hl_, kaden, ladboy233, lopotras, m_Rassska, peanuts, reassor, volodya
8.2654 USDC - $8.27
Deposit from Asymmetry finance would revert if external protocol's deposit is paused
In the current implementation, user needs to call SafETH.sol#stake to deposit ETH into the external protocol to generate yield.
The current external protocol that are integrated are frax ETH liquid staking, LIDO finance and rocket protocol
However, frax ETH protocol or LIDO finance protocol can pause the deposit
We are calling staking ->
uint256 totalStakeValueEth = 0; // total amount of derivatives worth of ETH in system for (uint i = 0; i < derivativeCount; i++) { uint256 weight = weights[i]; IDerivative derivative = derivatives[i]; if (weight == 0) continue; uint256 ethAmount = (msg.value * weight) / totalWeight; // This is slightly less than ethAmount because slippage uint256 depositAmount = derivative.deposit{value: ethAmount}(); uint derivativeReceivedEthValue = (derivative.ethPerDerivative( depositAmount ) * depositAmount) / 10 ** 18; totalStakeValueEth += derivativeReceivedEthValue; }
calling derivative.deposit{value: ethAmount}()
if the derivative is sFrxETH.sol, we are calling
/** @notice - Owner only function to Deposit into derivative @dev - Owner is set to SafEth contract */ function deposit() external payable onlyOwner returns (uint256) { IFrxETHMinter frxETHMinterContract = IFrxETHMinter( FRX_ETH_MINTER_ADDRESS ); uint256 sfrxBalancePre = IERC20(SFRX_ETH_ADDRESS).balanceOf( address(this) ); frxETHMinterContract.submitAndDeposit{value: msg.value}(address(this)); uint256 sfrxBalancePost = IERC20(SFRX_ETH_ADDRESS).balanceOf( address(this) ); return sfrxBalancePost - sfrxBalancePre; }
calling frxETHMinterContract.submitAndDeposit{value: msg.value}(address(this));
/// @notice Mint frxETH and deposit it to receive sfrxETH in one transaction /** @dev Could try using EIP-712 / EIP-2612 here in the future if you replace this contract, but you might run into msg.sender vs tx.origin issues with the ERC4626 */ function submitAndDeposit(address recipient) external payable returns (uint256 shares) { // Give the frxETH to this contract after it is generated _submit(address(this)); // Approve frxETH to sfrxETH for staking frxETHToken.approve(address(sfrxETHToken), msg.value); // Deposit the frxETH and give the generated sfrxETH to the final recipient uint256 sfrxeth_recieved = sfrxETHToken.deposit(msg.value, recipient); require(sfrxeth_recieved > 0, 'No sfrxETH was returned'); return sfrxeth_recieved; }
calling _submit(adress(this)), which calling https://github.com/FraxFinance/frxETH-public/blob/7f7731dbc93154131aba6e741b6116da05b25662/src/frxETHMinter.sol#L85
/// @notice Mint frxETH to the recipient using sender's funds. Internal portion function _submit(address recipient) internal nonReentrant { // Initial pause and value checks require(!submitPaused, "Submit is paused"); require(msg.value != 0, "Cannot submit 0"); // Give the sender frxETH frxETHToken.minter_mint(recipient, msg.value); // Track the amount of ETH that we are keeping uint256 withheld_amt = 0; if (withholdRatio != 0) { withheld_amt = (msg.value * withholdRatio) / RATIO_PRECISION; currentWithheldETH += withheld_amt; } emit ETHSubmitted(msg.sender, recipient, msg.value, withheld_amt); }
note the line of code: require(!submitPaued, "Submit is paused"), the frax ETH admin can toggle the submitPaused to block the user from deposit fund, because the usage of for loop, if one deposit revert, the deposit transaction revert.
The section above demonstrates that frax ETH deposit can be blocked, the section below would demonstrates the LIDO finance deposit can be blocked as well
When deposting into the LIDO finance, we are calling
(bool sent, ) = WST_ETH.call{value: msg.value}("");
which calling:
receive() external payable { uint256 shares = stETH.submit{value: msg.value}(address(0)); _mint(msg.sender, shares); }
calling stETH.submit{value: msg.value}(address(0));
calling https://etherscan.io/address/0x47ebab13b806773ec2a2d16873e2df770d130b50#code#F1#L290
function submit(address _referral) external payable returns (uint256) { return _submit(_referral); }
which calling _submit
function _submit(address _referral) internal returns (uint256) { require(msg.value != 0, "ZERO_DEPOSIT"); StakeLimitState.Data memory stakeLimitData = STAKING_STATE_POSITION.getStorageStakeLimitStruct(); require(!stakeLimitData.isStakingPaused(), "STAKING_PAUSED");
as we can see, if the admin pause the contract staking in LIDO finance side, require(!stakeLimitData.isStakingPaused(), "STAKING_PAUSED"); revert.
If we revisit the deposit function in Asymmetry finance side,
uint256 totalStakeValueEth = 0; // total amount of derivatives worth of ETH in system for (uint i = 0; i < derivativeCount; i++) { uint256 weight = weights[i]; IDerivative derivative = derivatives[i]; if (weight == 0) continue; uint256 ethAmount = (msg.value * weight) / totalWeight; // This is slightly less than ethAmount because slippage uint256 depositAmount = derivative.deposit{value: ethAmount}(); uint derivativeReceivedEthValue = (derivative.ethPerDerivative( depositAmount ) * depositAmount) / 10 ** 18; totalStakeValueEth += derivativeReceivedEthValue; }
the for loop introduce a dependency, meaning one deposit function revert, the whole deposit is blocked.
In fact, Rocket Protocol's admin can pause the depoist as well to block deposit and revert transaction.
// Accept a deposit from a user function deposit() override external payable onlyThisLatestContract { // Check deposit settings RocketDAOProtocolSettingsDepositInterface rocketDAOProtocolSettingsDeposit = RocketDAOProtocolSettingsDepositInterface(getContractAddress("rocketDAOProtocolSettingsDeposit")); require(rocketDAOProtocolSettingsDeposit.getDepositEnabled(), "Deposits into Rocket Pool are currently disabled");
Manual Review
We recommend the protocol check if deposit is enabled when depositing, when the deposit is disabled, the protocol can either set the weight to 0 or swapping the share token for user via uniswap pool or curve pool just like the protocol handling the rocket protocol deposit cap.
#0 - c4-pre-sort
2023-04-04T21:58:38Z
0xSorryNotSorry marked the issue as primary issue
#1 - elmutt
2023-04-07T20:38:21Z
We intentionally revert if 1 fails to keep logic easier to understand
#2 - c4-sponsor
2023-04-07T20:38:26Z
elmutt marked the issue as sponsor disputed
#3 - c4-judge
2023-04-21T09:58:24Z
Picodes marked the issue as satisfactory
#4 - c4-judge
2023-04-21T10:47:15Z
Picodes marked the issue as duplicate of #770
🌟 Selected for report: RaymondFam
Also found by: 0xepley, BPZ, Franfran, Parad0x, RedTiger, d3e4, fyvgsk, handsomegiraffe, ladboy233, rbserver, silviaxyz, whoismatthewmc1, yac
40.6368 USDC - $40.64
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L91 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L198 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/SfrxEth.sol#L101
Protocol can lose fund if the integrated liquid staking protocol charge high fee
The protocol integrate with the LIDO finance and frax share and rocket protocol,
however, the user can deposit via the SafETH.sol can lose their deposit if the underlying protocol charge high fee.
For example, when depositing into the rocketProtoocol,
we are calling:
uint256 rethBalance1 = rocketTokenRETH.balanceOf(address(this)); rocketDepositPool.deposit{value: msg.value}(); uint256 rethBalance2 = rocketTokenRETH.balanceOf(address(this));
which calls rocketDepositPool.deposit{value: msg.value}();
// Accept a deposit from a user function deposit() override external payable onlyThisLatestContract { // Check deposit settings RocketDAOProtocolSettingsDepositInterface rocketDAOProtocolSettingsDeposit = RocketDAOProtocolSettingsDepositInterface(getContractAddress("rocketDAOProtocolSettingsDeposit")); require(rocketDAOProtocolSettingsDeposit.getDepositEnabled(), "Deposits into Rocket Pool are currently disabled"); require(msg.value >= rocketDAOProtocolSettingsDeposit.getMinimumDeposit(), "The deposited amount is less than the minimum deposit size"); RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault")); require(rocketVault.balanceOf("rocketDepositPool").add(msg.value) <= rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize(), "The deposit pool size after depositing exceeds the maximum size"); // Calculate deposit fee uint256 depositFee = msg.value.mul(rocketDAOProtocolSettingsDeposit.getDepositFee()).div(calcBase); uint256 depositNet = msg.value.sub(depositFee); // Mint rETH to user account RocketTokenRETHInterface rocketTokenRETH = RocketTokenRETHInterface(getContractAddress("rocketTokenRETH")); rocketTokenRETH.mint(depositNet, msg.sender); // Emit deposit received event emit DepositReceived(msg.sender, msg.value, block.timestamp); // Process deposit processDeposit(rocketVault, rocketDAOProtocolSettingsDeposit); }
note the code:
uint256 depositFee = msg.value.mul(rocketDAOProtocolSettingsDeposit.getDepositFee()).div(calcBase); uint256 depositNet = msg.value.sub(depositFee);
the if the rocket protocl charge 50% of the fee or even 99% of the fee, it is not reasonable to still deposit the fund into the protocol for yield because the deposit instanlty incurs large loss.
This is the same case for frax liquid staking, when depositing, the protocol from liquid staking can withdraw a part of ETH based on the withhold ratio
When depositing into the frax pool, we are calling
frxETHMinterContract.submitAndDeposit{value: msg.value}(address(this));
which calls:
/// @notice Mint frxETH and deposit it to receive sfrxETH in one transaction /** @dev Could try using EIP-712 / EIP-2612 here in the future if you replace this contract, but you might run into msg.sender vs tx.origin issues with the ERC4626 */ function submitAndDeposit(address recipient) external payable returns (uint256 shares) { // Give the frxETH to this contract after it is generated _submit(address(this)); // Approve frxETH to sfrxETH for staking frxETHToken.approve(address(sfrxETHToken), msg.value); // Deposit the frxETH and give the generated sfrxETH to the final recipient uint256 sfrxeth_recieved = sfrxETHToken.deposit(msg.value, recipient); require(sfrxeth_recieved > 0, 'No sfrxETH was returned'); return sfrxeth_recieved; }
which calls:
/// @notice Mint frxETH to the recipient using sender's funds. Internal portion function _submit(address recipient) internal nonReentrant { // Initial pause and value checks require(!submitPaused, "Submit is paused"); require(msg.value != 0, "Cannot submit 0"); // Give the sender frxETH frxETHToken.minter_mint(recipient, msg.value); // Track the amount of ETH that we are keeping uint256 withheld_amt = 0; if (withholdRatio != 0) { withheld_amt = (msg.value * withholdRatio) / RATIO_PRECISION; currentWithheldETH += withheld_amt; } emit ETHSubmitted(msg.sender, recipient, msg.value, withheld_amt); }
As we can see, if the witholdRatio is very high, the frax protocol can withdraw a very large of ETH.
Mnaual Review
We recommend the protocol check if the depsoit fee is reasonable when depositing, otherwise, revert the transaction and set the derivative pool weight to 0.
#0 - c4-pre-sort
2023-04-04T21:54:39Z
0xSorryNotSorry marked the issue as primary issue
#1 - c4-sponsor
2023-04-07T20:23:44Z
toshiSat marked the issue as sponsor acknowledged
#2 - Picodes
2023-04-24T18:12:54Z
Related to #549
#3 - c4-judge
2023-04-24T18:19:56Z
Picodes changed the severity to 3 (High Risk)
#4 - c4-judge
2023-04-24T18:23:00Z
Picodes changed the severity to 2 (Med Risk)
#5 - c4-judge
2023-04-24T18:23:14Z
Picodes marked the issue as selected for report
#6 - c4-judge
2023-04-24T20:41:51Z
Picodes marked issue #150 as primary and marked this issue as a duplicate of 150
#7 - c4-judge
2023-04-24T21:20:52Z
Picodes marked the issue as satisfactory