The addCredit
function requires the consent of both lender and borrower to proceed and create the credit. If ETH is used as the token and the lender consents first, then the borrower won't be able to give consent, preventing the credit from being created and losing the deposited ETH.
When ETH is used as the credit token and if the lender is the first to give consent, he will execute a call to addCredit
with a transaction value of the desired deposit amount of the credit.
Since he is the first to give consent, the mutualConsent
modifier will wait for the other party (borrower) before continuing execution, however the ETH has been successfully transferred from the lender to the line contract.
Now, if the borrower tries to give consent to this addCredit
transaction, the body of the function will be executed (since consent has been given by both parties) but it will get an error because LineLib.receiveTokenOrETH
will require the borrower transaction to include the ETH payment.
This will effectively prevent the addCredit
call from completing, and the lender will lose his deposit.
A different but similar issue happens if the borrower never consents, the lender has already transferred the ETH but there's no way to "cancel" that consent to refund the payment.
Lender wants to create a new credit and goes first, transferring 1 ETH. Then the borrower tries to give consent with the same params, but the call fails since it will ask the borrower for the ETH payment.
Note: the context for this test (setup, variables and helper functions) is similar to the one found in the file LineOfCredit.t.sol
.
function test_addCredit_BadMutualConsent() public {
address lender = makeAddr("lender");
_mintAndApprove(lender);
uint256 amount = 1 ether;
uint256 lenderBalanceBefore = lender.balance;
// Lender gives consent first
vm.prank(lender);
line.addCredit{value: amount}(dRate, fRate, amount, Denominations.ETH, lender);
// lender has already sent 1 ETH
assertEq(lender.balance, lenderBalanceBefore - amount);
// borrower tries to consent the credit but this will fail since addCredit will ask THE BORROWER to send the ETH value
vm.expectRevert(LineLib.TransferFailed.selector);
vm.prank(borrower);
line.addCredit(dRate, fRate, amount, Denominations.ETH, lender);
// We end up in an inconsistent state, borrower won't be able to give consent and the lender has already lost the ETH
}
- Make sure the borrower side gives consent first. or...
- Instead of using native ETH (or the equivalent in the chain this gets deployed) use the wrapped ERC20 version WETH. This will enable pull payments for ETH.