From 802befc9b82a2c6e4588d5ed85250bdc62133f80 Mon Sep 17 00:00:00 2001 From: Joseph Delong Date: Sat, 4 Nov 2023 13:42:12 -0500 Subject: [PATCH 1/3] fix: review BaseRecall --- src/status/BaseRecall.sol | 105 +++++++++++++++-------------- test/unit-testing/TestV1Status.sol | 8 +-- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/src/status/BaseRecall.sol b/src/status/BaseRecall.sol index 7261cd91..9c9bfe5a 100644 --- a/src/status/BaseRecall.sol +++ b/src/status/BaseRecall.sol @@ -86,6 +86,7 @@ abstract contract BaseRecall { function getRecallRate(Starport.Loan calldata loan) external view returns (uint256) { Details memory details = abi.decode(loan.terms.statusData, (Details)); uint256 loanId = loan.getId(); + // calculates the porportion of time elapsed, then multiplies times the max rate return details.recallMax.mulWad((block.timestamp - recalls[loanId].start).divWad(details.recallWindow)); } @@ -97,33 +98,32 @@ abstract contract BaseRecall { revert RecallBeforeHoneymoonExpiry(); } - if (loan.issuer != msg.sender && loan.borrower != msg.sender) { - AdditionalTransfer[] memory recallConsideration = _generateRecallConsideration( - loan, 0, details.recallStakeDuration, 1e18, msg.sender, payable(address(this)) - ); - StarportLib.transferAdditionalTransfers(recallConsideration); - } uint256 loanId = loan.getId(); - - if (!SP.active(loanId)) { + if (SP.inactive(loanId)) { revert LoanDoesNotExist(); } if (recalls[loanId].start > 0) { revert RecallAlreadyExists(); } + + AdditionalTransfer[] memory recallConsideration = _generateRecallConsideration( + msg.sender, loan, 0, details.recallStakeDuration, 1e18, msg.sender, payable(address(this)) + ); + if (recallConsideration.length > 0) { + StarportLib.transferAdditionalTransfers(recallConsideration); + } + recalls[loanId] = Recall(payable(msg.sender), uint64(block.timestamp)); emit Recalled(loanId, msg.sender, block.timestamp + details.recallWindow); } // transfers all stake to anyone who asks after the LM token is burned function withdraw(Starport.Loan calldata loan, address receiver) external { - Details memory details = abi.decode(loan.terms.statusData, (Details)); - bytes memory encodedLoan = abi.encode(loan); - uint256 loanId = uint256(keccak256(encodedLoan)); + uint256 loanId = loan.getId(); // loan has not been refinanced, loan is still active. SP.tokenId changes on refinance - if (!SP.inactive(loanId)) { + if (SP.active(loanId)) { revert LoanHasNotBeenRefinanced(); } @@ -133,37 +133,28 @@ abstract contract BaseRecall { revert WithdrawDoesNotExist(); } - if (loan.issuer != recall.recaller && loan.borrower != recall.recaller) { - AdditionalTransfer[] memory recallConsideration = - _generateRecallConsideration(loan, 0, details.recallStakeDuration, 1e18, address(this), receiver); - recall.recaller = payable(address(0)); - recall.start = 0; - - for (uint256 i; i < recallConsideration.length;) { - if (loan.debt[i].itemType != ItemType.ERC20) { - revert InvalidItemType(); - } - - ERC20(loan.debt[i].token).transfer(receiver, recallConsideration[i].amount); + Details memory details = abi.decode(loan.terms.statusData, (Details)); + AdditionalTransfer[] memory recallConsideration = _generateRecallConsideration( + recall.recaller, loan, 0, details.recallStakeDuration, 1e18, address(this), receiver + ); - unchecked { - ++i; - } - } + if (recallConsideration.length > 0) { + _withdrawRecallStake(recallConsideration); } + recall.recaller = payable(address(0)); + recall.start = 0; + emit Withdraw(loanId, receiver); } - function _getRecallStake(Starport.Loan memory loan, uint256 start, uint256 end) - internal - view - returns (uint256[] memory recallStake) - { - BasePricing.Details memory details = abi.decode(loan.terms.pricingData, (BasePricing.Details)); - recallStake = new uint256[](loan.debt.length); - for (uint256 i; i < loan.debt.length;) { - recallStake[i] = BasePricing(loan.terms.pricing).getInterest(loan, details.rate, start, end, i); + function _withdrawRecallStake(AdditionalTransfer[] memory transfers) internal { + uint256 i = 0; + for (i; i < transfers.length;) { + if (transfers[i].itemType != ItemType.ERC20) { + revert InvalidItemType(); + } + ERC20(transfers[i].token).transfer(transfers[i].to, transfers[i].amount); unchecked { ++i; @@ -177,10 +168,13 @@ abstract contract BaseRecall { returns (AdditionalTransfer[] memory consideration) { Details memory details = abi.decode(loan.terms.statusData, (Details)); - return _generateRecallConsideration(loan, 0, details.recallStakeDuration, proportion, from, to); + uint256 loanId = loan.getId(); + Recall memory recall = recalls[loanId]; + return _generateRecallConsideration(recall.recaller, loan, 0, details.recallStakeDuration, proportion, from, to); } function _generateRecallConsideration( + address recaller, Starport.Loan calldata loan, uint256 start, uint256 end, @@ -188,21 +182,28 @@ abstract contract BaseRecall { address from, address to ) internal view returns (AdditionalTransfer[] memory additionalTransfers) { - uint256[] memory stake = _getRecallStake(loan, start, end); - additionalTransfers = new AdditionalTransfer[](stake.length); - - for (uint256 i; i < additionalTransfers.length;) { - additionalTransfers[i] = AdditionalTransfer({ - itemType: loan.debt[i].itemType, - identifier: loan.debt[i].identifier, - amount: stake[i].mulWad(proportion), - token: loan.debt[i].token, - from: from, - to: to - }); - unchecked { - ++i; + if (loan.issuer != recaller && loan.borrower != recaller) { + additionalTransfers = new AdditionalTransfer[](loan.debt.length); + + uint256 delta_t = end - start; + BasePricing.Details memory details = abi.decode(loan.terms.pricingData, (BasePricing.Details)); + for (uint256 i; i < additionalTransfers.length;) { + uint256 stake = + BasePricing(loan.terms.pricing).calculateInterest(delta_t, loan.debt[i].amount, details.rate); + additionalTransfers[i] = AdditionalTransfer({ + itemType: loan.debt[i].itemType, + identifier: loan.debt[i].identifier, + amount: stake.mulWad(proportion), + token: loan.debt[i].token, + from: from, + to: to + }); + unchecked { + ++i; + } } + } else { + additionalTransfers = new AdditionalTransfer[](0); } } } diff --git a/test/unit-testing/TestV1Status.sol b/test/unit-testing/TestV1Status.sol index 617802ed..7ba443c6 100644 --- a/test/unit-testing/TestV1Status.sol +++ b/test/unit-testing/TestV1Status.sol @@ -80,7 +80,7 @@ contract TestAstariaV1Status is AstariaV1Test, DeepEq { erc20s[0].approve(loan.terms.status, 10e18); skip(details.honeymoon); - vm.mockCall(address(SP), abi.encodeWithSelector(SP.active.selector, loan.getId()), abi.encode(false)); + vm.mockCall(address(SP), abi.encodeWithSelector(SP.inactive.selector, loan.getId()), abi.encode(true)); vm.expectRevert(abi.encodeWithSelector(BaseRecall.LoanDoesNotExist.selector)); AstariaV1Status(loan.terms.status).recall(loan); } @@ -103,10 +103,10 @@ contract TestAstariaV1Status is AstariaV1Test, DeepEq { BaseRecall.Details memory details = abi.decode(loan.terms.statusData, (BaseRecall.Details)); skip(details.honeymoon); - vm.mockCall(address(SP), abi.encodeWithSelector(SP.active.selector, loan.getId()), abi.encode(true)); + vm.mockCall(address(SP), abi.encodeWithSelector(SP.inactive.selector, loan.getId()), abi.encode(false)); AstariaV1Status(loan.terms.status).recall(loan); skip(details.recallWindow); - vm.mockCall(address(SP), abi.encodeWithSelector(SP.inactive.selector, loan.getId()), abi.encode(true)); + vm.mockCall(address(SP), abi.encodeWithSelector(SP.active.selector, loan.getId()), abi.encode(false)); vm.expectRevert(abi.encodeWithSelector(BaseRecall.InvalidItemType.selector)); AstariaV1Status(loan.terms.status).withdraw(loan, payable(address(this))); } @@ -254,7 +254,7 @@ contract TestAstariaV1Status is AstariaV1Test, DeepEq { }); Starport.Loan memory loan = _createLoan721Collateral20Debt({lender: lender.addr, borrowAmount: 1e18, terms: terms}); - vm.mockCall(address(SP), abi.encodeWithSelector(SP.inactive.selector, loan.getId()), abi.encode(true)); + vm.mockCall(address(SP), abi.encodeWithSelector(SP.active.selector, loan.getId()), abi.encode(false)); vm.expectRevert(abi.encodeWithSelector(BaseRecall.WithdrawDoesNotExist.selector)); AstariaV1Status(loan.terms.status).withdraw(loan, payable(address(this))); } From 1e7a9a5d29c355966dbc166ae1861e896121a06c Mon Sep 17 00:00:00 2001 From: Andrew Redden Date: Sat, 4 Nov 2023 16:37:30 -0300 Subject: [PATCH 2/3] add postRepayment call to refinance flow, update v1 tests to conform with new withdraw mechanisms inside the v1 settlement --- src/Custodian.sol | 11 ++---- src/Starport.sol | 15 ++++++++ src/settlement/AstariaV1Settlement.sol | 17 +++++---- src/settlement/DutchAuctionSettlement.sol | 4 +-- src/settlement/Settlement.sol | 2 +- .../integration-testing/TestAstariaV1Loan.sol | 24 ++++--------- test/unit-testing/TestV1Status.sol | 36 +++++++++++++++++++ 7 files changed, 74 insertions(+), 35 deletions(-) diff --git a/src/Custodian.sol b/src/Custodian.sol index 99f49a04..55492362 100644 --- a/src/Custodian.sol +++ b/src/Custodian.sol @@ -412,14 +412,12 @@ contract Custodian is ERC721, ContractOffererInterface { */ function _postSettlementExecute(Starport.Loan memory loan, address fulfiller) internal virtual { _beforeSettlementHandlerHook(loan); - if ( - Settlement(loan.terms.settlement).postSettlement{gas: 100_000}(loan, fulfiller) - != Settlement.postSettlement.selector - ) { + if (Settlement(loan.terms.settlement).postSettlement(loan, fulfiller) != Settlement.postSettlement.selector) { revert InvalidPostSettlement(); } _afterSettlementHandlerHook(loan); } + /** * @dev settle the loan with the LoanManager * @@ -429,10 +427,7 @@ contract Custodian is ERC721, ContractOffererInterface { function _postRepaymentExecute(Starport.Loan memory loan, address fulfiller) internal virtual { _beforeSettlementHandlerHook(loan); - if ( - Settlement(loan.terms.settlement).postRepayment{gas: 100_000}(loan, fulfiller) - != Settlement.postRepayment.selector - ) { + if (Settlement(loan.terms.settlement).postRepayment(loan, fulfiller) != Settlement.postRepayment.selector) { revert InvalidPostRepayment(); } _afterSettlementHandlerHook(loan); diff --git a/src/Starport.sol b/src/Starport.sol index 681303f6..4038675e 100644 --- a/src/Starport.sol +++ b/src/Starport.sol @@ -36,6 +36,7 @@ import {CaveatEnforcer} from "starport-core/enforcers/CaveatEnforcer.sol"; import {Ownable} from "solady/src/auth/Ownable.sol"; import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol"; import {PausableNonReentrant} from "starport-core/lib/PausableNonReentrant.sol"; +import {Settlement} from "starport-core/settlement/Settlement.sol"; contract Starport is ERC721, PausableNonReentrant { using FixedPointMathLib for uint256; @@ -121,6 +122,7 @@ contract Starport is ERC721, PausableNonReentrant { error UnauthorizedAdditionalTransferIncluded(); error InvalidCaveatSigner(); error MalformedRefinance(); + error InvalidPostRepayment(); constructor(ConsiderationInterface seaport_) { address custodian = address(new Custodian(this, seaport_)); @@ -227,6 +229,7 @@ contract Starport is ERC721, PausableNonReentrant { ) = Pricing(loan.terms.pricing).getRefinanceConsideration(loan, pricingData, msg.sender); _settle(loan); + _postRepaymentExecute(loan, msg.sender); loan = applyRefinanceConsiderationToLoan(loan, considerationPayment, carryPayment, pricingData); StarportLib.transferSpentItems(considerationPayment, lender, loan.issuer, false); @@ -251,6 +254,18 @@ contract Starport is ERC721, PausableNonReentrant { _issueLoan(loan); } + /** + * @dev settle the loan with the LoanManager + * + * @param loan The the loan that is settled + * @param fulfiller The address executing seaport + */ + function _postRepaymentExecute(Starport.Loan memory loan, address fulfiller) internal virtual { + if (Settlement(loan.terms.settlement).postRepayment(loan, fulfiller) != Settlement.postRepayment.selector) { + revert InvalidPostRepayment(); + } + } + function applyRefinanceConsiderationToLoan( Starport.Loan memory loan, SpentItem[] memory considerationPayment, diff --git a/src/settlement/AstariaV1Settlement.sol b/src/settlement/AstariaV1Settlement.sol index f0387af6..304a9545 100644 --- a/src/settlement/AstariaV1Settlement.sol +++ b/src/settlement/AstariaV1Settlement.sol @@ -150,15 +150,20 @@ contract AstariaV1Settlement is DutchAuctionSettlement { override returns (bytes4) { - // TODO: do we need the commented out code if we dont care about reverts anyways, seems like extra gas - // (address recaller, uint64 recallStart) = BaseRecall(loan.terms.status).recalls(loan.getId()); - // if (recallStart != 0 || recaller != address(0)) { - //we dont wanna revert if theres ever a halt in the underlying call, settlement must complete - loan.terms.status.call(abi.encodeWithSelector(BaseRecall.withdraw.selector, loan, fulfiller)); - // } + _executeWithdraw(loan, fulfiller); return Settlement.postSettlement.selector; } + function postRepayment(Starport.Loan calldata loan, address fulfiller) external virtual override returns (bytes4) { + _executeWithdraw(loan, fulfiller); + + return Settlement.postRepayment.selector; + } + + function _executeWithdraw(Starport.Loan calldata loan, address fulfiller) internal { + loan.terms.status.call(abi.encodeWithSelector(BaseRecall.withdraw.selector, loan, fulfiller)); + } + function validate(Starport.Loan calldata loan) external view virtual override returns (bool) { if (loan.terms.settlement != address(this)) { revert InvalidHandler(); diff --git a/src/settlement/DutchAuctionSettlement.sol b/src/settlement/DutchAuctionSettlement.sol index 4a31e126..f4041d77 100644 --- a/src/settlement/DutchAuctionSettlement.sol +++ b/src/settlement/DutchAuctionSettlement.sol @@ -16,9 +16,7 @@ import {Starport, Settlement} from "starport-core/settlement/Settlement.sol"; import {BasePricing} from "starport-core/pricing/BasePricing.sol"; abstract contract DutchAuctionSettlement is Settlement, AmountDeriver { - constructor(Starport SP_) Settlement(SP_) { - SP = SP_; - } + constructor(Starport SP_) Settlement(SP_) {} using FixedPointMathLib for uint256; diff --git a/src/settlement/Settlement.sol b/src/settlement/Settlement.sol index 81d336f1..64d609cb 100644 --- a/src/settlement/Settlement.sol +++ b/src/settlement/Settlement.sol @@ -26,7 +26,7 @@ import {SpentItem, ReceivedItem} from "seaport-types/src/lib/ConsiderationStruct import {TokenReceiverInterface} from "starport-core/interfaces/TokenReceiverInterface.sol"; abstract contract Settlement is TokenReceiverInterface { - Starport SP; + Starport public immutable SP; constructor(Starport SP_) { SP = SP_; diff --git a/test/integration-testing/TestAstariaV1Loan.sol b/test/integration-testing/TestAstariaV1Loan.sol index 90b4c217..9d370873 100644 --- a/test/integration-testing/TestAstariaV1Loan.sol +++ b/test/integration-testing/TestAstariaV1Loan.sol @@ -152,7 +152,7 @@ contract TestAstariaV1Loan is AstariaV1Test { erc20s[0].approve(address(SP), stake); refinanceLoan(loan, pricingData, address(this), refinancerCaveat, refinancer.addr); - console.log("here2"); + console.log("here2", stake); } uint256 delta_t = block.timestamp - loan.start; @@ -182,19 +182,17 @@ contract TestAstariaV1Loan is AstariaV1Test { ); { - uint256 oldOriginatorAfter = erc20s[0].balanceOf(loan.originator); assertEq( - oldOriginatorAfter, + erc20s[0].balanceOf(loan.originator), oldOriginatorBefore + interest.mulWad(pricingDetails.carryRate), "Carry payment to old originator calculated incorrectly" ); } { - uint256 newFullfillerAfter = erc20s[0].balanceOf(address(this)); assertEq( - newFullfillerAfter, - newFullfillerBefore - stake, + erc20s[0].balanceOf(address(this)), + newFullfillerBefore, "New fulfiller did not repay recaller stake correctly" ); } @@ -205,22 +203,14 @@ contract TestAstariaV1Loan is AstariaV1Test { } } { - uint256 withdrawerBalanceBefore = erc20s[0].balanceOf(address(this)); uint256 recallContractBalanceBefore = erc20s[0].balanceOf(address(status)); BaseRecall recallContract = BaseRecall(address(status)); // attempt a withdraw after the loan has been successfully refinanced - recallContract.withdraw(loan, payable(address(this))); - uint256 withdrawerBalanceAfter = erc20s[0].balanceOf(address(this)); + // recallContract.withdraw(loan, payable(address(this))); + uint256 recallContractBalanceAfter = erc20s[0].balanceOf(address(status)); - assertEq( - withdrawerBalanceBefore + stake, withdrawerBalanceAfter, "Withdrawer did not recover stake as expected" - ); - assertEq( - recallContractBalanceBefore - stake, - recallContractBalanceAfter, - "BaseRecall did not return the stake as expected" - ); + assertEq(recallContractBalanceAfter, uint256(0), "BaseRecall did get emptied as expected"); } } diff --git a/test/unit-testing/TestV1Status.sol b/test/unit-testing/TestV1Status.sol index 7ba443c6..08ae032b 100644 --- a/test/unit-testing/TestV1Status.sol +++ b/test/unit-testing/TestV1Status.sol @@ -61,6 +61,42 @@ contract TestAstariaV1Status is AstariaV1Test, DeepEq { assert(AstariaV1Status(loan.terms.status).isRecalled(loan)); } + function testRecallAndRefinanceInsideWindow() public { + Starport.Terms memory terms = Starport.Terms({ + status: address(status), + settlement: address(settlement), + pricing: address(pricing), + pricingData: defaultPricingData, + settlementData: defaultSettlementData, + statusData: defaultStatusData + }); + Starport.Loan memory loan = + _createLoan721Collateral20Debt({lender: lender.addr, borrowAmount: 1e18, terms: terms}); + uint256 loanId = loan.getId(); + + BaseRecall.Details memory details = abi.decode(loan.terms.statusData, (BaseRecall.Details)); + + erc20s[0].mint(address(this), 10e18); + erc20s[0].approve(loan.terms.status, 10e18); + + skip(details.honeymoon); + vm.expectEmit(); + emit Recalled(loanId, address(this), block.timestamp + details.recallWindow); + AstariaV1Status(loan.terms.status).recall(loan); + (address recaller, uint64 recallStart) = AstariaV1Status(loan.terms.status).recalls(loanId); + skip(details.recallWindow - 1); + address newLender = address(55); + + BasePricing.Details memory newPricingData = abi.decode(defaultPricingData, (BasePricing.Details)); + newPricingData.rate = newPricingData.rate * 2; + + vm.startPrank(newLender); + erc20s[0].mint(newLender, 10e18); + erc20s[0].approve(address(SP), 10e18); + SP.refinance(newLender, _emptyCaveat(), loan, abi.encode(newPricingData)); + assert(erc20s[0].balanceOf(address(loan.terms.status)) == 0); + } + function testInvalidRecallLoanDoesNotExist() public { Starport.Terms memory terms = Starport.Terms({ status: address(status), From aa82a1fd6212e9e2e0dd287d4441322237f8d7b7 Mon Sep 17 00:00:00 2001 From: Andrew Redden Date: Sat, 4 Nov 2023 16:43:23 -0300 Subject: [PATCH 3/3] chore: update snapshot --- .gas-snapshot | 97 ++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index eda659a8..27952673 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,5 +1,5 @@ -DiffFuzzTestStarportLib:testSpentToReceived((uint8,address,uint256,uint256)[]) (runs: 256, μ: 880557, ~: 883183) -DiffFuzzTestStarportLib:testUnboundSpentToReceived((uint8,address,uint256,uint256)[]) (runs: 256, μ: 232911, ~: 237832) +DiffFuzzTestStarportLib:testSpentToReceived((uint8,address,uint256,uint256)[]) (runs: 256, μ: 880612, ~: 882686) +DiffFuzzTestStarportLib:testUnboundSpentToReceived((uint8,address,uint256,uint256)[]) (runs: 256, μ: 232899, ~: 237832) IntegrationTestCaveats:testOriginateUnapprovedFulfiller() (gas: 341166) IntegrationTestCaveats:testOriginateWBorrowerApproval() (gas: 298037) IntegrationTestCaveats:testOriginateWCaveats() (gas: 272104) @@ -7,24 +7,24 @@ IntegrationTestCaveats:testOriginateWCaveatsIncrementedNonce() (gas: 200214) IntegrationTestCaveats:testOriginateWCaveatsInvalidSalt() (gas: 257759) IntegrationTestCaveats:testOriginateWCaveatsInvalidSaltManual() (gas: 175780) IntegrationTestCaveats:testOriginateWLenderApproval() (gas: 298091) -IntegrationTestCaveats:testRefinanceAsLender() (gas: 1036148) -IntegrationTestCaveats:testRefinanceCaveatFailure() (gas: 402330) +IntegrationTestCaveats:testRefinanceAsLender() (gas: 1042171) +IntegrationTestCaveats:testRefinanceCaveatFailure() (gas: 408353) IntegrationTestCaveats:testRefinanceLoanStartAtBlockTimestampInvalidLoan() (gas: 377358) -IntegrationTestCaveats:testRefinanceUnapprovedFulfiller() (gas: 459637) -IntegrationTestCaveats:testRefinanceWCaveatsInvalidSalt() (gas: 380750) -IntegrationTestCaveats:testRefinanceWLenderApproval() (gas: 405252) +IntegrationTestCaveats:testRefinanceUnapprovedFulfiller() (gas: 465660) +IntegrationTestCaveats:testRefinanceWCaveatsInvalidSalt() (gas: 386773) +IntegrationTestCaveats:testRefinanceWLenderApproval() (gas: 411275) PausableNonReentrantImpl:test() (gas: 2442) PausableNonReentrantImpl:testReentrancy() (gas: 2735) -TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallBase() (gas: 1021149) -TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLender() (gas: 727571) -TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLiquidation() (gas: 1404896) -TestAstariaV1Pricing:testGetRefiannceConsiderationValidEqualRate() (gas: 146135) -TestAstariaV1Pricing:testGetRefiannceConsiderationValidHigherRate() (gas: 158343) -TestAstariaV1Pricing:testGetRefiannceConsiderationValidLowerRate() (gas: 153372) +TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallBase() (gas: 1009322) +TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLender() (gas: 711557) +TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLiquidation() (gas: 1382121) +TestAstariaV1Pricing:testGetRefiannceConsiderationValidEqualRate() (gas: 143045) +TestAstariaV1Pricing:testGetRefiannceConsiderationValidHigherRate() (gas: 155206) +TestAstariaV1Pricing:testGetRefiannceConsiderationValidLowerRate() (gas: 150235) TestAstariaV1Pricing:testGetRefinanceConsiderationAsBorrowerZeroRate() (gas: 81159) -TestAstariaV1Pricing:testGetRefinanceConsiderationInsufficientRefinance() (gas: 108010) -TestAstariaV1Pricing:testGetRefinanceConsiderationInvalidRefinance() (gas: 88264) -TestAstariaV1Pricing:testGetRefinanceConsiderationZeroRate() (gas: 138753) +TestAstariaV1Pricing:testGetRefinanceConsiderationInsufficientRefinance() (gas: 107916) +TestAstariaV1Pricing:testGetRefinanceConsiderationInvalidRefinance() (gas: 88217) +TestAstariaV1Pricing:testGetRefinanceConsiderationZeroRate() (gas: 135663) TestAstariaV1Settlement:testGetAuctionStart() (gas: 433743) TestAstariaV1Settlement:testGetAuctionStartNotStarted() (gas: 433316) TestAstariaV1Settlement:testGetCurrentAuctionPrice() (gas: 448527) @@ -34,17 +34,18 @@ TestAstariaV1Settlement:testGetSettlementFailedDutchAuction() (gas: 448917) TestAstariaV1Settlement:testGetSettlementLoanNotRecalled() (gas: 439423) TestAstariaV1Settlement:testV1SettlementHandlerValidate() (gas: 419993) TestAstariaV1Settlement:testV1SettlementHandlerValidateInvalidHandler() (gas: 420117) -TestAstariaV1Status:testCannotRecallTwice() (gas: 580122) -TestAstariaV1Status:testCannotWithdrawLoanHasNotBeenRefinanced() (gas: 425694) -TestAstariaV1Status:testCannotWithdrawWithdrawDoesNotExist() (gas: 432123) -TestAstariaV1Status:testGenerateRecallConsideration() (gas: 481364) -TestAstariaV1Status:testInvalidRecallInvalidStakeType() (gas: 507391) -TestAstariaV1Status:testInvalidRecallLoanDoesNotExist() (gas: 523634) -TestAstariaV1Status:testIsActive() (gas: 432737) -TestAstariaV1Status:testIsRecalledInsideWindow() (gas: 570875) -TestAstariaV1Status:testIsRecalledOutsideWindow() (gas: 568776) -TestAstariaV1Status:testRecallRateActiveRecall() (gas: 556605) -TestAstariaV1Status:testRecallRateEmptyRecall() (gas: 430391) +TestAstariaV1Status:testCannotRecallTwice() (gas: 545599) +TestAstariaV1Status:testCannotWithdrawLoanHasNotBeenRefinanced() (gas: 428440) +TestAstariaV1Status:testCannotWithdrawWithdrawDoesNotExist() (gas: 434857) +TestAstariaV1Status:testGenerateRecallConsideration() (gas: 477168) +TestAstariaV1Status:testInvalidRecallInvalidStakeType() (gas: 507644) +TestAstariaV1Status:testInvalidRecallLoanDoesNotExist() (gas: 475937) +TestAstariaV1Status:testIsActive() (gas: 432690) +TestAstariaV1Status:testIsRecalledInsideWindow() (gas: 559633) +TestAstariaV1Status:testIsRecalledOutsideWindow() (gas: 557534) +TestAstariaV1Status:testRecallAndRefinanceInsideWindow() (gas: 713941) +TestAstariaV1Status:testRecallRateActiveRecall() (gas: 545388) +TestAstariaV1Status:testRecallRateEmptyRecall() (gas: 430233) TestBorrowerEnforcer:testBERevertAdditionalTransfers() (gas: 73195) TestBorrowerEnforcer:testBERevertInvalidLoanTerms() (gas: 78316) TestBorrowerEnforcer:testBEValidLoanTerms() (gas: 69518) @@ -56,19 +57,19 @@ TestCompoundInterest:testRateTooLowZero() (gas: 68419) TestCustodian:testCannotLazyMintTwice() (gas: 76753) TestCustodian:testCannotMintInvalidLoanInvalidCustodian() (gas: 66884) TestCustodian:testCannotMintInvalidLoanValidCustodian() (gas: 72556) -TestCustodian:testCustodySelector() (gas: 2820915) +TestCustodian:testCustodySelector() (gas: 2818515) TestCustodian:testDefaultCustodySelectorRevert() (gas: 70061) -TestCustodian:testGenerateOrderInvalidPostRepayment() (gas: 171281) -TestCustodian:testGenerateOrderInvalidPostSettlement() (gas: 155941) -TestCustodian:testGenerateOrderRepay() (gas: 175271) -TestCustodian:testGenerateOrderRepayAsRepayApprovedBorrower() (gas: 191405) -TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNative() (gas: 827047) -TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNativeHandlerAuthorized() (gas: 744382) +TestCustodian:testGenerateOrderInvalidPostRepayment() (gas: 171272) +TestCustodian:testGenerateOrderInvalidPostSettlement() (gas: 155932) +TestCustodian:testGenerateOrderRepay() (gas: 175262) +TestCustodian:testGenerateOrderRepayAsRepayApprovedBorrower() (gas: 191396) +TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNative() (gas: 827029) +TestCustodian:testGenerateOrderRepayERC1155AndERC20AndNativeHandlerAuthorized() (gas: 744385) TestCustodian:testGenerateOrderRepayInvalidHookAddress() (gas: 90248) TestCustodian:testGenerateOrderRepayInvalidHookReturnType() (gas: 84652) TestCustodian:testGenerateOrderRepayNotBorrower() (gas: 99148) -TestCustodian:testGenerateOrderSettlement() (gas: 147620) -TestCustodian:testGenerateOrderSettlementHandlerAuthorized() (gas: 153177) +TestCustodian:testGenerateOrderSettlement() (gas: 147611) +TestCustodian:testGenerateOrderSettlementHandlerAuthorized() (gas: 153168) TestCustodian:testGenerateOrderSettlementNoActiveLoan() (gas: 155828) TestCustodian:testGenerateOrderSettlementUnauthorized() (gas: 94388) TestCustodian:testGenerateOrdersWithLoanStartAtBlockTimestampInvalidLoan() (gas: 452795) @@ -84,11 +85,11 @@ TestCustodian:testNonPayableFunctions() (gas: 212635) TestCustodian:testOnlySeaport() (gas: 17873) TestCustodian:testPayableFunctions() (gas: 41645) TestCustodian:testPreviewOrderNoActiveLoan() (gas: 98764) -TestCustodian:testPreviewOrderRepay() (gas: 228570) -TestCustodian:testPreviewOrderSettlement() (gas: 179304) +TestCustodian:testPreviewOrderRepay() (gas: 228561) +TestCustodian:testPreviewOrderSettlement() (gas: 179295) TestCustodian:testPreviewOrderSettlementInvalidFufliller() (gas: 100931) TestCustodian:testPreviewOrderSettlementInvalidRepayer() (gas: 106834) -TestCustodian:testRatifyOrder() (gas: 181788) +TestCustodian:testRatifyOrder() (gas: 181779) TestCustodian:testSeaportMetadata() (gas: 8545) TestCustodian:testSupportsInterface() (gas: 9428) TestCustodian:testSymbol() (gas: 7127) @@ -99,30 +100,30 @@ TestLenderEnforcer:testLERevertInvalidLoanTerms() (gas: 78341) TestLenderEnforcer:testLEValidLoanTerms() (gas: 69496) TestLenderEnforcer:testLEValidLoanTermsAnyBorrower() (gas: 69494) TestLenderEnforcer:testLEValidLoanTermsWithAdditionalTransfers() (gas: 70740) -TestLoanCombinations:testLoan20For721SimpleInterestDutchFixedRepay() (gas: 542758) -TestLoanCombinations:testLoan20for20SimpleInterestDutchFixedRepay() (gas: 530387) -TestLoanCombinations:testLoan721for20SimpleInterestDutchFixedRepay() (gas: 560581) -TestLoanCombinations:testLoanAstariaSettlementRepay() (gas: 550323) -TestLoanCombinations:testLoanSimpleInterestEnglishFixed() (gas: 556101) +TestLoanCombinations:testLoan20For721SimpleInterestDutchFixedRepay() (gas: 542749) +TestLoanCombinations:testLoan20for20SimpleInterestDutchFixedRepay() (gas: 530378) +TestLoanCombinations:testLoan721for20SimpleInterestDutchFixedRepay() (gas: 560572) +TestLoanCombinations:testLoanAstariaSettlementRepay() (gas: 550314) +TestLoanCombinations:testLoanSimpleInterestEnglishFixed() (gas: 556092) TestNewLoan:testBuyNowPayLater() (gas: 2832773) TestNewLoan:testNewLoanERC721CollateralDefaultTerms2() (gas: 394391) TestNewLoan:testNewLoanERC721CollateralLessDebtThanOffered() (gas: 2348) TestNewLoan:testNewLoanRefinanceNew() (gas: 207) TestNewLoan:testNewLoanViaOriginatorBorrowerApprovalAndLenderApproval() (gas: 305651) TestNewLoan:testNewLoanViaOriginatorLenderApproval() (gas: 358345) -TestNewLoan:testSettleLoan() (gas: 611290) +TestNewLoan:testSettleLoan() (gas: 611281) TestPausableNonReentrant:testNotOwner() (gas: 21254) TestPausableNonReentrant:testPauseAndUnpause() (gas: 22555) TestPausableNonReentrant:testReentrancy() (gas: 15360) TestPausableNonReentrant:testUnpauseWhenNotPaused() (gas: 12582) TestRefStarportLib:testSpentToReceived() (gas: 13315) TestRefStarportLib:testValidateSalt(address,bytes32) (runs: 256, μ: 33865, ~: 33865) -TestRepayLoan:testRepayLoanApprovedRepayer() (gas: 635212) -TestRepayLoan:testRepayLoanBase() (gas: 571502) +TestRepayLoan:testRepayLoanApprovedRepayer() (gas: 635203) +TestRepayLoan:testRepayLoanBase() (gas: 571493) TestRepayLoan:testRepayLoanGenerateOrderNotSeaport() (gas: 402312) TestRepayLoan:testRepayLoanInSettlement() (gas: 549952) TestRepayLoan:testRepayLoanInvalidRepayer() (gas: 569739) -TestRepayLoan:testRepayLoanThatDoesNotExist() (gas: 842802) +TestRepayLoan:testRepayLoanThatDoesNotExist() (gas: 842793) TestSimpleInterestPricing:test_calculateInterest() (gas: 800891) TestSimpleInterestPricing:test_getPaymentConsideration() (gas: 882219) TestSimpleInterestPricing:test_getRefinanceConsideration() (gas: 876412)