# Gelato Aave Security Assessment (DONE)
<!-- ## Overview -->
<!-- ## Privileged Roles -->
# Findings
## Incorrect calculation of amtOfColToWithdraw and amtToFlashBorrow
<!-- Severity: `Medium` -->
https://github.com/gelatodigital/gelato-aave/blob/4a6d42fc61e21ca1e0850d6d088763e2cd08d12d/contracts/functions/FProtectionResolver.sol#L18-L66
```solidity=
function _amountToPaybackAndWithdraw(
bytes32 _id,
ProtectionDataCompute memory _protectionDataCompute
) view returns (RepayAndWithdrawResult memory) {
// Liquidation is a 4 digit values lower than 10000.
uint256 intermediateValue =
_wdiv(
(
(
(_wmul(
_protectionDataCompute.wantedHealthFactor,
_protectionDataCompute.totalBorrowsETH
) -
(
_qmul(
_protectionDataCompute.totalCollateralETH,
_protectionDataCompute
.currentLiquidationThreshold
)
))
)
),
_qmul(
_protectionDataCompute.wantedHealthFactor,
(TEN_THOUSAND_BPS - _protectionDataCompute.flashloanPremium)
) -
_qmul(
_protectionDataCompute.colLiquidationThreshold,
(TEN_THOUSAND_BPS +
_protectionDataCompute.protectionActionFee)
) *
1e14
);
uint256 colTokenDecimals =
ERC20(_protectionDataCompute.colToken).decimals();
uint256 debtTokenDecimals =
ERC20(_protectionDataCompute.debtToken).decimals();
return
RepayAndWithdrawResult(
_id,
((_tokenToTokenPrecision(intermediateValue, 18, colTokenDecimals) *
10**colTokenDecimals) / _protectionDataCompute.colPrice),
((_tokenToTokenPrecision(intermediateValue, 18, debtTokenDecimals) *
10**debtTokenDecimals) / _protectionDataCompute.debtPrice),
"OK"
);
}
```
### Issue Description
$amtOfColToWithdraw = amtToFlashBorrow \times (1 + flashloanPremiumRate)$
$wantedHealthFactor = {totalCollateralETH \times currentLiquidationThreshold - amtOfColToWithdraw \times (1 + protectionActionFeeRate) \times colLiquidationThreshold \over totalBorrowsETH - amtToFlashBorrow}$
$\iff$
$amtOfColToWithdraw = { wantedHealthFactor \times totalBorrowsETH - totalCollateralETH \times currentLiquidationThreshold \over {wantedHealthFactor \over 1 + flashloanPremiumRate} - (1 + protectionActionFeeRate) \times colLiquidationThreshold}$
$amtToFlashBorrow = {amtOfColToWithdraw \over 1 + flashloanPremiumRate}$
### Recommendation
- change
```solidity=
_qmul(
_protectionDataCompute.wantedHealthFactor,
(TEN_THOUSAND_BPS - _protectionDataCompute.flashloanPremium)
)
```
to
```solidity=
_qdiv(
_protectionDataCompute.wantedHealthFactor,
(TEN_THOUSAND_BPS + _protectionDataCompute.flashloanPremium)
)
```
- change
```solidity=
return
RepayAndWithdrawResult(
_id,
((_tokenToTokenPrecision(intermediateValue, 18, colTokenDecimals) *
10**colTokenDecimals) / _protectionDataCompute.colPrice),
((_tokenToTokenPrecision(intermediateValue, 18, debtTokenDecimals) *
10**debtTokenDecimals) / _protectionDataCompute.debtPrice),
"OK"
);
```
to
```solidity=
return
RepayAndWithdrawResult(
_id,
((_tokenToTokenPrecision(intermediateValue, 18, colTokenDecimals) *
10**colTokenDecimals) / _protectionDataCompute.colPrice),
((_tokenToTokenPrecision(_qdiv(intermediateValue, TEN_THOUSAND_BPS + _protectionDataCompute.flashloanPremium), 18, debtTokenDecimals) *
10**debtTokenDecimals) / _protectionDataCompute.debtPrice),
"OK"
);
```
<!-- ### Resolution
-->
---
## Miscalculation in _tokenToTokenPrecision when _oldPrecision <= _newPrecision
<!-- Severity: `Medium` -->
https://github.com/gelatodigital/gelato-aave/blob/4a6d42fc61e21ca1e0850d6d088763e2cd08d12d/contracts/functions/FProtectionResolver.sol#L122-L131
```solidity=
function _tokenToTokenPrecision(
uint256 _amount,
uint256 _oldPrecision,
uint256 _newPrecision
) pure returns (uint256) {
return
_oldPrecision > _newPrecision
? _amount / (10**(_oldPrecision - _newPrecision))
: _amount * (10**(_oldPrecision - _newPrecision));
}
```
### Issue Description
When `_oldPrecision <= _newPrecision`, it should return `_amount * (10**(_newPrecision - _oldPrecision))`
### Recommendation
change
```solidity=
_amount * (10**(_oldPrecision - _newPrecision))
```
to
```solidity=
_amount * (10**(_newPrecision - _oldPrecision))
```
---
## Unused code
<!-- Severity: `Informational` -->
### Issue Description
https://github.com/gelatodigital/gelato-aave/blob/master/contracts/services/aave/resolver/ProtectionResolver.sol#L131-L141
https://github.com/gelatodigital/gelato-aave/blob/master/contracts/functions/FProtectionResolver.sol#L83-L118
The variable `colInETH` and `debtInETH` in `ProtectionResolver.sol#protectionDataCompute()` and the function `_getColInETH` and `_getDebtInEth` in `FProtectionResolver.sol` are unused. They may be leftover from a previous version.
### Recommendation
Consider removing unused variables and functions.
---
## Misleading names
<!-- Severity: `Informational` -->
### Issue Description
- https://github.com/gelatodigital/gelato-aave/blob/master/contracts/functions/FProtectionAction.sol#L107
The second parameter of `_withdrawCollateral()` named `aaveService` is actually not the aaveService contract.
**Recommendation**: Consider changing the name to `recipient` or `to`.
- https://github.com/gelatodigital/gelato-aave/blob/master/contracts/functions/FProtectionAction.sol#L144
The function `FProtectionAction.sol#_isPositionSafe` and `FProtectionAction.sol#_isPositionUnSafe` suggests that it returns a bool that indicates whether the position is safe/unsafe, while it actually requires the position to be safe/unsafe.
https://github.com/gelatodigital/gelato-aave/blob/master/contracts/functions/FProtectionResolver.sol#L139-L146
Using `_isPositionSafe` and `_isPositionUnSafe` is also likely to be confused with `FProtectionResolver.sol#_isPositionUnsafe`.
**Recommendation**: Consider changing the name from `_isPositionUnSafe` to `_requirePositionUnSafe`, and `_isPositionSafe` to `_requirePositionSafe`.
- https://github.com/gelatodigital/gelato-aave/blob/e9cd8e37aea7117273decc379175cfe923f780a6/contracts/services/aave/resolver/ProtectionResolver.sol#L129-L132
The name `protectionActionFee` and `flashloanPremium` might be misinterpreted as the amount of the fees.
**Recommendation**: Consider changing the name from `protectionActionFee` to `protectionActionFeeBps`, and `flashloanPremium` to `flashloanPremiumBps`.
---
## Unnecessary imports
<!-- Severity: `Informational` -->
### Issue Description
- https://github.com/gelatodigital/gelato-aave/blob/master/contracts/services/aave/FlashLoanReceiverBase.sol#L17
Unnecessary import of `SafeMath` library in `FlashLoanReceiverBase.sol`.
- https://github.com/gelatodigital/gelato-aave/blob/master/contracts/structs/SProtection.sol#L4
`import {Path} from "../structs/SParaswap.sol"`
- https://github.com/gelatodigital/gelato-aave/blob/master/contracts/functions/FProtectionAction.sol#L10
`import {_rdiv} from "../vendor/DSMath.sol"`
- https://github.com/gelatodigital/gelato-aave/blob/7bc98ff445dc700e158a56832e35a677e2313fc2/contracts/services/aave/resolver/ProtectionResolver.sol#L12
Unnecessary import of `IPriceOracle`, `ProtectionDataCompute` and `IERC20`.
### Recommendation
Remove unnecessary imports.
---
## Wrong name or value of `isPositionSafe`
<!-- Severity: `Informational` -->
### Issue Description
https://github.com/gelatodigital/gelato-aave/blob/master/contracts/services/aave/resolver/ProtectionResolver.sol#L188-L191
```solidity=
result.isPositionSafe = _isPositionUnsafe(
_canExecData.user,
_canExecData.minimumHF
);
```
### Recommendation
Consider changing to:
```solidity=
result.isPositionSafe = !_isPositionUnsafe(
_canExecData.user,
_canExecData.minimumHF
);
```
or
```solidity=
result.isPositionUnsafe = _isPositionUnsafe(
_canExecData.user,
_canExecData.minimumHF
);
```
---
## Unnecessary ternary statements
### Issue Description
https://github.com/gelatodigital/gelato-aave/blob/master/contracts/functions/FProtectionResolver.sol#L94-L114
```solidity=
function _isPositionUnsafe(address _user, uint256 _minimumHF)
view
returns (bool)
{
(, , , , , uint256 currenthealthFactor) =
ILendingPool(LENDINGPOOL).getUserAccountData(_user);
return currenthealthFactor < _minimumHF ? true : false;
}
function _isAllowed(
address _aToken,
address _user,
address _spender,
uint256 _allowedAmt
) view returns (bool) {
return
IERC20(_aToken).balanceOf(_user) >= _allowedAmt &&
IERC20(_aToken).allowance(_user, _spender) >= _allowedAmt
? true
: false;
}
```
The ternary statements in `FProtectionResolver.sol#_isPositionUnsafe()` and `FProtectionResolver.sol#_isAllowed()` are unnecessary. They may be leftover from a previous version.
### Recommendation
Consider removing the unnecessary ternary statements.
---
## Leftover debtTokens in edge cases are not returned
### Issue Description
https://github.com/gelatodigital/gelato-aave/blob/master/contracts/services/aave/actions/ProtectionAction.sol#L195-L201
The current implementation of `ProtectionAction.sol#_executeOperation()` won't return the leftover debtTokens in edge cases of the `debtRepaid` being larger than the debt amount.
As an example, consider the scenario of:
Current debt amount: 10,000 USDC
Calculated amtOfDebtToRepay : 9,999 USDC
After the task is generated and submitted, but before executed, the user repaid the debt for 2 USDC.
So, by the time we execute the task, the `amtOfDebtToRepay` will actually be larger than the debt amount (9,998 USDC). As a result, 1 USDC will be left in the contract.
### Recommendation
Consider checking for leftover debtTokens after `_paybackToLendingPool` and send them back to the user.
---
## Using all the wallet balance can cause Denial of Service (DoS)
### Issue Description
https://github.com/gelatodigital/gelato-aave/blob/f58b6b042090582edec59c2b65f1497a5f8901c7/contracts/services/aave/actions/ProtectionAction.sol#L200
https://github.com/gelatodigital/gelato-aave/blob/7bc98ff445dc700e158a56832e35a677e2313fc2/contracts/functions/FProtectionAction.sol#L242
`ProtectionAction.sol#_executeOperation()` and `FProtectionAction.sol#_swap()` is using all the wallet balance to swap and repay debt.
An attacker may send a certain amount of colToken/debtToken to the `ProtectionAction` contract which leads to over repay of the debt so that the result Health Factor can be higher than the upper limit and cause the transaction to be reverted.
### Recommendation
Consider using `_flashloanData.amounts[0]` to swap, and `receivedAmt` to repay debt.
---
## ProtectionAction.sol allows for arbitrary code execution
### Issue Description
https://github.com/gelatodigital/gelato-aave/blob/f58b6b042090582edec59c2b65f1497a5f8901c7/contracts/functions/FProtectionAction.sol#L181
The Gelato protocol's design allows the executors to craft calldata and make call to `ProtectionAction.sol` on behalf of the GELATO address, and the on-chain requirements only enforced the post protection health factor.
A compromised or malicious executor can take advantage of this and stealing user's funds.
As an example, consider the scenario of:
A user with 200 ETH worth of collateral and 50 ETH worth of debt, and the weighted average liquidation threshold is 50%, then the current Health Factor will be:
(200 * 0.5) / 50 = 200%
If the target Health Factor of the protection task is 300%.
A malicious executor can craft a transaction that do the following:
1. Withdraw collateral and repay 49.9 ETH worth of debt;
2. Withdraw 149.5 ETH worth of collateral and transfer to the executor's address;
The final Health Factor will be:
(200 - 49.9 - 149.5) * 0.5 / (50 - 49.9) = 300%
Which will pass the onchain check.
### Recommendation
Create an isolated swap contract and heighten the on-chain requirements.
### Resolution
This issue has been addressed with commit [7bc98ff445dc700e158a56832e35a677e2313fc2](https://github.com/gelatodigital/gelato-aave/commit/7bc98ff445dc700e158a56832e35a677e2313fc2) by adding an isolated swap contract and on-chain verification of `amtToFlashBorrow` and `amtOfDebtToRepay`.
<!--
## 待定或待写
- 把闪电贷改为借 colToken
- 任意代码执行 at https://github.com/gelatodigital/gelato-aave/blob/e9cd8e37aea7117273decc379175cfe923f780a6/contracts/functions/FProtectionAction.sol#L167-L177
- unused import
- unused import of IPriceOracle, ProtectionDataCompute and IERC20 in [ProtectionResolver.sol](https://github.com/gelatodigital/gelato-aave/blob/7bc98ff445dc700e158a56832e35a677e2313fc2/contracts/services/aave/resolver/ProtectionResolver.sol#L12)
-->