First pool borrower pays extra interest
For any function in which the current interest rate is important in a pool, we compute interest updates by accruing with `_accruePoolInterest` at the start of the function, then execute the main logic, then update the interest state accordingly with `_updateInterestState`. See below a simplified example for ERC20Pool.drawDebt:\\n```\\nfunction drawDebt(\\n    address borrowerAddress_,\\n    uint256 amountToBorrow_,\\n    uint256 limitIndex_,\\n    uint256 collateralToPledge_\\n) external nonReentrant {\\n    PoolState memory poolState = _accruePoolInterest();\\n\\n   // rest of code\\n\\n    DrawDebtResult memory result = BorrowerActions.drawDebt(\\n        auctions,\\n        deposits,\\n        loans,\\n        poolState,\\n        _availableQuoteToken(),\\n        borrowerAddress_,\\n        amountToBorrow_,\\n        limitIndex_,\\n        collateralToPledge_\\n    );\\n\\n    // rest of code\\n\\n    // update pool interest rate state\\n    _updateInterestState(poolState, result.newLup);\\n\\n   // rest of code\\n}\\n```\\n\\nWhen accruing interest in `_accruePoolInterest`, we only update the state if `poolState_.t0Debt != 0`. Most notably, we don't set `poolState_.isNewInterestAccrued`. See below simplified logic from _accruePoolInterest:\\n```\\n// check if t0Debt is not equal to 0, indicating that there is debt to be tracked for the pool\\nif (poolState_.t0Debt != 0) {\\n    // rest of code\\n\\n    // calculate elapsed time since inflator was last updated\\n    uint256 elapsed = block.timestamp - inflatorState.inflatorUpdate;\\n\\n    // set isNewInterestAccrued field to true if elapsed time is not 0, indicating that new interest may have accrued\\n    poolState_.isNewInterestAccrued = elapsed != 0;\\n\\n    // rest of code\\n}\\n```\\n\\nOf course before we actually update the state from the first borrow, the debt of the pool is 0, and recall that `_accruePoolInterest` runs before the main state changing logic of the function in `BorrowerActions.drawDebt`.\\nAfter executing the main state changing logic in `BorrowerActions.drawDebt`, where we update state, including incrementing the pool and borrower debt as expected, we run the logic in `_updateInterestState`. Here we update the inflator if either `poolState_.isNewInterestAccrued` or `poolState_.debt == 0`.\\n```\\n// update pool inflator\\nif (poolState_.isNewInterestAccrued) {\\n    inflatorState.inflator       = uint208(poolState_.inflator);\\n    inflatorState.inflatorUpdate = uint48(block.timestamp);\\n// if the debt in the current pool state is 0, also update the inflator and inflatorUpdate fields in inflatorState\\n// slither-disable-next-line incorrect-equality\\n} else if (poolState_.debt == 0) {\\n    inflatorState.inflator       = uint208(Maths.WAD);\\n    inflatorState.inflatorUpdate = uint48(block.timestamp);\\n}\\n```\\n\\nThe problem here is that since there was no debt at the start of the function, `poolState_.isNewInterestAccrued` is false and since there is debt now at the end of the function, `poolState_.debt == 0` is also false. As a result, the inflator is not updated. Updating the inflator here is paramount since it effectively marks a starting time at which interest accrues on the borrowers debt. Since we don't update the inflator, the borrowers debt effectively started accruing interest at the time of the last inflator update, which is an arbitrary duration.\\nWe can prove this vulnerability by modifying `ERC20PoolBorrow.t.sol:testPoolBorrowAndRepay` to skip 100 days before initially drawing debt:\\n```\\nfunction testPoolBorrowAndRepay() external tearDown {\\n    // check balances before borrow\\n    assertEq(_quote.balanceOf(address(_pool)), 50_000 * 1e18);\\n    assertEq(_quote.balanceOf(_lender),        150_000 * 1e18);\\n\\n    // @audit skip 100 days to break test\\n    skip(100 days);\\n\\n    _drawDebt({\\n        from: _borrower,\\n        borrower: _borrower,\\n        amountToBorrow: 21_000 * 1e18,\\n        limitIndex: 3_000,\\n        collateralToPledge: 100 * 1e18,\\n        newLup: 2_981.007422784467321543 * 1e18\\n    });\\n\\n    // rest of code\\n}\\n```\\n\\nUnlike the result without skipping time before drawing debt, the test fails with output logs being off by amounts roughly corresponding to the unexpected interest.
Issue First pool borrower pays extra interest\\nWhen checking whether the debt of the pool is 0 to determine whether to reset the inflator, it should not only check whether the debt is 0 at the end of execution, but also whether the debt was 0 before execution. To do so, we should cache the debt at the start of the function and modify the `_updateInterestState` logic to be something like:\\n```\\n// update pool inflator\\nif (poolState_.isNewInterestAccrued) {\\n    inflatorState.inflator       = uint208(poolState_.inflator);\\n    inflatorState.inflatorUpdate = uint48(block.timestamp);\\n// if the debt in the current pool state is 0, also update the inflator and inflatorUpdate fields in inflatorState\\n// slither-disable-next-line incorrect-equality\\n// @audit reset inflator if no debt before execution\\n} else if (poolState_.debt == 0 || debtBeforeExecution == 0) {\\n    inflatorState.inflator       = uint208(Maths.WAD);\\n    inflatorState.inflatorUpdate = uint48(block.timestamp);\\n}\\n```\\n
First borrower always pays extra interest, with losses depending upon time between adding liquidity and drawing debt and amount of debt drawn.\\nNote also that there's an attack vector here in which the liquidity provider can intentionally create and fund the pool a long time before announcing it, causing the initial borrower to lose a significant amount to interest.
```\\nfunction drawDebt(\\n    address borrowerAddress_,\\n    uint256 amountToBorrow_,\\n    uint256 limitIndex_,\\n    uint256 collateralToPledge_\\n) external nonReentrant {\\n    PoolState memory poolState = _accruePoolInterest();\\n\\n   // rest of code\\n\\n    DrawDebtResult memory result = BorrowerActions.drawDebt(\\n        auctions,\\n        deposits,\\n        loans,\\n        poolState,\\n        _availableQuoteToken(),\\n        borrowerAddress_,\\n        amountToBorrow_,\\n        limitIndex_,\\n        collateralToPledge_\\n    );\\n\\n    // rest of code\\n\\n    // update pool interest rate state\\n    _updateInterestState(poolState, result.newLup);\\n\\n   // rest of code\\n}\\n```\\n