Relevant GitHub Links
Summary
The fee-minted in the form of shares (svTokens) would not be subtracted from the amount of shares (svTokens) to be minted to the GMXVault's depositor.
Due to that, a depositor of the GMXVault could receive the amount of the shares (svTokens), which the amount of the fee-minted in the form of the shares (svTokens) via the GMXDeposit#
mintFee()
was not subtracted.This means that a depositor of the GMXVault can bypass paying the fee when the depositor deposit into the GMXVault.
Vulnerability Details
Within the GMXDeposit#
deposit()
, the GMXVault#mintFee()
would be called to mint the fee in the form of the svTokens like this:
https://github.com/Cyfrin/2023-10-SteadeFi/blob/main/contracts/strategy/gmx/GMXDeposit.sol#L119plain text/** * @notice @inheritdoc GMXVault * @param self GMXTypes.Store * @param isNative Boolean as to whether user is depositing native asset (e.g. ETH, AVAX, etc.) */ function deposit( GMXTypes.Store storage self, GMXTypes.DepositParams memory dp, bool isNative ) external { ... self.status = GMXTypes.Status.Deposit; self.vault.mintFee(); ///<----------------------- @audit ...
Within the GMXVault#
mintFee()
, the amount (GMXReader.pendingFee(_store)
) of the shares would be minted to the treasury (_store.treasury
) in the form of the svTokens like this:
https://github.com/Cyfrin/2023-10-SteadeFi/blob/main/contracts/strategy/gmx/GMXVault.sol#L335plain text/** * @notice Mint vault token shares as management fees to protocol treasury */ function mintFee() public { _mint(_store.treasury, GMXReader.pendingFee(_store)); ///<------------ @audit _store.lastFeeCollected = block.timestamp; }
When callback of deposit, the the GMXDeposit#
processDeposit()
would be called via the GMXVault#deposit()
.Within the GMXDeposit#
processDeposit()
, the amount (self.depositCache.sharesToUser
) of shares (VaultTokens) would be minted to the GMXVault's depositor (self.depositCache.user
) like this:
https://github.com/Cyfrin/2023-10-SteadeFi/blob/main/contracts/strategy/gmx/GMXDeposit.sol#L172plain text/** * @notice @inheritdoc GMXVault * @param self GMXTypes.Store */ function processDeposit( GMXTypes.Store storage self ) external { GMXChecks.beforeProcessDepositChecks(self); // We transfer the core logic of this function to GMXProcessDeposit.processDeposit() // to allow try/catch here to catch for any issues or any checks in afterDepositChecks() failing. // If there are any issues, a DepositFailed event will be emitted and processDepositFailure() // should be triggered to refund assets accordingly and reset the vault status to Open again. try GMXProcessDeposit.processDeposit(self) { // Mint shares to depositor self.vault.mint(self.depositCache.user, self.depositCache.sharesToUser); ///<------------- @audit ...
Within the GMXDeposit#
processDeposit()
above, the amount of the fee-minted in the form of the shares (svTokens) via the GMXDeposit#mintFee()
is supposed to be subtracted from the amount of the shares to be minted to the GMXVault's depositor via the GMXDeposit#processDeposit()
.However, there is no logic to subtract the amount of the fee-minted in the form of the shares (svTokens) via the GMXDeposit#
mintFee()
from the amount of the shares to be minted to the GMXVault's depositor in the form of the shares (svTokens) via the GMXDeposit#processDeposit()
.Impact
The depositor could receive the amount of the shares (svTokens), which the amount of the fee-minted in the form of the shares (svTokens) via the GMXDeposit#
mintFee()
was not subtracted.This means that a depositor of the GMXVault can bypass paying the fee when the depositor deposit into the GMXVault.
Tools Used
- Foundry