From 687ef2e59f18cb429df038c1c743f2107f8c87c4 Mon Sep 17 00:00:00 2001 From: GregTheDev Date: Thu, 2 Nov 2023 17:42:10 -0500 Subject: [PATCH] rebase w/ one failing test --- src/LoanManager.sol | 5 +- test/StarPortTest.sol | 2 +- test/integration-testing/TestCaveats.sol | 138 ++++++++++++++++------- test/utils/MockCall.sol | 5 +- 4 files changed, 107 insertions(+), 43 deletions(-) diff --git a/src/LoanManager.sol b/src/LoanManager.sol index 2eb3f54b..a19e5a63 100644 --- a/src/LoanManager.sol +++ b/src/LoanManager.sol @@ -242,7 +242,10 @@ contract LoanManager is Ownable, ERC721, PausableNonReentrant { loan = applyRefinanceConsiderationToLoan(loan, considerationPayment, carryPayment, pricingData); _transferSpentItems(considerationPayment, lender, loan.issuer); - _transferSpentItems(carryPayment, lender, loan.originator); + + if (carryPayment.length > 0) { + _transferSpentItems(carryPayment, loan.issuer, loan.custodian); + } loan.issuer = lender; loan.originator = address(0); diff --git a/test/StarPortTest.sol b/test/StarPortTest.sol index 4fc54fe0..029f0551 100644 --- a/test/StarPortTest.sol +++ b/test/StarPortTest.sol @@ -513,7 +513,7 @@ contract StarPortTest is BaseOrderTest { } function getRefinanceCaveat(LoanManager.Loan memory loan, bytes memory pricingData, address fulfiller) - external + public returns (LoanManager.Loan memory) { (SpentItem[] memory considerationPayment, SpentItem[] memory carryPayment,) = diff --git a/test/integration-testing/TestCaveats.sol b/test/integration-testing/TestCaveats.sol index d181072f..00fd9e50 100644 --- a/test/integration-testing/TestCaveats.sol +++ b/test/integration-testing/TestCaveats.sol @@ -4,14 +4,16 @@ import "starport-test/StarPortTest.sol"; import {DeepEq} from "starport-test/utils/DeepEq.sol"; import {MockCall} from "starport-test/utils/MockCall.sol"; import "forge-std/Test.sol"; -import {StarPortLib, Actions} from "starport-core/lib/StarPortLib.sol"; +import {StarPortLib, Actions, AdditionalTransfer} from "starport-core/lib/StarPortLib.sol"; import "forge-std/console.sol"; //Informational Finding: //If you sign a caveat and submit the caveat as the borrower or lender, then it will not be invalidated -//Can the borrower refinance there own loan in this setup? +//With the current implementations, I think finding a valid refinance may be difficult contract IntegrationTestCaveats is StarPortTest, DeepEq, MockCall { + event LogLoan(LoanManager.Loan loan); + function testOriginateWCaveats() public { LoanManager.Loan memory loan = generateDefaultLoanTerms(); @@ -27,7 +29,7 @@ contract IntegrationTestCaveats is StarPortTest, DeepEq, MockCall { _setApprovalsForSpentItems(lender.addr, loan.debt); vm.prank(loan.borrower); - LM.originate(new ConduitTransfer[](0), _emptyCaveat(), lenderCaveat, loan); + LM.originate(new AdditionalTransfer[](0), _emptyCaveat(), lenderCaveat, loan); } function testOriginateWCaveatsInvalidSalt() public { @@ -45,24 +47,52 @@ contract IntegrationTestCaveats is StarPortTest, DeepEq, MockCall { _setApprovalsForSpentItems(lender.addr, loan.debt); vm.startPrank(loan.issuer); - LM.originate(new ConduitTransfer[](0), borrowerCaveat, _emptyCaveat(), loan); + LM.originate(new AdditionalTransfer[](0), borrowerCaveat, _emptyCaveat(), loan); vm.expectRevert(StarPortLib.InvalidSalt.selector); - LM.originate(new ConduitTransfer[](0), borrowerCaveat, _emptyCaveat(), loan); + LM.originate(new AdditionalTransfer[](0), borrowerCaveat, _emptyCaveat(), loan); } function testOriginateWCaveatsInvalidSaltManual() public { - vm.prank(lender.addr); - LM.invalidateCaveatSalt(msg.sig); + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + + CaveatEnforcer.CaveatWithApproval memory borrowerCaveat = getBorrowerSignedCaveat({ + details: BorrowerEnforcer.Details({loan: loan}), + signer: borrower, + salt: bytes32(0), + enforcer: address(borrowerEnforcer) + }); + _setApprovalsForSpentItems(borrower.addr, loan.collateral); + + _setApprovalsForSpentItems(lender.addr, loan.debt); + + vm.prank(borrower.addr); + LM.invalidateCaveatSalt(0); + vm.expectRevert(StarPortLib.InvalidSalt.selector); - newLoanWithDefaultTerms(); + vm.prank(lender.addr); + LM.originate(new AdditionalTransfer[](0), borrowerCaveat, _emptyCaveat(), loan); } function testOriginateWCaveatsIncrementedNonce() public { - vm.prank(lender.addr); + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + + CaveatEnforcer.CaveatWithApproval memory borrowerCaveat = getBorrowerSignedCaveat({ + details: BorrowerEnforcer.Details({loan: loan}), + signer: borrower, + salt: bytes32(0), + enforcer: address(borrowerEnforcer) + }); + _setApprovalsForSpentItems(borrower.addr, loan.collateral); + + _setApprovalsForSpentItems(lender.addr, loan.debt); + + vm.prank(borrower.addr); LM.incrementCaveatNonce(); + vm.expectRevert(LoanManager.InvalidCaveatSigner.selector); - newLoanWithDefaultTerms(); + vm.prank(lender.addr); + LM.originate(new AdditionalTransfer[](0), borrowerCaveat, _emptyCaveat(), loan); } function testOriginateWBorrowerApproval() public { @@ -82,7 +112,7 @@ contract IntegrationTestCaveats is StarPortTest, DeepEq, MockCall { vm.prank(loan.borrower); LM.setOriginateApproval(address(0x5), LoanManager.ApprovalType.BORROWER); vm.prank(address(0x5)); - LM.originate(new ConduitTransfer[](0), _emptyCaveat(), lenderCaveat, loan); + LM.originate(new AdditionalTransfer[](0), _emptyCaveat(), lenderCaveat, loan); } function testOriginateWLenderApproval() public { @@ -100,7 +130,7 @@ contract IntegrationTestCaveats is StarPortTest, DeepEq, MockCall { vm.prank(lender.addr); LM.setOriginateApproval(address(0x5), LoanManager.ApprovalType.LENDER); vm.prank(address(0x5)); - LM.originate(new ConduitTransfer[](0), borrowerCaveat, _emptyCaveat(), loan); + LM.originate(new AdditionalTransfer[](0), borrowerCaveat, _emptyCaveat(), loan); } function testOriginateUnapprovedFulfiller() public { @@ -124,33 +154,22 @@ contract IntegrationTestCaveats is StarPortTest, DeepEq, MockCall { _setApprovalsForSpentItems(lender.addr, loan.debt); vm.prank(address(0x5)); - LM.originate(new ConduitTransfer[](0), borrowerCaveat, lenderCaveat, loan); + LM.originate(new AdditionalTransfer[](0), borrowerCaveat, lenderCaveat, loan); } - //Test Refinance with caveats - function testRefinanceWCaveats() public { + function testRefinanceWCaveatsInvalidSalt() public { LoanManager.Loan memory loan = newLoanWithDefaultTerms(); - CaveatEnforcer.CaveatWithApproval memory lenderCaveat = getLenderSignedCaveat({ - details: LenderEnforcer.Details({loan: loan}), - signer: lender, - salt: bytes32(0), - enforcer: address(lenderEnforcer) + LenderEnforcer.Details memory details = LenderEnforcer.Details({ + loan: LM.applyRefinanceConsiderationToLoan(loan, loan.debt, new SpentItem[](0), defaultPricingData) }); - _setApprovalsForSpentItems(lender.addr, loan.debt); - - vm.prank(loan.borrower); - mockIsValidRefinanceCall(loan.terms.pricing, new SpentItem[](0), new SpentItem[](0), new ConduitTransfer[](0)); - LM.refinance(lender.addr, lenderCaveat, loan, ""); - } - - function testRefinanceWCaveatsInvalidSalt() public { - LoanManager.Loan memory loan = newLoanWithDefaultTerms(); - loan.collateral[0] = _getERC20SpentItem(erc20s[0], 1000); + details.loan.issuer = lender.addr; + details.loan.originator = address(0); + details.loan.start = 0; CaveatEnforcer.CaveatWithApproval memory lenderCaveat = getLenderSignedCaveat({ - details: LenderEnforcer.Details({loan: loan}), + details: details, signer: borrower, salt: bytes32(msg.sig), enforcer: address(borrowerEnforcer) @@ -158,28 +177,51 @@ contract IntegrationTestCaveats is StarPortTest, DeepEq, MockCall { _setApprovalsForSpentItems(lender.addr, loan.debt); + vm.warp(block.timestamp + 1); + mockIsValidRefinanceCall(loan.terms.pricing, loan.debt, new SpentItem[](0), new AdditionalTransfer[](0)); vm.expectRevert(StarPortLib.InvalidSalt.selector); LM.refinance(lender.addr, lenderCaveat, loan, ""); } + function testRefinanceAsLender() public { + LoanManager.Loan memory loan = newLoanWithDefaultTerms(); + + address newLender = address(0x5); + allocateTokensAndApprovals(newLender, type(uint128).max); + _setApprovalsForSpentItems(newLender, loan.debt); + + vm.warp(block.timestamp + 1); + vm.prank(newLender); + mockIsValidRefinanceCall(loan.terms.pricing, loan.debt, new SpentItem[](0), new AdditionalTransfer[](0)); + LM.refinance(newLender, _emptyCaveat(), loan, defaultPricingData); + } + function testRefinanceWLenderApproval() public { - LoanManager.Loan memory loan = generateDefaultLoanTerms(); + LoanManager.Loan memory loan = newLoanWithDefaultTerms(); _setApprovalsForSpentItems(lender.addr, loan.debt); vm.prank(lender.addr); LM.setOriginateApproval(borrower.addr, LoanManager.ApprovalType.LENDER); + vm.warp(block.timestamp + 1); vm.prank(borrower.addr); - mockIsValidRefinanceCall(loan.terms.pricing, new SpentItem[](0), new SpentItem[](0), new ConduitTransfer[](0)); - LM.refinance(lender.addr, _emptyCaveat(), loan, ""); + mockIsValidRefinanceCall(loan.terms.pricing, loan.debt, new SpentItem[](0), new AdditionalTransfer[](0)); + LM.refinance(lender.addr, _emptyCaveat(), loan, defaultPricingData); } function testRefinanceUnapprovedFulfiller() public { LoanManager.Loan memory loan = newLoanWithDefaultTerms(); + LenderEnforcer.Details memory details = LenderEnforcer.Details({ + loan: LM.applyRefinanceConsiderationToLoan(loan, loan.debt, new SpentItem[](0), defaultPricingData) + }); + + details.loan.issuer = lender.addr; + details.loan.originator = address(0); + details.loan.start = 0; CaveatEnforcer.CaveatWithApproval memory lenderCaveat = getLenderSignedCaveat({ - details: LenderEnforcer.Details({loan: loan}), + details: details, signer: lender, salt: bytes32(0), enforcer: address(lenderEnforcer) @@ -187,12 +229,30 @@ contract IntegrationTestCaveats is StarPortTest, DeepEq, MockCall { _setApprovalsForSpentItems(lender.addr, loan.debt); + vm.warp(block.timestamp + 1); + vm.prank(loan.borrower); - mockIsValidRefinanceCall(loan.terms.pricing, new SpentItem[](0), new SpentItem[](0), new ConduitTransfer[](0)); - LM.refinance(lender.addr, lenderCaveat, loan, ""); + + mockIsValidRefinanceCall(loan.terms.pricing, loan.debt, new SpentItem[](0), new AdditionalTransfer[](0)); + + LM.refinance(lender.addr, lenderCaveat, loan, defaultPricingData); } - //Test caveat enforcement revert + function testRefinanceCaveatFailure() public { + LoanManager.Loan memory loan = newLoanWithDefaultTerms(); + + CaveatEnforcer.CaveatWithApproval memory lenderCaveat = getLenderSignedCaveat({ + details: LenderEnforcer.Details({loan: loan}), + signer: lender, + salt: bytes32(0), + enforcer: address(lenderEnforcer) + }); + + _setApprovalsForSpentItems(lender.addr, loan.debt); - //Test multiple caveats + vm.prank(loan.borrower); + mockIsValidRefinanceCall(loan.terms.pricing, loan.debt, new SpentItem[](0), new AdditionalTransfer[](0)); + vm.expectRevert(LenderEnforcer.InvalidLoanTerms.selector); + LM.refinance(lender.addr, lenderCaveat, loan, defaultPricingData); + } } diff --git a/test/utils/MockCall.sol b/test/utils/MockCall.sol index 2ea085ce..8fa2413a 100644 --- a/test/utils/MockCall.sol +++ b/test/utils/MockCall.sol @@ -1,7 +1,7 @@ pragma solidity ^0.8.17; import {ItemType, SpentItem, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; -import {ConduitTransfer} from "seaport-types/src/conduit/lib/ConduitStructs.sol"; +import {AdditionalTransfer} from "starport-core/lib/StarPortLib.sol"; import {TestBase} from "forge-std/Test.sol"; import {SettlementHook} from "src/hooks/SettlementHook.sol"; import {SettlementHandler} from "src/handlers/SettlementHandler.sol"; @@ -12,11 +12,12 @@ abstract contract MockCall is TestBase { vm.mockCall(hook, abi.encodeWithSelector(SettlementHook.isActive.selector), abi.encode(status)); } + function mockIsValidRefinanceCall( address pricing, SpentItem[] memory considerationPayment, SpentItem[] memory carryPayment, - ConduitTransfer[] memory additionalTransfers + AdditionalTransfer[] memory additionalTransfers ) public { vm.mockCall( pricing,