diff --git a/.gas-snapshot b/.gas-snapshot index f97179ba..d29b4bbc 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,8 +1,12 @@ DiffFuzzTestStarPortLib:testSpentToReceived((uint8,address,uint256,uint256)[]) (runs: 256, μ: 880501, ~: 882340) DiffFuzzTestStarPortLib:testUnboundSpentToReceived((uint8,address,uint256,uint256)[]) (runs: 256, μ: 232899, ~: 237832) -TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallBase() (gas: 1041770) -TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLender() (gas: 693528) -TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLiquidation() (gas: 773097) +TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallBase() (gas: 1041013) +TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLender() (gas: 693016) +TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLiquidation() (gas: 772585) +TestBorrowerEnforcer:testBERevertAdditionalTransfers() (gas: 73101) +TestBorrowerEnforcer:testBERevertInvalidLoanTerms() (gas: 78338) +TestBorrowerEnforcer:testBEValidLoanTerms() (gas: 69429) +TestBorrowerEnforcer:testBEValidLoanTermsAnyIssuer() (gas: 69581) TestCustodian:testCannotLazyMintTwice() (gas: 76686) TestCustodian:testCannotMintInvalidLoanInvalidCustodian() (gas: 66883) TestCustodian:testCannotMintInvalidLoanValidCustodian() (gas: 72511) @@ -11,9 +15,9 @@ TestCustodian:testDefaultCustodySelectorRevert() (gas: 70105) TestCustodian:testGenerateOrderInvalidHandlerExecution() (gas: 132855) TestCustodian:testGenerateOrderRepay() (gas: 164353) TestCustodian:testGenerateOrderRepayAsRepayApprovedBorrower() (gas: 190000) -TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNative() (gas: 848490) -TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNativeHandlerAuthorized() (gas: 790122) -TestCustodian:testGenerateOrderRepayERC1155WithRevert() (gas: 520526) +TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNative() (gas: 847956) +TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNativeHandlerAuthorized() (gas: 789588) +TestCustodian:testGenerateOrderRepayERC1155WithRevert() (gas: 520259) TestCustodian:testGenerateOrderRepayInvalidHookAddress() (gas: 90249) TestCustodian:testGenerateOrderRepayInvalidHookReturnType() (gas: 84653) TestCustodian:testGenerateOrderRepayNotBorrower() (gas: 96449) @@ -41,48 +45,53 @@ TestCustodian:testSupportsInterface() (gas: 9428) TestCustodian:testSymbol() (gas: 7149) TestCustodian:testTokenURI() (gas: 64839) TestCustodian:testTokenURIInvalidLoan() (gas: 13218) -TestLoanCombinations:testLoan20For721SimpleInterestDutchFixedRepay() (gas: 533373) -TestLoanCombinations:testLoan20for20SimpleInterestDutchFixedRepay() (gas: 502230) -TestLoanCombinations:testLoan721for20SimpleInterestDutchFixedRepay() (gas: 552096) -TestLoanCombinations:testLoanAstariaSettlementRepay() (gas: 541954) -TestLoanCombinations:testLoanSimpleInterestEnglishFixed() (gas: 546614) -TestLoanManager:testAdditionalTransfers() (gas: 293327) -TestLoanManager:testCannotIssueSameLoanTwice() (gas: 331775) +TestLenderEnforcer:testLERevertAdditionalTransfersFromLender() (gas: 73760) +TestLenderEnforcer:testLERevertInvalidLoanTerms() (gas: 78385) +TestLenderEnforcer:testLEValidLoanTerms() (gas: 69429) +TestLenderEnforcer:testLEValidLoanTermsAnyBorrower() (gas: 69516) +TestLenderEnforcer:testLEValidLoanTermsWithAdditionalTransfers() (gas: 70757) +TestLoanCombinations:testLoan20For721SimpleInterestDutchFixedRepay() (gas: 532861) +TestLoanCombinations:testLoan20for20SimpleInterestDutchFixedRepay() (gas: 501718) +TestLoanCombinations:testLoan721for20SimpleInterestDutchFixedRepay() (gas: 551584) +TestLoanCombinations:testLoanAstariaSettlementRepay() (gas: 541442) +TestLoanCombinations:testLoanSimpleInterestEnglishFixed() (gas: 546102) +TestLoanManager:testAdditionalTransfers() (gas: 293082) +TestLoanManager:testCannotIssueSameLoanTwice() (gas: 331285) TestLoanManager:testCannotOriginateWhilePaused() (gas: 87923) TestLoanManager:testCannotSettleInvalidLoan() (gas: 72594) TestLoanManager:testCannotSettleUnlessValidCustodian() (gas: 68750) TestLoanManager:testCaveatEnforcerRevert() (gas: 119155) -TestLoanManager:testDefaultFeeRake() (gas: 352730) -TestLoanManager:testExoticDebtWithNoCaveatsNotAsBorrower() (gas: 342979) +TestLoanManager:testDefaultFeeRake() (gas: 352463) +TestLoanManager:testExoticDebtWithNoCaveatsNotAsBorrower() (gas: 342734) TestLoanManager:testInitializedFlagSetProperly() (gas: 65262) -TestLoanManager:testInvalidAmountCollateral() (gas: 153078) -TestLoanManager:testInvalidAmountCollateral721() (gas: 153221) -TestLoanManager:testInvalidAmountDebt() (gas: 177389) -TestLoanManager:testInvalidIdentifierDebt() (gas: 197383) -TestLoanManager:testInvalidItemType() (gas: 139126) -TestLoanManager:testInvalidTransferLengthCollateral() (gas: 161295) -TestLoanManager:testInvalidTransferLengthDebt() (gas: 165838) +TestLoanManager:testInvalidAmountCollateral() (gas: 152833) +TestLoanManager:testInvalidAmountCollateral721() (gas: 152976) +TestLoanManager:testInvalidAmountDebt() (gas: 177144) +TestLoanManager:testInvalidIdentifierDebt() (gas: 197138) +TestLoanManager:testInvalidItemType() (gas: 138881) +TestLoanManager:testInvalidTransferLengthCollateral() (gas: 161050) +TestLoanManager:testInvalidTransferLengthDebt() (gas: 165593) TestLoanManager:testIssued() (gas: 67144) TestLoanManager:testName() (gas: 7184) -TestLoanManager:testNonDefaultCustodianCustodyCallFails() (gas: 190465) -TestLoanManager:testNonDefaultCustodianCustodyCallSuccess() (gas: 258714) +TestLoanManager:testNonDefaultCustodianCustodyCallFails() (gas: 190220) +TestLoanManager:testNonDefaultCustodianCustodyCallSuccess() (gas: 258469) TestLoanManager:testNonPayableFunctions() (gas: 175555) -TestLoanManager:testOverrideFeeRake() (gas: 346469) +TestLoanManager:testOverrideFeeRake() (gas: 346202) TestLoanManager:testPause() (gas: 34222) TestLoanManager:testSupportsInterface() (gas: 9181) TestLoanManager:testSymbol() (gas: 7235) -TestLoanManager:testTokenNoCodeCollateral() (gas: 137898) -TestLoanManager:testTokenNoCodeDebt() (gas: 170934) +TestLoanManager:testTokenNoCodeCollateral() (gas: 137653) +TestLoanManager:testTokenNoCodeDebt() (gas: 170689) TestLoanManager:testTokenURI() (gas: 64892) TestLoanManager:testTokenURIInvalidLoan() (gas: 13244) TestLoanManager:testTransferFromFail() (gas: 80176) TestLoanManager:testUnPause() (gas: 14291) -TestNewLoan:testBuyNowPayLater() (gas: 2831964) -TestNewLoan:testNewLoanERC721CollateralDefaultTerms2() (gas: 392402) +TestNewLoan:testBuyNowPayLater() (gas: 2831719) +TestNewLoan:testNewLoanERC721CollateralDefaultTerms2() (gas: 391890) TestNewLoan:testNewLoanERC721CollateralLessDebtThanOffered() (gas: 2259) TestNewLoan:testNewLoanRefinanceNew() (gas: 207) TestNewLoan:testNewLoanViaOriginatorBorrowerApprovalAndLenderApproval() (gas: 299336) -TestNewLoan:testNewLoanViaOriginatorLenderApproval() (gas: 354219) +TestNewLoan:testNewLoanViaOriginatorLenderApproval() (gas: 353952) TestNewLoan:testSettleLoan() (gas: 163) TestRefStarPortLib:testSpentToReceived() (gas: 13315) TestRefStarPortLib:testValidateSalt(address,bytes32) (runs: 256, μ: 33865, ~: 33865) @@ -100,6 +109,6 @@ TestStrategistOriginator:testInvalidDebtAmountAskingMoreThanOffered() (gas: 2065 TestStrategistOriginator:testInvalidDebtAmountOfferingZero() (gas: 186903) TestStrategistOriginator:testInvalidDebtAmountRequestingZero() (gas: 206801) TestStrategistOriginator:testInvalidDebtLength() (gas: 205428) -TestStrategistOriginator:testInvalidOffer() (gas: 396592) +TestStrategistOriginator:testInvalidOffer() (gas: 396325) TestStrategistOriginator:testInvalidSigner() (gas: 208639) TestStrategistOriginator:testSetStrategist() (gas: 17818) \ No newline at end of file diff --git a/src/enforcers/BorrowerEnforcer.sol b/src/enforcers/BorrowerEnforcer.sol index ae504e39..d6186f23 100644 --- a/src/enforcers/BorrowerEnforcer.sol +++ b/src/enforcers/BorrowerEnforcer.sol @@ -14,19 +14,23 @@ contract BorrowerEnforcer is CaveatEnforcer { LoanManager.Loan loan; } + /// @notice Enforces that the loan terms are identical except for the issuer + /// @notice The issuer is allowed to be any address + /// @notice No additional transfers are permitted + /// @param additionalTransfers The additional transfers to be made + /// @param loan The loan terms + /// @param caveatData The borrowers encoded details function validate( ConduitTransfer[] calldata additionalTransfers, LoanManager.Loan calldata loan, bytes calldata caveatData ) public view virtual override { - bytes32 loanHash = keccak256(abi.encode(loan)); - Details memory details = abi.decode(caveatData, (Details)); - if (details.loan.borrower != loan.borrower) revert BorrowerOnlyEnforcer(); details.loan.issuer = loan.issuer; - if (loanHash != keccak256(abi.encode(details.loan))) revert InvalidLoanTerms(); + if (keccak256(abi.encode(loan)) != keccak256(abi.encode(details.loan))) revert InvalidLoanTerms(); + //Should additional transfers from the accounts other than the borrower be allowed? if (additionalTransfers.length > 0) revert InvalidAdditionalTransfer(); } } diff --git a/src/enforcers/LenderEnforcer.sol b/src/enforcers/LenderEnforcer.sol index 0b6ed7fc..53e35314 100644 --- a/src/enforcers/LenderEnforcer.sol +++ b/src/enforcers/LenderEnforcer.sol @@ -13,13 +13,18 @@ contract LenderEnforcer is CaveatEnforcer { LoanManager.Loan loan; } + /// @notice Enforces that the loan terms are identical except for the borrower + /// @notice The borrower is allowed to be any address + /// @notice No additional transfers from the issuer are permitted + /// @param additionalTransfers The additional transfers to be made + /// @param loan The loan terms + /// @param caveatData The borrowers encoded details function validate( ConduitTransfer[] calldata additionalTransfers, LoanManager.Loan calldata loan, bytes calldata caveatData ) public view virtual override { Details memory details = abi.decode(caveatData, (Details)); - if (details.loan.issuer != loan.issuer) revert LenderOnlyEnforcer(); details.loan.borrower = loan.borrower; if (keccak256(abi.encode(loan)) != keccak256(abi.encode(details.loan))) { diff --git a/test/unit-testing/TestBorrowerEnforcer.sol b/test/unit-testing/TestBorrowerEnforcer.sol new file mode 100644 index 00000000..ba1b836c --- /dev/null +++ b/test/unit-testing/TestBorrowerEnforcer.sol @@ -0,0 +1,46 @@ +import "starport-test/StarPortTest.sol"; +import {BorrowerEnforcer} from "starport-core/enforcers/BorrowerEnforcer.sol"; +import {ConduitTransfer, ConduitItemType} from "seaport-types/src/conduit/lib/ConduitStructs.sol"; + +import "forge-std/console.sol"; + +contract TestBorrowerEnforcer is StarPortTest { + function testBERevertAdditionalTransfers() external { + ConduitTransfer[] memory additionalTransfers = new ConduitTransfer[](1); + additionalTransfers[0] = ConduitTransfer({ + token: address(0), + amount: 0, + to: address(0), + from: address(0), + identifier: 0, + itemType: ConduitItemType.ERC20 + }); + + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + BorrowerEnforcer.Details memory details = BorrowerEnforcer.Details({loan: loan}); + vm.expectRevert(BorrowerEnforcer.InvalidAdditionalTransfer.selector); + borrowerEnforcer.validate(additionalTransfers, loan, abi.encode(details)); + } + + function testBERevertInvalidLoanTerms() external { + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + + BorrowerEnforcer.Details memory details = BorrowerEnforcer.Details({loan: loan}); + details.loan.borrower = lender.addr; + vm.expectRevert(BorrowerEnforcer.InvalidLoanTerms.selector); + borrowerEnforcer.validate(new ConduitTransfer[](0), generateDefaultLoanTerms(), abi.encode(details)); + } + + function testBEValidLoanTerms() external view { + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + borrowerEnforcer.validate(new ConduitTransfer[](0), loan, abi.encode(BorrowerEnforcer.Details({loan: loan}))); + } + + function testBEValidLoanTermsAnyIssuer() external view { + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + BorrowerEnforcer.Details memory details = BorrowerEnforcer.Details({loan: loan}); + details.loan.issuer = address(0); + + borrowerEnforcer.validate(new ConduitTransfer[](0), loan, abi.encode(BorrowerEnforcer.Details({loan: loan}))); + } +} diff --git a/test/unit-testing/TestLenderEnforcer.sol b/test/unit-testing/TestLenderEnforcer.sol new file mode 100644 index 00000000..abf3acb6 --- /dev/null +++ b/test/unit-testing/TestLenderEnforcer.sol @@ -0,0 +1,61 @@ +import "starport-test/StarPortTest.sol"; +import {LenderEnforcer} from "starport-core/enforcers/LenderEnforcer.sol"; +import {ConduitTransfer, ConduitItemType} from "seaport-types/src/conduit/lib/ConduitStructs.sol"; + +import "forge-std/console.sol"; + +contract TestLenderEnforcer is StarPortTest { + function testLERevertAdditionalTransfersFromLender() external { + ConduitTransfer[] memory additionalTransfers = new ConduitTransfer[](1); + additionalTransfers[0] = ConduitTransfer({ + token: address(0), + amount: 0, + to: address(0), + from: lender.addr, + identifier: 0, + itemType: ConduitItemType.ERC20 + }); + + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + + vm.expectRevert(LenderEnforcer.InvalidAdditionalTransfer.selector); + lenderEnforcer.validate(additionalTransfers, loan, abi.encode(LenderEnforcer.Details({loan: loan}))); + } + + function testLERevertInvalidLoanTerms() external { + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + + LenderEnforcer.Details memory details = LenderEnforcer.Details({loan: loan}); + details.loan.custodian = borrower.addr; + vm.expectRevert(LenderEnforcer.InvalidLoanTerms.selector); + + lenderEnforcer.validate(new ConduitTransfer[](0), generateDefaultLoanTerms(), abi.encode(details)); + } + + function testLEValidLoanTermsWithAdditionalTransfers() external view { + ConduitTransfer[] memory additionalTransfers = new ConduitTransfer[](1); + additionalTransfers[0] = ConduitTransfer({ + token: address(0), + amount: 0, + to: address(0), + from: address(0), + identifier: 0, + itemType: ConduitItemType.ERC20 + }); + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + lenderEnforcer.validate(additionalTransfers, loan, abi.encode(LenderEnforcer.Details({loan: loan}))); + } + + function testLEValidLoanTerms() external view { + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + lenderEnforcer.validate(new ConduitTransfer[](0), loan, abi.encode(LenderEnforcer.Details({loan: loan}))); + } + + function testLEValidLoanTermsAnyBorrower() external view { + LoanManager.Loan memory loan = generateDefaultLoanTerms(); + LenderEnforcer.Details memory details = LenderEnforcer.Details({loan: loan}); + details.loan.borrower = address(0); + + lenderEnforcer.validate(new ConduitTransfer[](0), loan, abi.encode(LenderEnforcer.Details({loan: loan}))); + } +}