Skip to content

Commit

Permalink
Merge branch 'main' into feat/pricing-unit-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
justingreenberg authored Nov 3, 2023
2 parents 64fc039 + cb328f4 commit 595ee71
Show file tree
Hide file tree
Showing 10 changed files with 668 additions and 144 deletions.
229 changes: 122 additions & 107 deletions .gas-snapshot

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions mermaid/refinance.mmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
sequenceDiagram
title Starport Refinance Sequence Diagram
participant F as Fulfiller
participant R as Refinancer
participant L as Lender
participant R as Recaller


F->>LoanManager: refinance
LoanManager->>Pricing: isValidRefinance
Pricing->>LoanManager: (SpentItem[] ConsiderPayment, SpentItem[] CarryPayment, AdditionalTransfer[] AT)

LoanManager->>LoanManager: settle loan

loop Transfer 1->n ConsiderPayment items
R->>L: Move ConsiderPayment to Lender
end
opt CarryPayment length > 0
R->>Originator: Move CarryPayment to Originator
end

opt F is not Refinancer and F is not approved
loop Validate 1->n Caveats
LoanManager->>CaveatEnforcer: validate
end
end
opt AdditionalTransferItems length > 0
LoanManager->>LoanManager: validateAdditionalTransfers
loop 1->n
alt From is Borrower
B->>AdditionalTransferRecipient: AdditionalTransferItem from Borrower -> AdditionalTransferRecipient
else From is Lender
L->>AdditionalTransferRecipient: AdditionalTransferItem from Lender -> AdditionalTransferRecipient
else From is F
F->>AdditionalTransferRecipient: AdditionalTransferItem from Fulfiller -> AdditionalTransferRecipient
end
end
end

opt Lender is contract
LoanManager->>L: onERC721Received
end
35 changes: 35 additions & 0 deletions mermaid/settlement.mmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
sequenceDiagram
title Starport Settlement Sequence Diagram
participant F as Fulfiller

F->>Seaport: fulfillAdvancedOrder/matchAdvancedOrder
Seaport->>Custodian: generateOrder
Custodian->>SettlementHook: isActive
SettlementHook->>Custodian: (true/false)
alt Action is Repayment and SettlementHook is Active
Custodian->>Custodian: _beforeApprovalsSetHook
Custodian->>Custodian: _setOfferApprovalsWithSeaport
Custodian->>Pricing: getPaymentConsideration
Pricing->>Custodian: (SpentItem[] payment, SpentItem[] carry)
Custodian->>LoanManager: settle loan
else Action Settlement and Custodian->>SettlementHook: isActive is false
Custodian->>Custodian: _beforeGetSettlement
Custodian->>SettlementHandler: getSettlement
SettlementHandler->>Custodian: (ReceivedItem[] consideration, address authorized)
Custodian->>Custodian: _afterGetSettlement
alt authorized is address(0) || authorized is Fulfiller
Custodian->>Custodian: _beforeApprovalsSetHook
Custodian->>Custodian: _setOfferApprovalsWithSeaport
else authorized is loan handler || authorized is loan issuer
Custodian->>Custodian: _moveCollateralToAuthorized
Custodian->>Custodian: _beforeSettlementHandlerHook
alt authorized is loan handler
Custodian->>SettlementHandler: execute
Custodian->>Custodian: _afterSettlementHandlerHook
end
end
Custodian->>LoanManager: settle loan
opt Lender is contract
LoanManager->>Lender: onLoanSettledHook
end
end
50 changes: 21 additions & 29 deletions src/LoanManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ 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 {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";
import {PausableNonReentrant} from "starport-core/lib/PausableNonReentrant.sol";

interface LoanSettledCallback {
function onLoanSettled(LoanManager.Loan calldata loan) external;
}

contract LoanManager is Ownable, ERC721 {
contract LoanManager is ERC721, PausableNonReentrant {
using FixedPointMathLib for uint256;

using {StarPortLib.toReceivedItems} for SpentItem[];
Expand All @@ -69,7 +70,6 @@ contract LoanManager is Ownable, ERC721 {
bytes32 public constant INTENT_ORIGINATION_TYPEHASH =
keccak256("Origination(bytes32 hash,bytes32 salt,bytes32 caveatHash");
bytes32 public constant VERSION = keccak256("0");
bool public paused;
address public feeTo;
uint88 public defaultFeeRake;
mapping(address => mapping(bytes32 => bool)) public invalidHashes;
Expand Down Expand Up @@ -118,8 +118,6 @@ contract LoanManager is Ownable, ERC721 {

event Close(uint256 loanId);
event Open(uint256 loanId, LoanManager.Loan loan);
event Paused();
event UnPaused();

error InvalidRefinance();
error InvalidCustodian();
Expand All @@ -138,7 +136,6 @@ contract LoanManager is Ownable, ERC721 {
error UnauthorizedAdditionalTransferIncluded();
error InvalidCaveatSigner();
error MalformedRefinance();
error IsPaused();

constructor(ConsiderationInterface seaport_) {
address custodian = address(new Custodian(this, seaport_));
Expand Down Expand Up @@ -188,34 +185,21 @@ contract LoanManager is Ownable, ERC721 {
revert CannotTransferLoans();
}

function pause() external onlyOwner {
paused = true;
emit Paused();
}

function unPause() external onlyOwner {
paused = false;
emit UnPaused();
}

function originate(
AdditionalTransfer[] calldata additionalTransfers,
CaveatEnforcer.CaveatWithApproval calldata borrowerCaveat,
CaveatEnforcer.CaveatWithApproval calldata lenderCaveat,
LoanManager.Loan memory loan
) external payable {
if (paused) {
revert IsPaused();
}
) external payable pausableNonReentrant {
//cache the addresses
address borrower = loan.borrower;
address issuer = loan.issuer;
address feeRecipient = feeTo;
if (msg.sender != loan.borrower && !(approvals[borrower][msg.sender] == ApprovalType.BORROWER)) {
if (msg.sender != borrower && approvals[borrower][msg.sender] != ApprovalType.BORROWER) {
_validateAndEnforceCaveats(borrowerCaveat, borrower, additionalTransfers, loan);
}

if (msg.sender != issuer && !(approvals[issuer][msg.sender] == ApprovalType.LENDER)) {
if (msg.sender != issuer && approvals[issuer][msg.sender] != ApprovalType.LENDER) {
_validateAndEnforceCaveats(lenderCaveat, issuer, additionalTransfers, loan);
}

Expand Down Expand Up @@ -246,10 +230,7 @@ contract LoanManager is Ownable, ERC721 {
CaveatEnforcer.CaveatWithApproval calldata lenderCaveat,
LoanManager.Loan memory loan,
bytes calldata pricingData
) external {
if (paused) {
revert IsPaused();
}
) external pausableNonReentrant {
(
SpentItem[] memory considerationPayment,
SpentItem[] memory carryPayment,
Expand All @@ -260,18 +241,21 @@ contract LoanManager is Ownable, ERC721 {
loan = applyRefinanceConsiderationToLoan(loan, considerationPayment, carryPayment, pricingData);

_transferSpentItems(considerationPayment, lender, loan.issuer);
_transferSpentItems(carryPayment, lender, loan.originator);

if (carryPayment.length > 0) {
_transferSpentItems(carryPayment, lender, loan.originator);
}

loan.issuer = lender;
loan.originator = address(0);
loan.start = 0;

if (msg.sender != loan.issuer && !(approvals[loan.issuer][msg.sender] == ApprovalType.LENDER)) {
_validateAndEnforceCaveats(lenderCaveat, loan.issuer, additionalTransfers, loan);
if (msg.sender != lender && approvals[lender][msg.sender] != ApprovalType.LENDER) {
_validateAndEnforceCaveats(lenderCaveat, lender, additionalTransfers, loan);
}

if (additionalTransfers.length > 0) {
_validateAdditionalTransfers(loan.borrower, loan.issuer, msg.sender, additionalTransfers);
_validateAdditionalTransfers(loan.borrower, lender, msg.sender, additionalTransfers);
StarPortLib.transferAdditionalTransfers(additionalTransfers);
}

Expand Down Expand Up @@ -469,6 +453,14 @@ contract LoanManager is Ownable, ERC721 {
);
}

function incrementCaveatNonce() external {
++caveatNonces[msg.sender];
}

function invalidateCaveatSalt(bytes32 salt) external {
invalidHashes[msg.sender][salt] = true;
}

/**
* @dev the erc721 name of the contract
* @return The name of the contract as a string
Expand Down
67 changes: 67 additions & 0 deletions src/lib/PausableNonReentrant.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {Ownable} from "solady/src/auth/Ownable.sol";

abstract contract PausableNonReentrant is Ownable {
uint256 private constant _UNLOCKED = 0x1;
uint256 private constant _LOCKED = 0x2;
uint256 private constant _PAUSED = 0x3;

uint256 private _state = _UNLOCKED;

event Paused();
event Unpaused();

error IsPaused();
error IsLocked();
error NotPaused();

modifier pausableNonReentrant() {
assembly {
//If locked or paused, handle revert cases
if gt(sload(_state.slot), _UNLOCKED) {
if gt(sload(_state.slot), _LOCKED) {
//Revert IsPaused
mstore(0, 0x1309a563)
revert(0x1c, 0x04)
}
//Revert IsLocked
mstore(0, 0xcaa30f55)
revert(0x1c, 0x04)
}
sstore(_state.slot, _LOCKED)
}
_;
assembly {
sstore(_state.slot, _UNLOCKED)
}
}

function pause() external onlyOwner {
assembly {
//If locked, prevent owner from overriding state
if eq(sload(_state.slot), _LOCKED) {
//Revert IsLocked
mstore(0, 0xcaa30f55)
revert(0x1c, 0x04)
}
sstore(_state.slot, _PAUSED)
}
emit Paused();
}

function unpause() external onlyOwner {
assembly {
//If not paused, prevent owner from overriding state
if lt(sload(_state.slot), _PAUSED) {
//Revert NotPaused
mstore(0, 0x6cd60201)
revert(0x1c, 0x04)
}
sstore(_state.slot, _UNLOCKED)
}
emit Unpaused();
}

function paused() external view returns (bool) {
return _state == _PAUSED;
}
}
17 changes: 16 additions & 1 deletion test/StarPortTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,21 @@ contract StarPortTest is BaseOrderTest {
}
}

function getBorrowerSignedCaveat(
BorrowerEnforcer.Details memory details,
Account memory signer,
bytes32 salt,
address enforcer
) public view returns (CaveatEnforcer.CaveatWithApproval memory caveatApproval) {
caveatApproval.caveat = new CaveatEnforcer.Caveat[](1);
caveatApproval.salt = salt;
caveatApproval.caveat[0] =
CaveatEnforcer.Caveat({enforcer: enforcer, deadline: block.timestamp + 1 days, data: abi.encode(details)});
bytes32 hash = LM.hashCaveatWithSaltAndNonce(signer.addr, salt, caveatApproval.caveat);

(caveatApproval.v, caveatApproval.r, caveatApproval.s) = vm.sign(signer.key, hash);
}

function getLenderSignedCaveat(
LenderEnforcer.Details memory details,
Account memory signer,
Expand Down Expand Up @@ -498,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,) =
Expand Down
Loading

0 comments on commit 595ee71

Please sign in to comment.