Skip to content

Commit

Permalink
fix: remove permissionless recall
Browse files Browse the repository at this point in the history
  • Loading branch information
dangerousfood committed Feb 6, 2024
1 parent b0f39a7 commit 5d12830
Show file tree
Hide file tree
Showing 14 changed files with 86 additions and 1,431 deletions.
2 changes: 1 addition & 1 deletion lib/starport
Submodule starport updated 0 files
1 change: 1 addition & 0 deletions src/enforcers/AstariaV1LenderEnforcer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {BasePricing} from "v1-core/pricing/BasePricing.sol";
import {AdditionalTransfer} from "starport-core/lib/StarportLib.sol";

import {AstariaV1Lib} from "v1-core/lib/AstariaV1Lib.sol";

import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol";
import {SpentItem} from "seaport-types/src/lib/ConsiderationStructs.sol";

Expand Down
10 changes: 1 addition & 9 deletions src/lib/AstariaV1Lib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@

pragma solidity ^0.8.17;

import {Starport} from "starport-core/Starport.sol";
import {BasePricing} from "v1-core/pricing/BasePricing.sol";
import {StarportLib} from "starport-core/lib/StarportLib.sol";
import "forge-std/console2.sol";

import {BaseRecall} from "v1-core/status/BaseRecall.sol";

import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";

library AstariaV1Lib {
Expand All @@ -36,7 +29,6 @@ library AstariaV1Lib {
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

error InterestAccrualRoundingMinimum();
error UnsupportedDecimalValue();
error RateExceedMaxRecallRate();
error RateTooLow();
Expand Down Expand Up @@ -89,7 +81,7 @@ library AstariaV1Lib {

function getBaseRecallMax(bytes memory statusData) internal pure returns (uint256 recallMax) {
assembly ("memory-safe") {
recallMax := mload(add(0x80, statusData))
recallMax := mload(add(0x60, statusData))
}
}

Expand Down
44 changes: 19 additions & 25 deletions src/pricing/AstariaV1Pricing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,24 @@
pragma solidity ^0.8.17;

import {Starport} from "starport-core/Starport.sol";

import {Pricing} from "starport-core/pricing/Pricing.sol";
import {BasePricing} from "v1-core/pricing/BasePricing.sol";
import {StarportLib, AdditionalTransfer} from "starport-core/lib/StarportLib.sol";
import {BasePricing} from "v1-core/pricing/BasePricing.sol";
import {Validation} from "starport-core/lib/Validation.sol";

import {CompoundInterestPricing} from "v1-core/pricing/CompoundInterestPricing.sol";
import {AstariaV1Status} from "v1-core/status/AstariaV1Status.sol";
import {AstariaV1Lib} from "v1-core/lib/AstariaV1Lib.sol";

import {SpentItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
import {Validation} from "starport-core/lib/Validation.sol";

contract AstariaV1Pricing is CompoundInterestPricing {
using FixedPointMathLib for uint256;
using {StarportLib.getId} for Starport.Loan;

contract AstariaV1Pricing is BasePricing {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

error InsufficientRefinance();
error LoanIsNotRecalled();

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
Expand Down Expand Up @@ -69,29 +65,13 @@ contract AstariaV1Pricing is CompoundInterestPricing {
AstariaV1Status status = AstariaV1Status(loan.terms.status);

if (!status.isRecalled(loan)) {
revert InvalidRefinance();
revert LoanIsNotRecalled();
}
uint256 rate = status.getRecallRate(loan);
// Offered loan did not meet the terms of the recall auction
if (newDetails.rate > rate) {
revert InsufficientRefinance();
}

uint256 proportion;
address payable receiver = payable(loan.issuer);
uint256 loanId = loan.getId();
// Scenario where the recaller is not penalized
// Recaller stake is refunded
if (newDetails.rate > oldDetails.rate) {
proportion = 0;
(receiver,) = status.recalls(loanId);
} else {
// Scenario where the recaller is penalized
// Essentially the old lender and the new lender split the stake of the recaller
// Split is proportional to the difference in rate
proportion = (oldDetails.rate - newDetails.rate) * (10 ** newDetails.decimals) / oldDetails.rate;
}
recallConsideration = status.generateRecallConsideration(loan, proportion, fulfiller, receiver);
}

(repayConsideration, carryConsideration) = getPaymentConsideration(loan);
Expand All @@ -114,4 +94,18 @@ contract AstariaV1Pricing is CompoundInterestPricing {
}
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

// @inheritdoc BasePricing
function calculateInterest(uint256 delta_t, uint256 amount, uint256 rate, uint256 decimals)
public
pure
override
returns (uint256)
{
return AstariaV1Lib.calculateCompoundInterest(delta_t, amount, rate, decimals);
}
}
33 changes: 0 additions & 33 deletions src/pricing/CompoundInterestPricing.sol

This file was deleted.

202 changes: 7 additions & 195 deletions src/settlement/AstariaV1Settlement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,122 +12,36 @@

pragma solidity ^0.8.17;

import {Starport, SpentItem} from "starport-core/Starport.sol";
import {BasePricing} from "v1-core/pricing/BasePricing.sol";
import {Starport} from "starport-core/Starport.sol";
import {Settlement} from "starport-core/settlement/Settlement.sol";
import {StarportLib} from "starport-core/lib/StarportLib.sol";

import {BaseRecall} from "v1-core/status/BaseRecall.sol";

import {AmountDeriver} from "seaport-core/src/lib/AmountDeriver.sol";
import {ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol";
import {Validation} from "starport-core/lib/Validation.sol";

contract AstariaV1Settlement is Settlement, AmountDeriver {
using {StarportLib.getId} for Starport.Loan;
using FixedPointMathLib for uint256;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

error AuctionNotStarted();
error LoanNotRecalled();
error NoAuction();
import {ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol";

contract AstariaV1Settlement is Settlement {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

constructor(Starport SP_) Settlement(SP_) {}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

struct Details {
uint256 startingPrice;
uint256 endingPrice;
uint256 window;
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/**
* @dev retrieve the current auction price
* @param loan The loan in question
* @return uint256 The current auction price
*/
function getCurrentAuctionPrice(Starport.Loan calldata loan) public view virtual returns (uint256) {
(address recaller, uint64 recallStart) = BaseRecall(loan.terms.status).recalls(loan.getId());
if (recaller == loan.issuer || recallStart == uint256(0) || recaller == address(0)) {
revert NoAuction();
}

uint256 start = _getAuctionStart(loan, recallStart);

Details memory details = abi.decode(loan.terms.settlementData, (Details));

return _locateCurrentAmount({
startAmount: details.startingPrice,
endAmount: details.endingPrice,
startTime: start,
endTime: start + details.window,
roundUp: true
});
}

/**
* @dev Get the start of the auction
* @param loan The loan being referenced
* @return uint256 The start of the auction
*/
function getAuctionStart(Starport.Loan calldata loan) public view virtual returns (uint256) {
(, uint64 start) = BaseRecall(loan.terms.status).recalls(loan.getId());
if (start == 0) {
revert LoanNotRecalled();
}
return _getAuctionStart(loan, start);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/* @dev internal helper to get the auction start to save double decoding
* @param loan The loan in question
* @return uint256 The start of the auction
*/
function _getAuctionStart(Starport.Loan calldata loan, uint64 start) internal view virtual returns (uint256) {
uint256 recallWindow = abi.decode(loan.terms.statusData, (BaseRecall.Details)).recallWindow;
return start + recallWindow + 1;
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

// @inheritdoc Settlement
function postSettlement(Starport.Loan calldata loan, address) external virtual override returns (bytes4) {
(address recaller,) = BaseRecall(loan.terms.status).recalls(loan.getId());
_executeWithdraw(loan, recaller);
function postSettlement(Starport.Loan calldata, address) external virtual override returns (bytes4) {
return Settlement.postSettlement.selector;
}

// @inheritdoc Settlement
function postRepayment(Starport.Loan calldata loan, address fulfiller) external virtual override returns (bytes4) {
_executeWithdraw(loan, fulfiller);

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

// @inheritdoc Validation
function validate(Starport.Loan calldata loan) external view virtual override returns (bytes4) {
Details memory details = abi.decode(loan.terms.settlementData, (Details)); // Will revert if this fails
return (details.startingPrice > details.endingPrice) ? Validation.validate.selector : bytes4(0xFFFFFFFF);
return (loan.terms.settlementData.length == 0) ? Validation.validate.selector : bytes4(0xFFFFFFFF);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
Expand All @@ -142,108 +56,6 @@ contract AstariaV1Settlement is Settlement, AmountDeriver {
override
returns (ReceivedItem[] memory consideration, address authorized)
{
uint256 start;
address recaller;
{
uint64 recallStart;
(recaller, recallStart) = BaseRecall(loan.terms.status).recalls(loan.getId());

if (recaller == address(0) || recallStart == uint256(0)) {
revert LoanNotRecalled();
}

if (recaller == loan.issuer) {
return (consideration, recaller);
}

start = _getAuctionStart(loan, recallStart);
}

if (block.timestamp < start) {
revert AuctionNotStarted();
}

Details memory details = abi.decode(loan.terms.settlementData, (Details));

// DutchAuction has failed, give the NFT back to the lender (if they want it 😐)
if (start + details.window < block.timestamp) {
return (new ReceivedItem[](0), loan.issuer);
}

// DutchAuction price for anyone to bid on
uint256 settlementPrice = _locateCurrentAmount({
startAmount: details.startingPrice,
endAmount: details.endingPrice,
startTime: start,
endTime: start + details.window,
roundUp: true
});

consideration = new ReceivedItem[](3);
uint256 i = 0;
BasePricing.Details memory pricingDetails = abi.decode(loan.terms.pricingData, (BasePricing.Details));
uint256 interest = BasePricing(loan.terms.pricing).getInterest(
loan, pricingDetails.rate, loan.start, block.timestamp, 0, pricingDetails.decimals
);
SpentItem calldata debtItem = loan.debt[0];

uint256 carry = (interest * pricingDetails.carryRate) / 10 ** pricingDetails.decimals;

if (carry > 0 && debtItem.amount + interest - carry < settlementPrice) {
uint256 excess = settlementPrice - (debtItem.amount + interest - carry);
consideration[i] = ReceivedItem({
itemType: debtItem.itemType,
identifier: debtItem.identifier,
amount: (excess > carry) ? carry : excess,
token: debtItem.token,
recipient: payable(loan.originator)
});
settlementPrice -= consideration[i].amount;
unchecked {
++i;
}
}

BaseRecall.Details memory hookDetails = abi.decode(loan.terms.statusData, (BaseRecall.Details));

uint256 recallerReward = (settlementPrice * hookDetails.recallerRewardRatio) / 10 ** pricingDetails.decimals;

if (recallerReward > 0) {
consideration[i] = ReceivedItem({
itemType: debtItem.itemType,
identifier: debtItem.identifier,
amount: recallerReward,
token: debtItem.token,
recipient: payable(recaller)
});
settlementPrice -= consideration[i].amount;
unchecked {
++i;
}
}

consideration[i] = ReceivedItem({
itemType: debtItem.itemType,
identifier: debtItem.identifier,
amount: settlementPrice,
token: debtItem.token,
recipient: payable(loan.issuer)
});

unchecked {
++i;
}

assembly ("memory-safe") {
mstore(consideration, i)
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

function _executeWithdraw(Starport.Loan calldata loan, address fulfiller) internal {
loan.terms.status.call(abi.encodeWithSelector(BaseRecall.withdraw.selector, loan, fulfiller));
return (consideration, loan.issuer);
}
}
Loading

0 comments on commit 5d12830

Please sign in to comment.