Summary
Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas) in a function that may ultimately revert in the unhappy case.
Tools used
manual code review
Recommendations
Lender.sol
function addToPool(bytes32 poolId, uint256 amount) external {
if (amount == 0) revert PoolConfig();
if (pools[poolId].lender != msg.sender) revert Unauthorized();
.
.
);
}
function removeFromPool(bytes32 poolId, uint256 amount) external {
if (amount == 0) revert PoolConfig();
if (pools[poolId].lender != msg.sender) revert Unauthorized();
.
.
}
function updateMaxLoanRatio(bytes32 poolId, uint256 maxLoanRatio) external {
if (maxLoanRatio == 0) revert PoolConfig();
if (pools[poolId].lender != msg.sender) revert Unauthorized();
.
.
}
function updateInterestRate(bytes32 poolId, uint256 interestRate) external {
if (interestRate > MAX_INTEREST_RATE) revert PoolConfig();
if (pools[poolId].lender != msg.sender) revert Unauthorized();
.
.
}
function borrow(Borrow[] calldata borrows) public {
for (uint256 i = 0; i < borrows.length; i++) {
bytes32 poolId = borrows[i].poolId;
uint256 debt = borrows[i].debt;
uint256 collateral = borrows[i].collateral;
if (collateral == 0) revert ZeroCollateral();
Pool memory pool = pools[poolId];
if (pool.lender == address(0)) revert PoolConfig();
if (debt < pool.minLoanSize) revert LoanTooSmall();
if (debt > pool.poolBalance) revert LoanTooLarge();
.
.
.
}