diff --git a/src/Custodian.sol b/src/Custodian.sol index d4b553d5..552e8c1e 100644 --- a/src/Custodian.sol +++ b/src/Custodian.sol @@ -35,7 +35,7 @@ import {SettlementHook} from "starport-core/hooks/SettlementHook.sol"; import {SettlementHandler} from "starport-core/handlers/SettlementHandler.sol"; import {Pricing} from "starport-core/pricing/Pricing.sol"; import {LoanManager} from "starport-core/LoanManager.sol"; -import {StarPortLib} from "starport-core/lib/StarPortLib.sol"; +import {StarPortLib, Actions} from "starport-core/lib/StarPortLib.sol"; contract Custodian is ERC721, ContractOffererInterface, ConduitHelper, TokenReceiverInterface { using {StarPortLib.getId} for LoanManager.Loan; @@ -48,13 +48,14 @@ contract Custodian is ERC721, ContractOffererInterface, ConduitHelper, TokenRece event RepayApproval(address borrower, address repayer, bool approved); event SeaportCompatibleContractDeployed(); - error NotSeaport(); - error NotLoanManager(); - error InvalidRepayer(); + error ImplementInChild(); + error InvalidAction(); error InvalidFulfiller(); error InvalidHandlerExecution(); error InvalidLoan(); - error ImplementInChild(); + error InvalidRepayer(); + error NotSeaport(); + error NotLoanManager(); constructor(LoanManager LM_, address seaport_) { seaport = seaport_; @@ -202,12 +203,13 @@ contract Custodian is ERC721, ContractOffererInterface, ConduitHelper, TokenRece SpentItem[] calldata maximumSpent, bytes calldata context // encoded based on the schemaID ) public view returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { - LoanManager.Loan memory loan = abi.decode(context, (LoanManager.Loan)); + (Actions action, LoanManager.Loan memory loan) = abi.decode(context, (Actions, LoanManager.Loan)); if (!LM.issued(loan.getId())) { revert InvalidLoan(); } - if (SettlementHook(loan.terms.hook).isActive(loan)) { + bool loanActive = SettlementHook(loan.terms.hook).isActive(loan); + if (action == Actions.Repayment && loanActive) { address borrower = getBorrower(loan); if (fulfiller != borrower && !repayApproval[borrower][fulfiller]) { revert InvalidRepayer(); @@ -219,7 +221,7 @@ contract Custodian is ERC721, ContractOffererInterface, ConduitHelper, TokenRece consideration = _mergeConsiderations(paymentConsiderations, carryFeeConsideration, new ReceivedItem[](0)); consideration = _removeZeroAmounts(consideration); - } else { + } else if (action == Actions.Settlement && !loanActive) { address authorized; (consideration, authorized) = SettlementHandler(loan.terms.handler).getSettlement(loan); @@ -228,6 +230,8 @@ contract Custodian is ERC721, ContractOffererInterface, ConduitHelper, TokenRece } else if (authorized == loan.terms.handler || authorized == loan.issuer) {} else { revert InvalidFulfiller(); } + } else { + revert InvalidAction(); } } @@ -264,11 +268,10 @@ contract Custodian is ERC721, ContractOffererInterface, ConduitHelper, TokenRece internal returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { - LoanManager.Loan memory loan = abi.decode(context, (LoanManager.Loan)); - if (!LM.issued(loan.getId())) { - revert InvalidLoan(); - } - if (SettlementHook(loan.terms.hook).isActive(loan)) { + (Actions action, LoanManager.Loan memory loan) = abi.decode(context, (Actions, LoanManager.Loan)); + + bool loanActive = SettlementHook(loan.terms.hook).isActive(loan); + if (action == Actions.Repayment && loanActive) { address borrower = getBorrower(loan); if (fulfiller != borrower && !repayApproval[borrower][fulfiller]) { revert InvalidRepayer(); @@ -285,7 +288,7 @@ contract Custodian is ERC721, ContractOffererInterface, ConduitHelper, TokenRece consideration = _removeZeroAmounts(consideration); _settleLoan(loan); - } else { + } else if (action == Actions.Settlement && !loanActive) { address authorized; //add in originator fee _beforeSettlementHandlerHook(loan); @@ -310,6 +313,8 @@ contract Custodian is ERC721, ContractOffererInterface, ConduitHelper, TokenRece } _settleLoan(loan); + } else { + revert InvalidAction(); } } diff --git a/src/LoanManager.sol b/src/LoanManager.sol index 9fa8261a..4278ecf6 100644 --- a/src/LoanManager.sol +++ b/src/LoanManager.sol @@ -33,7 +33,7 @@ import {SettlementHook} from "starport-core/hooks/SettlementHook.sol"; import {SettlementHandler} from "starport-core/handlers/SettlementHandler.sol"; import {Pricing} from "starport-core/pricing/Pricing.sol"; -import {StarPortLib} from "starport-core/lib/StarPortLib.sol"; +import {StarPortLib, Actions} from "starport-core/lib/StarPortLib.sol"; import {ConduitTransfer, ConduitItemType} from "seaport-types/src/conduit/lib/ConduitStructs.sol"; import {ConduitControllerInterface} from "seaport-types/src/interfaces/ConduitControllerInterface.sol"; import {ConduitInterface} from "seaport-types/src/interfaces/ConduitInterface.sol"; @@ -43,34 +43,38 @@ import {SignatureCheckerLib} from "solady/src/utils/SignatureCheckerLib.sol"; import {CaveatEnforcer} from "starport-core/enforcers/CaveatEnforcer.sol"; import {Ownable} from "solady/src/auth/Ownable.sol"; import {ConduitHelper} from "starport-core/ConduitHelper.sol"; +import "forge-std/console.sol"; interface LoanSettledCallback { function onLoanSettled(LoanManager.Loan calldata loan) external; } -contract LoanManager is ContractOffererInterface, ConduitHelper, Ownable, ERC721 { +contract LoanManager is ConduitHelper, Ownable, ERC721 { using FixedPointMathLib for uint256; using {StarPortLib.toReceivedItems} for SpentItem[]; using {StarPortLib.getId} for LoanManager.Loan; using {StarPortLib.validateSalt} for mapping(address => mapping(bytes32 => bool)); + bytes32 internal immutable _DOMAIN_SEPARATOR; + ConsiderationInterface public immutable seaport; + // bool public paused; //TODO: + address payable public immutable defaultCustodian; bytes32 public immutable DEFAULT_CUSTODIAN_CODE_HASH; - bytes32 internal immutable _DOMAIN_SEPARATOR; // Define the EIP712 domain and typehash constants for generating signatures - bytes32 constant EIP_DOMAIN = keccak256("EIP712Domain(string version,uint256 chainId,address verifyingContract)"); + bytes32 public constant EIP_DOMAIN = + keccak256("EIP712Domain(string version,uint256 chainId,address verifyingContract)"); bytes32 public constant INTENT_ORIGINATION_TYPEHASH = keccak256("IntentOrigination(bytes32 hash,bytes32 salt,uint256 nonce)"); - bytes32 constant VERSION = keccak256("0"); - + bytes32 public constant VERSION = keccak256("0"); + address public feeTo; + uint96 public defaultFeeRake; mapping(address => mapping(bytes32 => bool)) public usedSalts; mapping(address => uint256) public borrowerNonce; //needs to be invalidated - address public feeTo; - uint96 public defaultFeeRake; //contract to token //fee rake mapping(address => Fee) public feeOverride; @@ -127,6 +131,7 @@ contract LoanManager is ContractOffererInterface, ConduitHelper, Ownable, ERC721 error CannotTransferLoans(); error ConduitTransferError(); + error InvalidAction(); error InvalidConduit(); error InvalidRefinance(); error InvalidCustodian(); @@ -240,24 +245,19 @@ contract LoanManager is ContractOffererInterface, ConduitHelper, Ownable, ERC721 bytes32[] calldata orderHashes, uint256 contractNonce, bytes calldata context - ) internal returns (bytes4 selector) { - address custodian; - - assembly { - custodian := calldataload(add(context.offset, 0x20)) // 0x20 offset for the first address 'custodian' - } + ) internal { + address custodian = StarPortLib.getCustodian(context); // Comparing the retrieved code hash with a known hash bytes32 codeHash; assembly { codeHash := extcodehash(custodian) } - if (codeHash != DEFAULT_CUSTODIAN_CODE_HASH) { - if ( - Custodian(payable(custodian)).custody(consideration, orderHashes, contractNonce, context) + if ( + codeHash != DEFAULT_CUSTODIAN_CODE_HASH + && Custodian(payable(custodian)).custody(consideration, orderHashes, contractNonce, context) != Custodian.custody.selector - ) { - revert InvalidCustodian(); - } + ) { + revert InvalidCustodian(); } } @@ -278,58 +278,89 @@ contract LoanManager is ContractOffererInterface, ConduitHelper, Ownable, ERC721 SpentItem[] calldata minimumReceivedFromBorrower, SpentItem[] calldata maximumSpentFromBorrower, bytes calldata context // encoded based on the schemaID - ) public view returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { - LoanManager.Obligation memory obligation = abi.decode(context, (LoanManager.Obligation)); + ) public returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { + Actions action = StarPortLib.getAction(context); + if (action == Actions.Origination) { + (, LoanManager.Obligation memory obligation) = abi.decode(context, (Actions, LoanManager.Obligation)); + + bool feeOn; + if (obligation.debt.length == 0) { + revert InvalidDebt(); + } + if (maximumSpentFromBorrower.length == 0) { + revert InvalidMaximumSpentEmpty(); + } + consideration = maximumSpentFromBorrower.toReceivedItems(obligation.custodian); + if (feeTo != address(0)) { + feeOn = true; + } + address receiver = obligation.borrower; + + // we settle via seaport channels if caveats are present + if (fulfiller != receiver || obligation.caveats.length > 0) { + bytes32 caveatHash = keccak256( + encodeWithSaltAndBorrowerCounter( + obligation.borrower, obligation.salt, keccak256(abi.encode(obligation.caveats)) + ) + ); + SpentItem[] memory debt = obligation.debt; + offer = new SpentItem[](debt.length + 1); + SpentItem[] memory feeItems = !feeOn ? new SpentItem[](0) : _feeRake(debt); + + for (uint256 i; i < debt.length;) { + offer[i] = debt[i]; + if (feeOn && feeItems[i].amount > 0) { + offer[i].amount = debt[i].amount - feeItems[i].amount; + } + unchecked { + ++i; + } + } - bool feeOn; - if (obligation.debt.length == 0) { - revert InvalidDebt(); - } - if (maximumSpentFromBorrower.length == 0) { - revert InvalidMaximumSpentEmpty(); - } - consideration = maximumSpentFromBorrower.toReceivedItems(obligation.custodian); - if (feeTo != address(0)) { - feeOn = true; - } - address receiver = obligation.borrower; + offer[debt.length] = SpentItem({ + itemType: ItemType.ERC721, + token: address(this), + identifier: uint256(caveatHash), + amount: 1 + }); + } else if (feeOn) { + SpentItem[] memory debt = obligation.debt; + offer = new SpentItem[](debt.length); - // we settle via seaport channels if caveats are present - if (fulfiller != receiver || obligation.caveats.length > 0) { - bytes32 caveatHash = keccak256( - encodeWithSaltAndBorrowerCounter( - obligation.borrower, obligation.salt, keccak256(abi.encode(obligation.caveats)) - ) - ); - SpentItem[] memory debt = obligation.debt; - offer = new SpentItem[](debt.length + 1); - SpentItem[] memory feeItems = !feeOn ? new SpentItem[](0) : _feeRake(debt); + SpentItem[] memory feeItems = !feeOn ? new SpentItem[](0) : _feeRake(debt); - for (uint256 i; i < debt.length;) { - offer[i] = debt[i]; - if (feeOn && feeItems[i].amount > 0) { + for (uint256 i; i < debt.length;) { + offer[i] = debt[i]; offer[i].amount = debt[i].amount - feeItems[i].amount; - } - unchecked { - ++i; + unchecked { + ++i; + } } } - - offer[debt.length] = - SpentItem({itemType: ItemType.ERC721, token: address(this), identifier: uint256(caveatHash), amount: 1}); - } else if (feeOn) { - SpentItem[] memory debt = obligation.debt; - offer = new SpentItem[](debt.length); - - SpentItem[] memory feeItems = !feeOn ? new SpentItem[](0) : _feeRake(debt); - - for (uint256 i; i < debt.length;) { - offer[i] = debt[i]; - offer[i].amount = debt[i].amount - feeItems[i].amount; - unchecked { - ++i; - } + } else if (action == Actions.Refinance) { + (, LoanManager.Loan memory loan, bytes memory newPricingData) = + abi.decode(context, (Actions, LoanManager.Loan, bytes)); + + ( + // used to update the new loan amount + ReceivedItem[] memory considerationPayment, + // used to pay the carry amount + ReceivedItem[] memory carryPayment, + // note: considerationPayment - carryPayment = amount to pay lender + + // used for any additional payments beyond consideration and carry + ReceivedItem[] memory additionalPayment + ) = Pricing(loan.terms.pricing).isValidRefinance(loan, newPricingData, msg.sender); + + consideration = _mergeConsiderations(considerationPayment, carryPayment, additionalPayment); + consideration = _removeZeroAmounts(consideration); + + // if for malicious or non-malicious the refinanceConsideration is zero + if (consideration.length == 0) { + revert InvalidNoRefinanceConsideration(); } + } else { + revert InvalidAction(); } } @@ -385,7 +416,7 @@ contract LoanManager is ContractOffererInterface, ConduitHelper, Ownable, ERC721 if (feeTo != address(0)) { feesOn = true; } - LoanManager.Obligation memory obligation = abi.decode(context, (LoanManager.Obligation)); + (, LoanManager.Obligation memory obligation) = abi.decode(context, (Actions, LoanManager.Obligation)); if (obligation.debt.length == 0) { revert InvalidDebt(); @@ -473,7 +504,14 @@ contract LoanManager is ContractOffererInterface, ConduitHelper, Ownable, ERC721 SpentItem[] calldata maximumSpent, bytes calldata context // encoded based on the schemaID ) external onlySeaport returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { - (offer, consideration) = _fillObligationAndVerify(fulfiller, maximumSpent, context); + Actions action = StarPortLib.getAction(context); + if (action == Actions.Origination) { + (offer, consideration) = _fillObligationAndVerify(fulfiller, maximumSpent, context); + } else if (action == Actions.Refinance) { + consideration = _refinance(fulfiller, context); + } else { + revert InvalidAction(); + } } function _moveFeesToReceived(SpentItem memory feeItem) internal { @@ -544,21 +582,60 @@ contract LoanManager is ContractOffererInterface, ConduitHelper, Ownable, ERC721 bytes32[] calldata orderHashes, uint256 contractNonce ) external onlySeaport returns (bytes4 ratifyOrderMagicValue) { - _callCustody(consideration, orderHashes, contractNonce, context); + Actions action = StarPortLib.getAction(context); + if (action == Actions.Origination) { + _callCustody(consideration, orderHashes, contractNonce, context); + } ratifyOrderMagicValue = ContractOffererInterface.ratifyOrder.selector; } - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721, ContractOffererInterface) - returns (bool) - { + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721) returns (bool) { return interfaceId == type(ContractOffererInterface).interfaceId || interfaceId == type(ERC721).interfaceId || super.supportsInterface(interfaceId); } + function _refinance(address fulfiller, bytes calldata context) + internal + returns (ReceivedItem[] memory consideration) + { + (, LoanManager.Loan memory loan, bytes memory newPricingData) = + abi.decode(context, (Actions, LoanManager.Loan, bytes)); + + ( + // used to update the new loan amount + ReceivedItem[] memory considerationPayment, + // used to pay the carry amount + ReceivedItem[] memory carryPayment, + // note: considerationPayment - carryPayment = amount to pay lender + + // used for any additional payments beyond consideration and carry + ReceivedItem[] memory additionalPayment + ) = Pricing(loan.terms.pricing).isValidRefinance(loan, newPricingData, msg.sender); + + consideration = _mergeConsiderations(considerationPayment, carryPayment, additionalPayment); + consideration = _removeZeroAmounts(consideration); + + // if for malicious or non-malicious the refinanceConsideration is zero + if (consideration.length == 0) { + revert InvalidNoRefinanceConsideration(); + } + + _settle(loan); + + for (uint256 i; i < loan.debt.length;) { + loan.debt[i].amount = considerationPayment[i].amount; + unchecked { + ++i; + } + } + + loan.terms.pricingData = newPricingData; + loan.originator = fulfiller; + loan.issuer = fulfiller; + loan.start = block.timestamp; + _issueLoanManager(loan, fulfiller.code.length > 0); + } + function refinance(LoanManager.Loan memory loan, bytes memory newPricingData, address conduit) external { (,, address conduitController) = seaport.information(); diff --git a/src/lib/StarPortLib.sol b/src/lib/StarPortLib.sol index 7894c40a..b3af8e2e 100644 --- a/src/lib/StarPortLib.sol +++ b/src/lib/StarPortLib.sol @@ -3,12 +3,36 @@ pragma solidity ^0.8.17; import {ItemType, ReceivedItem, SpentItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {LoanManager} from "starport-core/LoanManager.sol"; +import "forge-std/console.sol"; + +enum Actions { + Nothing, + Origination, + Refinance, + Repayment, + Settlement +} library StarPortLib { error InvalidSalt(); uint256 internal constant _INVALID_SALT = 0x81e69d9b00000000000000000000000000000000000000000000000000000000; + uint256 internal constant ONE_WORD = 0x20; + uint256 internal constant CUSTODIAN_WORD_OFFSET = 0x40; + + function getAction(bytes calldata data) internal pure returns (Actions action) { + assembly { + action := calldataload(data.offset) + } + } + + function getCustodian(bytes calldata data) internal pure returns (address custodian) { + assembly { + custodian := calldataload(add(data.offset, CUSTODIAN_WORD_OFFSET)) + } + } + function getId(LoanManager.Loan memory loan) internal pure returns (uint256 loanId) { loanId = uint256(keccak256(abi.encode(loan))); } diff --git a/src/pricing/BasePricing.sol b/src/pricing/BasePricing.sol index 5f960cbb..4d57af89 100644 --- a/src/pricing/BasePricing.sol +++ b/src/pricing/BasePricing.sol @@ -109,7 +109,6 @@ abstract contract BasePricing is Pricing { consideration = new ReceivedItem[](loan.debt.length); uint256[] memory owing = _getOwed(loan, details, loan.start, block.timestamp); - bool isActive = LM.active(loan.getId()); uint256 i = 0; for (; i < consideration.length;) { diff --git a/test/StarPortTest.sol b/test/StarPortTest.sol index c904b9f7..106ae4b1 100644 --- a/test/StarPortTest.sol +++ b/test/StarPortTest.sol @@ -64,6 +64,7 @@ import {ERC721} from "solady/src/tokens/ERC721.sol"; import {ContractOffererInterface} from "seaport-types/src/interfaces/ContractOffererInterface.sol"; import {TokenReceiverInterface} from "starport-core/interfaces/TokenReceiverInterface.sol"; import {LoanSettledCallback} from "starport-core/LoanManager.sol"; +import {Actions} from "starport-core/lib/StarPortLib.sol"; interface IWETH9 { function deposit() external payable; @@ -283,6 +284,72 @@ contract StarPortTest is BaseOrderTest { } } + function refinanceLoan(LoanManager.Loan memory loan, bytes memory newPricingData, address asWho) + internal + returns (LoanManager.Loan memory newLoan) + { + (SpentItem[] memory offer, ReceivedItem[] memory requiredConsideration) = LM.previewOrder( + address(LM.seaport()), + asWho, + new SpentItem[](0), + new SpentItem[](0), + abi.encode(Actions.Refinance, loan, newPricingData) + ); + //OrderParameters parameters; + // uint120 numerator; + // uint120 denominator; + // bytes signature; + // bytes extraData; + OfferItem[] memory offerItems = new OfferItem[](offer.length); + for (uint256 i = 0; i < offer.length; i++) { + offerItems[i] = OfferItem({ + itemType: offer[i].itemType, + token: offer[i].token, + identifierOrCriteria: offer[i].identifier, + startAmount: offer[i].amount, + endAmount: offer[i].amount + }); + } + + ConsiderationItem[] memory considerationItems = new ConsiderationItem[](requiredConsideration.length); + for (uint256 i = 0; i < requiredConsideration.length; i++) { + considerationItems[i] = ConsiderationItem({ + itemType: requiredConsideration[i].itemType, + token: requiredConsideration[i].token, + identifierOrCriteria: requiredConsideration[i].identifier, + startAmount: requiredConsideration[i].amount, + endAmount: requiredConsideration[i].amount, + recipient: requiredConsideration[i].recipient + }); + } + AdvancedOrder memory refinanceOrder = AdvancedOrder({ + signature: "", + parameters: _buildContractOrder(address(LM), offerItems, considerationItems), + numerator: 1, + denominator: 1, + extraData: abi.encode(Actions.Refinance, loan, newPricingData) + }); + vm.recordLogs(); + vm.startPrank(asWho); + + consideration.fulfillAdvancedOrder({ + advancedOrder: refinanceOrder, + criteriaResolvers: new CriteriaResolver[](0), + fulfillerConduitKey: bytes32(0), + recipient: address(asWho) + }); + vm.stopPrank(); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + for (uint256 i = 0; i < logs.length; i++) { + if (logs[i].topics[0] == bytes32(0x57cb72d73c48fadf55428537f6c9efbe080ae111339b0c5af42d9027ed20ba17)) { + (, newLoan) = abi.decode(logs[i].data, (uint256, LoanManager.Loan)); + break; + } + } + } + function buyNowPayLater( AdvancedOrder memory thingToBuy, NewLoanData memory loanData, @@ -392,7 +459,7 @@ contract StarPortTest is BaseOrderTest { numerator: 1, denominator: 1, signature: "0x", - extraData: abi.encode(activeLoan) + extraData: abi.encode(Actions.Settlement, activeLoan) }); uint256 balanceBefore = erc20s[0].balanceOf(borrower.addr); @@ -464,7 +531,7 @@ contract StarPortTest is BaseOrderTest { numerator: 1, denominator: 1, signature: "0x", - extraData: abi.encode(activeLoan) + extraData: abi.encode(Actions.Repayment, activeLoan) }); uint256 balanceBefore = erc20s[0].balanceOf(borrower.addr); @@ -556,7 +623,7 @@ contract StarPortTest is BaseOrderTest { numerator: 1, denominator: 1, signature: "", - extraData: abi.encode(nlr) + extraData: abi.encode(Actions.Origination, nlr) }); orders[2] = z; @@ -649,8 +716,13 @@ contract StarPortTest is BaseOrderTest { OrderParameters memory op = _buildContractOrder(address(LM), nlr.caveats.length == 0 ? new OfferItem[](0) : offer, collateral); - AdvancedOrder memory x = - AdvancedOrder({parameters: op, numerator: 1, denominator: 1, signature: "0x", extraData: abi.encode(nlr)}); + AdvancedOrder memory x = AdvancedOrder({ + parameters: op, + numerator: 1, + denominator: 1, + signature: "0x", + extraData: abi.encode(Actions.Origination, nlr) + }); uint256 balanceBefore; if (debt[0].token == address(0)) { diff --git a/test/TestAstariaV1Loan.sol b/test/TestAstariaV1Loan.sol index 94be2124..33b0c9b2 100644 --- a/test/TestAstariaV1Loan.sol +++ b/test/TestAstariaV1Loan.sol @@ -4,7 +4,7 @@ import {BaseRecall} from "starport-core/hooks/BaseRecall.sol"; // import {Base} from "starport-core/pricing/CompoundInterestPricing.sol"; // import {AstariaV1Pricing} from "starport-core/pricing/AstariaV1Pricing.sol"; import "forge-std/console2.sol"; -import {StarPortLib} from "starport-core/lib/StarPortLib.sol"; +import {StarPortLib, Actions} from "starport-core/lib/StarPortLib.sol"; contract TestAstariaV1Loan is AstariaV1Test { using {StarPortLib.getId} for LoanManager.Loan; @@ -300,7 +300,7 @@ contract TestAstariaV1Loan is AstariaV1Test { numerator: 1, denominator: 1, parameters: op, - extraData: abi.encode(loan), + extraData: abi.encode(Actions.Settlement, loan), signature: "" }); @@ -443,7 +443,7 @@ contract TestAstariaV1Loan is AstariaV1Test { numerator: 1, denominator: 1, parameters: op, - extraData: abi.encode(loan), + extraData: abi.encode(Actions.Settlement, loan), signature: "" }); diff --git a/test/TestCustodian.sol b/test/TestCustodian.sol index 7190986b..48999cf2 100644 --- a/test/TestCustodian.sol +++ b/test/TestCustodian.sol @@ -2,7 +2,7 @@ import "./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} from "starport-core/lib/StarPortLib.sol"; +import {StarPortLib, Actions} from "starport-core/lib/StarPortLib.sol"; contract MockCustodian is Custodian { constructor(LoanManager LM_, address seaport_) Custodian(LM_, seaport_) {} @@ -246,7 +246,9 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { //TODO: add assertions function testGenerateOrderRepay() public { vm.prank(seaportAddr); - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan) + ); } //TODO: add assertions @@ -254,7 +256,7 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { vm.prank(activeLoan.borrower); custodian.setRepayApproval(address(this), true); vm.prank(seaportAddr); - custodian.generateOrder(address(this), new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder(address(this), new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan)); } //TODO: add assertions @@ -275,10 +277,14 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { //function mockCallRevert(address callee, bytes calldata data, bytes calldata revertData) external; vm.mockCallRevert( address(issuer), - abi.encodeWithSelector(LoanSettledCallback.onLoanSettled.selector, abi.encode(activeLoan)), + abi.encodeWithSelector( + LoanSettledCallback.onLoanSettled.selector, abi.encode(Actions.Repayment, activeLoan) + ), new bytes(0) ); - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan) + ); } function testGenerateOrderRepayERC1155AndERC20AndNativeHandlerAuthorized() public { @@ -298,7 +304,9 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(activeLoan.terms.handler)); vm.prank(seaportAddr); - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan) + ); //ERC20 loanDetails = _generateOriginationDetails( @@ -314,7 +322,9 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { loan.toStorage(activeLoan); vm.prank(seaportAddr); - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan) + ); //Native loanDetails = _generateOriginationDetails( @@ -330,7 +340,9 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { loan.toStorage(activeLoan); vm.prank(seaportAddr); - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan) + ); } function testGenerateOrderRepayERC1155AndERC20AndNative() public { @@ -347,7 +359,9 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { loan.toStorage(activeLoan); vm.prank(seaportAddr); - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan) + ); //ERC20 loanDetails = _generateOriginationDetails( @@ -362,7 +376,9 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { loan.toStorage(activeLoan); vm.prank(seaportAddr); - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan) + ); //Native loanDetails = _generateOriginationDetails( @@ -378,13 +394,15 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { loan.toStorage(activeLoan); vm.prank(seaportAddr); - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan) + ); } function testGenerateOrderRepayNotBorrower() public { vm.prank(seaportAddr); vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidRepayer.selector)); - custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan)); } function testGenerateOrderSettlement() public { @@ -394,7 +412,7 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(0)); (SpentItem[] memory offer, ReceivedItem[] memory consideration) = - custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan)); vm.stopPrank(); @@ -408,7 +426,7 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(activeLoan.terms.handler)); (SpentItem[] memory offer, ReceivedItem[] memory consideration) = - custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan)); vm.stopPrank(); @@ -421,7 +439,7 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), alice); vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidFulfiller.selector)); - custodian.generateOrder(borrower.addr, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder(borrower.addr, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan)); } function testGenerateOrderSettlementNoActiveLoan() public { @@ -430,14 +448,14 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), lender.addr); activeLoan.borrower = address(bob); - vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidLoan.selector)); - custodian.generateOrder(borrower.addr, new SpentItem[](0), debt, abi.encode(activeLoan)); + vm.expectRevert(abi.encodeWithSelector(LoanManager.InvalidLoan.selector)); + custodian.generateOrder(borrower.addr, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan)); } //TODO: add assertions function testRatifyOrder() public { vm.startPrank(seaportAddr); - bytes memory context = abi.encode(activeLoan); + bytes memory context = abi.encode(Actions.Repayment, activeLoan); (SpentItem[] memory offer, ReceivedItem[] memory consideration) = custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, context); @@ -449,7 +467,7 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { function testGenerateOrderInvalidHandlerExecution() public { vm.startPrank(seaportAddr); - bytes memory context = abi.encode(activeLoan); + bytes memory context = abi.encode(Actions.Settlement, activeLoan); mockHookCall(activeLoan.terms.hook, false); mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(activeLoan.terms.handler)); mockHandlerExecuteFail(activeLoan.terms.handler); @@ -465,14 +483,19 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHookCall(activeLoan.terms.hook, true); mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(0)); - (SpentItem[] memory expectedOffer, ReceivedItem[] memory expectedConsideration) = - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + (SpentItem[] memory expectedOffer, ReceivedItem[] memory expectedConsideration) = custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan) + ); mockHookCall(activeLoan.terms.hook, true); mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(0)); (SpentItem[] memory receivedOffer, ReceivedItem[] memory receivedCosideration) = custodian.previewOrder( - activeLoan.borrower, activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan) + activeLoan.borrower, + activeLoan.borrower, + new SpentItem[](0), + debt, + abi.encode(Actions.Repayment, activeLoan) ); _deepEq(receivedOffer, expectedOffer); @@ -485,8 +508,9 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { destroyAccount(activeLoan.terms.hook, address(0)); vm.expectRevert(); - (SpentItem[] memory expectedOffer, ReceivedItem[] memory expectedConsideration) = - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + (SpentItem[] memory expectedOffer, ReceivedItem[] memory expectedConsideration) = custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan) + ); } function testGenerateOrderRepayInvalidHookReturnType() public { @@ -499,8 +523,9 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { ); vm.expectRevert(); - (SpentItem[] memory expectedOffer, ReceivedItem[] memory expectedConsideration) = - custodian.generateOrder(activeLoan.borrower, new SpentItem[](0), debt, abi.encode(activeLoan)); + (SpentItem[] memory expectedOffer, ReceivedItem[] memory expectedConsideration) = custodian.generateOrder( + activeLoan.borrower, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan) + ); } function testPreviewOrderSettlementInvalidFufliller() public { @@ -510,7 +535,7 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(1)); vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidFulfiller.selector)); (SpentItem[] memory receivedOffer, ReceivedItem[] memory receivedConsideration) = - custodian.previewOrder(alice, alice, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.previewOrder(alice, alice, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan)); } function testPreviewOrderSettlementInvalidRepayer() public { @@ -520,7 +545,7 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(0)); vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidRepayer.selector)); (SpentItem[] memory receivedOffer, ReceivedItem[] memory receivedCosideration) = - custodian.previewOrder(alice, bob, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.previewOrder(alice, bob, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan)); } function testPreviewOrderSettlement() public { @@ -530,13 +555,14 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(0)); (SpentItem[] memory expectedOffer, ReceivedItem[] memory expectedConsideration) = - custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(activeLoan)); + custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan)); mockHookCall(activeLoan.terms.hook, false); mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(0)); - (SpentItem[] memory receivedOffer, ReceivedItem[] memory receivedCosideration) = - custodian.previewOrder(seaportAddr, alice, new SpentItem[](0), debt, abi.encode(activeLoan)); + (SpentItem[] memory receivedOffer, ReceivedItem[] memory receivedCosideration) = custodian.previewOrder( + seaportAddr, alice, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan) + ); _deepEq(receivedOffer, expectedOffer); _deepEq(receivedCosideration, expectedConsideration); @@ -547,7 +573,43 @@ contract TestCustodian is StarPortTest, DeepEq, MockCall { mockHandlerCall(activeLoan.terms.handler, new ReceivedItem[](0), address(0)); activeLoan.borrower = address(bob); vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidLoan.selector)); - (SpentItem[] memory receivedOffer, ReceivedItem[] memory receivedCosideration) = - custodian.previewOrder(seaportAddr, alice, new SpentItem[](0), debt, abi.encode(activeLoan)); + (SpentItem[] memory receivedOffer, ReceivedItem[] memory receivedCosideration) = custodian.previewOrder( + seaportAddr, alice, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan) + ); + } + + function testInvalidActionSettleActiveLoan() public { + vm.prank(seaportAddr); + + mockHookCall(activeLoan.terms.hook, true); + vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidAction.selector)); + custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan)); + + vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidAction.selector)); + custodian.previewOrder(seaportAddr, alice, new SpentItem[](0), debt, abi.encode(Actions.Settlement, activeLoan)); + } + + function testInvalidActionRepayInActiveLoan() public { + vm.prank(seaportAddr); + + mockHookCall(activeLoan.terms.hook, false); + vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidAction.selector)); + custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan)); + + vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidAction.selector)); + custodian.previewOrder(seaportAddr, alice, new SpentItem[](0), debt, abi.encode(Actions.Repayment, activeLoan)); + } + + function testInvalidAction() public { + vm.prank(seaportAddr); + + mockHookCall(activeLoan.terms.hook, true); + vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidAction.selector)); + custodian.generateOrder(alice, new SpentItem[](0), debt, abi.encode(Actions.Origination, activeLoan)); + + vm.expectRevert(abi.encodeWithSelector(Custodian.InvalidAction.selector)); + custodian.previewOrder( + seaportAddr, alice, new SpentItem[](0), debt, abi.encode(Actions.Origination, activeLoan) + ); } } diff --git a/test/TestLoanManager.sol b/test/TestLoanManager.sol index 2be7d78a..c44e27c2 100644 --- a/test/TestLoanManager.sol +++ b/test/TestLoanManager.sol @@ -132,7 +132,7 @@ contract TestLoanManager is StarPortTest { // OrderParameters memory op = _buildContractOrder(address(LM), new OfferItem[](0), selectedCollateral); vm.startPrank(seaport); (SpentItem[] memory offer, ReceivedItem[] memory consideration) = - LM.generateOrder(address(this), new SpentItem[](0), maxSpent, abi.encode(O)); + LM.generateOrder(address(this), new SpentItem[](0), maxSpent, abi.encode(Actions.Origination, O)); //TODO:: validate return data matches request // assertEq(keccak256(abi.encode(consideration)), keccak256(abi.encode(maxSpent))); } @@ -191,7 +191,13 @@ contract TestLoanManager is StarPortTest { }); vm.prank(address(LM.seaport())); vm.expectRevert(abi.encodeWithSelector(LoanManager.InvalidCustodian.selector)); - LM.ratifyOrder(new SpentItem[](0), new ReceivedItem[](0), abi.encode(obligation), new bytes32[](0), uint256(0)); + LM.ratifyOrder( + new SpentItem[](0), + new ReceivedItem[](0), + abi.encode(Actions.Origination, obligation), + new bytes32[](0), + uint256(0) + ); } function testNonDefaultCustodianCustodyCallSuccess() public { @@ -209,12 +215,22 @@ contract TestLoanManager is StarPortTest { vm.mockCall( address(mockCustodian), abi.encodeWithSelector( - Custodian.custody.selector, new ReceivedItem[](0), new bytes32[](0), uint256(0), abi.encode(obligation) + Custodian.custody.selector, + new ReceivedItem[](0), + new bytes32[](0), + uint256(0), + abi.encode(Actions.Origination, obligation) ), abi.encode(bytes4(Custodian.custody.selector)) ); vm.prank(address(LM.seaport())); - LM.ratifyOrder(new SpentItem[](0), new ReceivedItem[](0), abi.encode(obligation), new bytes32[](0), uint256(0)); + LM.ratifyOrder( + new SpentItem[](0), + new ReceivedItem[](0), + abi.encode(Actions.Origination, obligation), + new bytes32[](0), + uint256(0) + ); } function testInvalidDebt() public { @@ -230,7 +246,9 @@ contract TestLoanManager is StarPortTest { }); vm.prank(address(LM.seaport())); vm.expectRevert(abi.encodeWithSelector(LoanManager.InvalidDebt.selector)); - LM.generateOrder(address(this), new SpentItem[](0), new SpentItem[](0), abi.encode(obligation)); + LM.generateOrder( + address(this), new SpentItem[](0), new SpentItem[](0), abi.encode(Actions.Origination, obligation) + ); } function testInvalidMaximumSpentEmpty() public { @@ -246,7 +264,9 @@ contract TestLoanManager is StarPortTest { }); vm.prank(address(LM.seaport())); vm.expectRevert(abi.encodeWithSelector(LoanManager.InvalidMaximumSpentEmpty.selector)); - LM.generateOrder(address(this), new SpentItem[](0), new SpentItem[](0), abi.encode(obligation)); + LM.generateOrder( + address(this), new SpentItem[](0), new SpentItem[](0), abi.encode(Actions.Origination, obligation) + ); } function testDefaultFeeRake() public { @@ -331,6 +351,55 @@ contract TestLoanManager is StarPortTest { ); vm.startPrank(seaport); vm.expectRevert(abi.encodeWithSelector(LoanManager.InvalidOrigination.selector)); - LM.generateOrder(address(this), new SpentItem[](0), maxSpent, abi.encode(O)); + LM.generateOrder(address(this), new SpentItem[](0), maxSpent, abi.encode(Actions.Origination, O)); + } + + function testGenerateOrderInvalidAction() public { + Originator originator = new MockOriginator(LM, address(0), 0); + address seaport = address(LM.seaport()); + debt.push(SpentItem({itemType: ItemType.ERC20, token: address(erc20s[0]), amount: 100, identifier: 0})); + + SpentItem[] memory maxSpent = new SpentItem[](1); + maxSpent[0] = SpentItem({token: address(erc721s[0]), amount: 1, identifier: 1, itemType: ItemType.ERC721}); + + // + LoanManager.Obligation memory O = LoanManager.Obligation({ + custodian: address(custodian), + borrower: borrower.addr, + debt: debt, + salt: bytes32(0), + details: "", + approval: "", + caveats: new LoanManager.Caveat[](0), + originator: address(originator) + }); + + vm.startPrank(seaport); + vm.expectRevert(abi.encodeWithSelector(LoanManager.InvalidAction.selector)); + LM.generateOrder(address(this), new SpentItem[](0), maxSpent, abi.encode(Actions.Repayment, O)); + } + + function testPreviewOrderInvalidAction() public { + Originator originator = new MockOriginator(LM, address(0), 0); + address seaport = address(LM.seaport()); + debt.push(SpentItem({itemType: ItemType.ERC20, token: address(erc20s[0]), amount: 100, identifier: 0})); + + SpentItem[] memory maxSpent = new SpentItem[](1); + maxSpent[0] = SpentItem({token: address(erc721s[0]), amount: 1, identifier: 1, itemType: ItemType.ERC721}); + + LoanManager.Obligation memory O = LoanManager.Obligation({ + custodian: address(custodian), + borrower: borrower.addr, + debt: debt, + salt: bytes32(0), + details: "", + approval: "", + caveats: new LoanManager.Caveat[](0), + originator: address(originator) + }); + + vm.startPrank(seaport); + vm.expectRevert(abi.encodeWithSelector(LoanManager.InvalidAction.selector)); + LM.previewOrder(seaport, address(this), new SpentItem[](0), maxSpent, abi.encode(Actions.Repayment, O)); } } diff --git a/test/TestNewLoan.sol b/test/TestNewLoan.sol index c5cae373..016b8400 100644 --- a/test/TestNewLoan.sol +++ b/test/TestNewLoan.sol @@ -1,5 +1,6 @@ import "./StarPortTest.sol"; import {AstariaV1Pricing} from "starport-core/pricing/AstariaV1Pricing.sol"; +import {Actions} from "starport-core/lib/StarPortLib.sol"; contract TestNewLoan is StarPortTest { function testNewLoanERC721CollateralDefaultTerms2() public returns (LoanManager.Loan memory) { @@ -153,6 +154,56 @@ contract TestNewLoan is StarPortTest { vm.stopPrank(); } + function testNewLoanRefinanceNew() public { + Custodian custody = Custodian(LM.defaultCustodian()); + + LoanManager.Terms memory terms = LoanManager.Terms({ + hook: address(hook), + handler: address(handler), + pricing: address(pricing), + pricingData: defaultPricingData, + handlerData: defaultHandlerData, + hookData: defaultHookData + }); + + selectedCollateral.push( + ConsiderationItem({ + token: address(erc721s[0]), + startAmount: 1, + endAmount: 1, + identifierOrCriteria: 1, + itemType: ItemType.ERC721, + recipient: payable(address(custody)) + }) + ); + + debt.push(SpentItem({itemType: ItemType.ERC20, token: address(erc20s[0]), amount: 100, identifier: 0})); + Originator.Details memory loanDetails = Originator.Details({ + conduit: address(lenderConduit), + custodian: address(custody), + issuer: lender.addr, + deadline: block.timestamp + 100, + offer: Originator.Offer({ + salt: bytes32(0), + terms: terms, + collateral: ConsiderationItemLib.toSpentItemArray(selectedCollateral), + debt: debt + }) + }); + + LoanManager.Loan memory loan = newLoan( + NewLoanData(address(custody), new LoanManager.Caveat[](0), abi.encode(loanDetails)), + Originator(UO), + selectedCollateral + ); + + refinanceLoan( + loan, + abi.encode(BasePricing.Details({rate: (uint256(1e16) * 100) / (365 * 1 days), carryRate: 0})), + refinancer.addr + ); + } + function testBuyNowPayLater() public { ConsiderationItem[] memory want = new ConsiderationItem[](1); want[0] = ConsiderationItem({ @@ -302,7 +353,7 @@ contract TestNewLoan is StarPortTest { numerator: 1, denominator: 1, parameters: op, - extraData: abi.encode(activeLoan), + extraData: abi.encode(Actions.Settlement, activeLoan), signature: "" });