My CodeHawks submission for Beedle Audit: Oracle free peer to peer perpetual lending
- High: 1
- Medium: 0
- Low: 0
- Gas/Info: 4
setPool
and updateInterestRate
functions in Lender.sol
contract allow the lender to change the interest rate to up to 1000% at any time without a delay, this also includes frontrunning the borrowers' transactions.
The arbitrary change of interest rates may be unexpected for borrowers and can result in an inability to repay the loan on time.
Manual Review
Consider adding a delay (for instance: 1 day) for the interest rate update to go live in both the setPool
and updateInterestRate
functions.
Internal functions _burn
and _afterTokenTransfer
are unnecessary, as they are present in ERC20.sol
already. This is a tautology.
Remove functions _burn
and _afterTokenTransfer
from Beedle.sol
.
In the Lender.sol
contract's setPool
function, replace:
// check if they already have a pool balance
poolId = getPoolId(p.lender, p.loanToken, p.collateralToken);
...
uint256 currentBalance = pools[poolId].poolBalance;
with:
// get pool id
poolId = getPoolId(p.lender, p.loanToken, p.collateralToken);
...
// check if they already have a pool balance
uint256 currentBalance = pools[poolId].poolBalance;
setPool
function in Lender.sol
contract does not check if pool parameters p.loanToken
and p.collateralToken
are contracts.
Manual Review
Use OpenZeppelin isContract(address account) function to check if the input address is a contract
Example:
function setPool(Pool calldata p) public returns (bytes32 poolId) {
...
require(isContract(p.loanToken) && isContract(p.collateralToken), "Token is not a contract");
...
}
Even though there's no significant difference in gas cost, it's generally recommended to use require
for input validation and pre-condition checks, and if
with revert
for more complex logic.
This is applicable across the whole codebase
Manual Review