Skip to content

Commit

Permalink
add postRepayment call to refinance flow, update v1 tests to conform …
Browse files Browse the repository at this point in the history
…with new withdraw mechanisms inside the v1 settlement
  • Loading branch information
androolloyd committed Nov 4, 2023
1 parent 802befc commit 1e7a9a5
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 35 deletions.
11 changes: 3 additions & 8 deletions src/Custodian.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions src/Starport.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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_));
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down
17 changes: 11 additions & 6 deletions src/settlement/AstariaV1Settlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 1 addition & 3 deletions src/settlement/DutchAuctionSettlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion src/settlement/Settlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
24 changes: 7 additions & 17 deletions test/integration-testing/TestAstariaV1Loan.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"
);
}
Expand All @@ -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");
}
}

Expand Down
36 changes: 36 additions & 0 deletions test/unit-testing/TestV1Status.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down

0 comments on commit 1e7a9a5

Please sign in to comment.