function refinance(Refinance[] calldata refinances) public {
for (uint256 i = 0; i < refinances.length;) {
uint256 loanId = refinances[i].loanId;
bytes32 poolId = refinances[i].poolId;
bytes32 oldPoolId = keccak256(
abi.encode(
loans[loanId].lender,
loans[loanId].loanToken,
loans[loanId].collateralToken
)
);
uint256 debt = refinances[i].debt;
uint256 collateral = refinances[i].collateral;
Loan memory loan = loans[loanId];
if (msg.sender != loan.borrower) revert Unauthorized();
Pool memory pool = pools[poolId];
if (pool.loanToken != loan.loanToken) revert TokenMismatch();
if (pool.collateralToken != loan.collateralToken)
revert TokenMismatch();
if (pool.poolBalance < debt) revert LoanTooLarge();
if (debt < pool.minLoanSize) revert LoanTooSmall();
uint256 loanRatio = (debt * 10 ** 18) / collateral;
if (loanRatio > pool.maxLoanRatio) revert RatioTooHigh();
(
uint256 lenderInterest,
uint256 protocolInterest
) = _calculateInterest(loan);
uint256 debtToPay = loan.debt + lenderInterest + protocolInterest;
_updatePoolBalance(
oldPoolId,
pools[oldPoolId].poolBalance + loan.debt + lenderInterest
);
pools[oldPoolId].outstandingLoans -= loan.debt;
_updatePoolBalance(poolId, pools[poolId].poolBalance - debt);
pools[poolId].outstandingLoans += debt;
if (debtToPay > debt) {
IERC20(loan.loanToken).transferFrom(
msg.sender,
address(this),
debtToPay - debt
);
} else if (debtToPay < debt) {
uint256 fee = (borrowerFee * (debt - debtToPay)) / 10000;
IERC20(loan.loanToken).transfer(feeReceiver, fee);
IERC20(loan.loanToken).transfer(msg.sender, debt - debtToPay - fee);
}
IERC20(loan.loanToken).transfer(feeReceiver, protocolInterest);
loans[loanId].debt = debt;
if (collateral > loan.collateral) {
IERC20(loan.collateralToken).transferFrom(
msg.sender,
address(this),
collateral - loan.collateral
);
} else if (collateral < loan.collateral) {
IERC20(loan.collateralToken).transfer(
msg.sender,
loan.collateral - collateral
);
}
emit Repaid(
msg.sender,
loan.lender,
loanId,
debt,
collateral,
loan.interestRate,
loan.startTimestamp
);
loans[loanId].collateral = collateral;
loans[loanId].interestRate = pool.interestRate;
loans[loanId].startTimestamp = block.timestamp;
loans[loanId].auctionStartTimestamp = type(uint256).max;
loans[loanId].auctionLength = pool.auctionLength;
loans[loanId].lender = pool.lender;
emit Borrowed(
msg.sender,
pool.lender,
loanId,
debt,
collateral,
pool.interestRate,
block.timestamp
);
emit Refinanced(loanId);
unchecked {
i++;
}
}
}