Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/custodian settlement flow adjustment #51

Merged
merged 12 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 127 additions & 125 deletions .gas-snapshot

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/workflows/lint_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run: forge test -vvv

- name: Snapshot
run: forge snapshot --check
run: forge snapshot --check --no-match-path '*fuzz*'



Expand Down
72 changes: 55 additions & 17 deletions src/Custodian.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ contract Custodian is ERC721, ContractOffererInterface {
error ImplementInChild();
error InvalidAction();
error InvalidFulfiller();
error InvalidHandlerExecution();
error InvalidPostSettlement();
error InvalidPostRepayment();
error InvalidLoan();
error InvalidRepayer();
error NotAuthorized();
error NotSeaport();
error NotEnteredViaSeaport();
error NotStarport();
Expand Down Expand Up @@ -154,16 +156,24 @@ contract Custodian is ERC721, ContractOffererInterface {

_safeMint(loan.borrower, loanId, encodedLoan);
}

/**
* @dev Set's approvals for who can repay a loan on behalf of the borrower.
* @dev Mints a custody token for a loan.
*
* @param who The address of the account to modify approval for
* @param approved The approval status
* @param loan The loan to mint a custody token for
* @param approvedTo The address with pre approvals set
*/
function setRepayApproval(address who, bool approved) external {
repayApproval[msg.sender][who] = approved;
emit RepayApproval(msg.sender, who, approved);

function mintWithApprovalSet(Starport.Loan calldata loan, address approvedTo) external {
bytes memory encodedLoan = abi.encode(loan);
uint256 loanId = uint256(keccak256(encodedLoan));
if (loan.custodian != address(this) || !SP.active(loanId)) {
revert InvalidLoan();
}
if (msg.sender != loan.borrower) {
revert NotAuthorized();
}
_safeMint(loan.borrower, loanId, encodedLoan);
_approve(loan.borrower, approvedTo, loanId);
}

/**
Expand Down Expand Up @@ -208,7 +218,7 @@ contract Custodian is ERC721, ContractOffererInterface {
}
if (action == Actions.Repayment && Status(loan.terms.status).isActive(loan)) {
address borrower = getBorrower(loan);
if (fulfiller != borrower && !repayApproval[borrower][fulfiller]) {
if (fulfiller != borrower && fulfiller != _getApproved(loan.getId())) {
revert InvalidRepayer();
}

Expand All @@ -222,6 +232,7 @@ contract Custodian is ERC721, ContractOffererInterface {
consideration = StarportLib.mergeSpentItemsToReceivedItems(payment, loan.issuer, carry, loan.originator);

_settleLoan(loan);
_postRepaymentExecute(loan, fulfiller);
} else if (action == Actions.Settlement && !Status(loan.terms.status).isActive(loan)) {
address authorized;
//add in originator fee
Expand All @@ -236,18 +247,11 @@ contract Custodian is ERC721, ContractOffererInterface {
_setOfferApprovalsWithSeaport(offer);
} else if (authorized == loan.terms.settlement || authorized == loan.issuer) {
_moveCollateralToAuthorized(loan.collateral, authorized);
_beforeSettlementHandlerHook(loan);
if (
authorized == loan.terms.settlement
&& Settlement(loan.terms.settlement).execute(loan, fulfiller) != Settlement.execute.selector
) {
revert InvalidHandlerExecution();
}
_afterSettlementHandlerHook(loan);
} else {
revert InvalidFulfiller();
}
_settleLoan(loan);
_postSettlementExecute(loan, fulfiller);
} else {
revert InvalidAction();
}
Expand Down Expand Up @@ -400,6 +404,40 @@ contract Custodian is ERC721, ContractOffererInterface {
}
}

/**
* @dev settle the loan with the LoanManager
*
* @param loan The the loan that is settled
* @param fulfiller The address executing seaport
*/
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
) {
revert InvalidPostSettlement();
}
_afterSettlementHandlerHook(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 {
_beforeSettlementHandlerHook(loan);
if (
Settlement(loan.terms.settlement).postRepayment{gas: 100_000}(loan, fulfiller)
!= Settlement.postRepayment.selector
) {
revert InvalidPostRepayment();
}
_afterSettlementHandlerHook(loan);
}

/**
* @dev settle the loan with the LoanManager
*
Expand Down
8 changes: 0 additions & 8 deletions src/Starport.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ 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(Starport.Loan calldata loan) external;
}

contract Starport is ERC721, PausableNonReentrant {
using FixedPointMathLib for uint256;

Expand Down Expand Up @@ -473,10 +469,6 @@ contract Starport is ERC721, PausableNonReentrant {
_burn(tokenId);
}
_setExtraData(tokenId, uint8(FieldFlags.INACTIVE));

if (loan.issuer.code.length > 0) {
loan.issuer.call(abi.encodeWithSelector(LoanSettledCallback.onLoanSettled.selector, loan));
}
emit Close(tokenId);
}

Expand Down
16 changes: 13 additions & 3 deletions src/settlement/AstariaV1Settlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {BaseRecall} from "starport-core/status/BaseRecall.sol";
import {DutchAuctionSettlement} from "starport-core/settlement/DutchAuctionSettlement.sol";
import {StarportLib} from "starport-core/lib/StarportLib.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";

import {Pricing} from "starport-core/pricing/Pricing.sol";
import {BasePricing} from "starport-core/pricing/BasePricing.sol";

Expand Down Expand Up @@ -145,8 +144,19 @@ contract AstariaV1Settlement is DutchAuctionSettlement {
}
}

function execute(Starport.Loan calldata loan, address fulfiller) external virtual override returns (bytes4) {
revert ExecuteHandlerNotImplemented();
function postSettlement(Starport.Loan calldata loan, address fulfiller)
external
virtual
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));
// }
return Settlement.postSettlement.selector;
}

function validate(Starport.Loan calldata loan) external view virtual override returns (bool) {
Expand Down
13 changes: 11 additions & 2 deletions src/settlement/DutchAuctionSettlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,17 @@ abstract contract DutchAuctionSettlement is Settlement, AmountDeriver {
uint256 window;
}

function execute(Starport.Loan calldata loan, address fulfiller) external virtual override returns (bytes4) {
return Settlement.execute.selector;
function postSettlement(Starport.Loan calldata loan, address fulfiller)
external
virtual
override
returns (bytes4)
{
return Settlement.postSettlement.selector;
}

function postRepayment(Starport.Loan calldata loan, address fulfiller) external virtual override returns (bytes4) {
return Settlement.postRepayment.selector;
}

function getAuctionStart(Starport.Loan calldata loan) public view virtual returns (uint256);
Expand Down
14 changes: 12 additions & 2 deletions src/settlement/EnglishAuctionSettlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
OrderType
} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {Pricing} from "starport-core/pricing/Pricing.sol";
import {Status} from "starport-core/status/Status.sol";

contract EnglishAuctionSettlement is Settlement {
using FixedPointMathLib for uint256;
Expand Down Expand Up @@ -55,11 +56,20 @@ contract EnglishAuctionSettlement is Settlement {
return details.reservePrice.length == loan.debt.length;
}

function execute(Starport.Loan calldata loan, address fulfiller) external virtual override returns (bytes4) {
function postSettlement(Starport.Loan calldata loan, address fulfiller)
external
virtual
override
returns (bytes4)
{
if (fulfiller != address(this)) {
revert("must liquidate via the handler to trigger english auction");
}
return Settlement.execute.selector;
return Settlement.postSettlement.selector;
}

function postRepayment(Starport.Loan calldata loan, address fulfiller) external virtual override returns (bytes4) {
return Settlement.postRepayment.selector;
}

function getSettlement(Starport.Loan calldata loan)
Expand Down
9 changes: 7 additions & 2 deletions src/settlement/FixedTermDutchAuctionSettlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ contract FixedTermDutchAuctionSettlement is DutchAuctionSettlement {
return loan.start + details.loanDuration;
}

function execute(Starport.Loan calldata loan, address fulfiller) external virtual override returns (bytes4) {
return Settlement.execute.selector;
function postSettlement(Starport.Loan calldata loan, address fulfiller)
external
virtual
override
returns (bytes4)
{
return Settlement.postSettlement.selector;
}

function validate(Starport.Loan calldata loan) external view virtual override returns (bool) {
Expand Down
4 changes: 3 additions & 1 deletion src/settlement/Settlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ abstract contract Settlement is TokenReceiverInterface {
SP = SP_;
}

function execute(Starport.Loan calldata loan, address fulfiller) external virtual returns (bytes4);
function postSettlement(Starport.Loan calldata loan, address fulfiller) external virtual returns (bytes4);

function postRepayment(Starport.Loan calldata loan, address fulfiller) external virtual returns (bytes4);

function validate(Starport.Loan calldata loan) external view virtual returns (bool);

Expand Down
15 changes: 7 additions & 8 deletions src/status/BaseRecall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ abstract contract BaseRecall {
}

// transfers all stake to anyone who asks after the LM token is burned
function withdraw(Starport.Loan calldata loan, address payable receiver) external {
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));
Expand Down Expand Up @@ -171,12 +171,11 @@ abstract contract BaseRecall {
}
}

function generateRecallConsideration(
Starport.Loan calldata loan,
uint256 proportion,
address from,
address payable to
) external view returns (AdditionalTransfer[] memory consideration) {
function generateRecallConsideration(Starport.Loan calldata loan, uint256 proportion, address from, address to)
external
view
returns (AdditionalTransfer[] memory consideration)
{
Details memory details = abi.decode(loan.terms.statusData, (Details));
return _generateRecallConsideration(loan, 0, details.recallStakeDuration, proportion, from, to);
}
Expand All @@ -187,7 +186,7 @@ abstract contract BaseRecall {
uint256 end,
uint256 proportion,
address from,
address payable to
address to
) internal view returns (AdditionalTransfer[] memory additionalTransfers) {
uint256[] memory stake = _getRecallStake(loan, start, end);
additionalTransfers = new AdditionalTransfer[](stake.length);
Expand Down
4 changes: 2 additions & 2 deletions test/AstariaV1Test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ contract AstariaV1Test is StarportTest {

pricing = new AstariaV1Pricing(SP);
settlement = new AstariaV1Settlement(SP);
hook = new AstariaV1Status(SP);
status = new AstariaV1Status(SP);

lenderEnforcer = new AstariaV1LenderEnforcer();

vm.startPrank(recaller.addr);
erc20s[0].approve(address(hook), 1e18);
erc20s[0].approve(address(status), 1e18);
vm.stopPrank();

// // 1% interest rate per second
Expand Down
11 changes: 4 additions & 7 deletions test/StarportTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ import {ERC721} from "solady/src/tokens/ERC721.sol";
import {ERC1155} from "solady/src/tokens/ERC1155.sol";
import {ContractOffererInterface} from "seaport-types/src/interfaces/ContractOffererInterface.sol";
import {TokenReceiverInterface} from "starport-core/interfaces/TokenReceiverInterface.sol";
import {LoanSettledCallback} from "starport-core/Starport.sol";
import {Actions} from "starport-core/lib/StarportLib.sol";

import {CaveatEnforcer} from "starport-core/enforcers/CaveatEnforcer.sol";
Expand All @@ -77,9 +76,7 @@ interface IWETH9 {
function withdraw(uint256) external;
}

contract MockIssuer is LoanSettledCallback, TokenReceiverInterface {
function onLoanSettled(Starport.Loan memory loan) external {}

contract MockIssuer is TokenReceiverInterface {
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
external
override
Expand Down Expand Up @@ -128,7 +125,7 @@ contract StarportTest is BaseOrderTest {

Pricing pricing;
Settlement settlement;
Status hook;
Status status;

uint256 defaultLoanDuration = 14 days;

Expand Down Expand Up @@ -205,7 +202,7 @@ contract StarportTest is BaseOrderTest {
SO = new StrategistOriginator(SP, strategist.addr, 1e16, address(this));
pricing = new SimpleInterestPricing(SP);
settlement = new FixedTermDutchAuctionSettlement(SP);
hook = new FixedTermStatus();
status = new FixedTermStatus();
vm.label(address(erc721s[0]), "Collateral NFT");
vm.label(address(erc721s[1]), "Collateral2 NFT");
vm.label(address(erc20s[0]), "Debt ERC20");
Expand Down Expand Up @@ -484,7 +481,7 @@ contract StarportTest is BaseOrderTest {
collateral: newCollateral,
debt: newDebt,
terms: Starport.Terms({
status: address(hook),
status: address(status),
settlement: address(settlement),
pricing: address(pricing),
pricingData: defaultPricingData,
Expand Down
Loading