Summary
Contract allows anyone to deposit assets into the contract without any access restrictions. Access control is not properly implemented, potentially enabling unauthorized users to interact with the contract's deposit functionality.
Vulnerability Details
The vulnerable function is deposit
, which allows users to deposit assets into the contract without checking their permissions. The lack of access control makes the contract susceptible to unauthorized deposits.
function deposit(
GMXTypes.Store storage self,
GMXTypes.DepositParams memory dp,
bool isNative
) external {
if (self.tokenA.balanceOf(address(this)) > 0) {
self.tokenA.safeTransfer(self.trove, self.tokenA.balanceOf(address(this)));
}
if (self.tokenB.balanceOf(address(this)) > 0) {
self.tokenB.safeTransfer(self.trove, self.tokenB.balanceOf(address(this)));
}
self.refundee = payable(msg.sender);
GMXTypes.HealthParams memory _hp;
_hp.equityBefore = GMXReader.equityValue(self);
_hp.lpAmtBefore = GMXReader.lpAmt(self);
_hp.debtRatioBefore = GMXReader.debtRatio(self);
_hp.deltaBefore = GMXReader.delta(self);
if (isNative) {
GMXChecks.beforeNativeDepositChecks(self, dp);
self.WNT.deposit{ value: dp.amt }();
} else {
IERC20(dp.token).safeTransferFrom(msg.sender, address(this), dp.amt);
}
GMXTypes.DepositCache memory _dc;
_dc.user = payable(msg.sender);
if (dp.token == address(self.lpToken)) {
_dc.depositValue = self.gmxOracle.getLpTokenValue(
address(self.lpToken),
address(self.tokenA),
address(self.tokenA),
address(self.tokenB),
false,
false
)
* dp.amt
/ SAFE_MULTIPLIER;
} else {
_dc.depositValue = GMXReader.convertToUsdValue(
self,
address(dp.token),
dp.amt
);
}
_dc.depositParams = dp;
_dc.healthParams = _hp;
self.depositCache = _dc;
GMXChecks.beforeDepositChecks(self, _dc.depositValue);
self.status = GMXTypes.Status.Deposit;
self.vault.mintFee();
(
uint256 _borrowTokenAAmt,
uint256 _borrowTokenBAmt
) = GMXManager.calcBorrow(self, _dc.depositValue);
_dc.borrowParams.borrowTokenAAmt = _borrowTokenAAmt;
_dc.borrowParams.borrowTokenBAmt = _borrowTokenBAmt;
GMXManager.borrow(self, _borrowTokenAAmt, _borrowTokenBAmt);
GMXTypes.AddLiquidityParams memory _alp;
_alp.tokenAAmt = self.tokenA.balanceOf(address(this));
_alp.tokenBAmt = self.tokenB.balanceOf(address(this));
_alp.minMarketTokenAmt = GMXManager.calcMinMarketSlippageAmt(
self,
_dc.depositValue,
dp.slippage
);
_alp.executionFee = dp.executionFee;
_dc.depositKey = GMXManager.addLiquidity(
self,
_alp
);
self.depositCache = _dc;
emit DepositCreated(
_dc.user,
_dc.depositParams.token,
_dc.depositParams.amt
);
}
Impact
Unauthorized users can deposit assets, potentially causing unexpected interactions or disruptions in the system. This lack of control could lead to unintended deposits, potentially affecting the contract's state and stability.
Tools Used
Manual
Recommendations
Implement proper access control by using modifiers to ensure that only authorized users can call the deposit
function. Below is an example of how to add an onlyController
modifier to restrict access to authorized controllers:
modifier onlyController() {
require(msg.sender == self.controller, "Unauthorized access");
_;
}
function deposit(
GMXTypes.Store storage self,
GMXTypes.DepositParams memory dp,
bool isNative
) external onlyController {
require(self.controller != address(0), "Controller address is not set");
if (self.tokenA.balanceOf(address(this)) > 0) {
self.tokenA.safeTransfer(self.trove, self.tokenA.balanceOf(address(this)));
}
if (self.tokenB.balanceOf(address(this)) > 0) {
self.tokenB.safeTransfer(self.trove, self.tokenB.balanceOf(address(this)));
}
self.refundee = payable(msg.sender);
GMXTypes.HealthParams memory _hp;
_hp.equityBefore = GMXReader.equityValue(self);
_hp.lpAmtBefore = GMXReader.lpAmt(self);
_hp.debtRatioBefore = GMXReader.debtRatio(self);
_hp.deltaBefore = GMXReader.delta(self);
if (isNative) {
GMXChecks.beforeNativeDepositChecks(self, dp);
self.WNT.deposit{ value: dp.amt }();
} else {
IERC20(dp.token).safeTransferFrom(msg.sender, address(this), dp.amt);
}
GMXTypes.DepositCache memory _dc;
_dc.user = payable(msg.sender);
if (dp.token == address(self.lpToken)) {
_dc.depositValue = self.gmxOracle.getLpTokenValue(
address(self.lpToken),
address(self.tokenA),
address(self.tokenA),
address(self.tokenB),
false,
false
) * dp.amt / SAFE_MULTIPLIER;
} else {
_dc.depositValue = GMXReader.convertToUsdValue(self, address(dp.token), dp.amt);
}
_dc.depositParams = dp;
_dc.healthParams = _hp;
self.depositCache = _dc;
GMXChecks.beforeDepositChecks(self, _dc.depositValue);
self.status = GMXTypes.Status.Deposit;
self.vault.mintFee();
(uint256 _borrowTokenAAmt, uint256 _borrowTokenBAmt) = GMXManager.calcBorrow(self, _dc.depositValue);
_dc.borrowParams.borrowTokenAAmt = _borrowTokenAAmt;
_dc.borrowParams.borrowTokenBAmt = _borrowTokenBAmt;
GMXManager.borrow(self, _borrowTokenAAmt, _borrowTokenBAmt);
GMXTypes.AddLiquidityParams memory _alp;
_alp.tokenAAmt = self.tokenA.balanceOf(address(this));
_alp.tokenBAmt = self.tokenB.balanceOf(address(this));
_alp.minMarketTokenAmt = GMXManager.calcMinMarketSlippageAmt(self, _dc.depositValue, dp.slippage);
_alp.executionFee = dp.executionFee;
_dc.depositKey = GMXManager.addLiquidity(self, _alp);
self.depositCache = _dc;
emit DepositCreated(_dc.user, _dc.depositParams.token, _dc.depositParams.amt);
}