Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/AstariaXYZ/starport into …
Browse files Browse the repository at this point in the history
…feat/exotic-repayment-settlement-tests
  • Loading branch information
androolloyd committed Nov 20, 2023
2 parents 37acaad + 92c45f0 commit 6001c62
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 196 deletions.
238 changes: 120 additions & 118 deletions .gas-snapshot

Large diffs are not rendered by default.

56 changes: 42 additions & 14 deletions src/Starport.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,9 @@ contract Starport is PausableNonReentrant {
BORROWER,
LENDER
}
enum FieldFlags {
INACTIVE,
ACTIVE
}

uint256 public constant LOAN_INACTIVE_FLAG = 0x0;
uint256 public constant LOAN_ACTIVE_FLAG = 0x1;

struct Terms {
address status; //the address of the status module
Expand Down Expand Up @@ -415,7 +414,7 @@ contract Starport is PausableNonReentrant {
* @return True if the loan is active
*/
function active(uint256 loanId) public view returns (bool) {
return loanState[loanId] == uint256(FieldFlags.ACTIVE);
return loanState[loanId] == LOAN_ACTIVE_FLAG;
}

/**
Expand All @@ -424,7 +423,7 @@ contract Starport is PausableNonReentrant {
* @return True if the loan is inactive
*/
function inactive(uint256 loanId) public view returns (bool) {
return loanState[loanId] == uint256(FieldFlags.INACTIVE);
return loanState[loanId] == LOAN_INACTIVE_FLAG;
}

/**
Expand All @@ -439,13 +438,28 @@ contract Starport is PausableNonReentrant {
_settle(loan);
}

bytes32 private constant _INVALID_LOAN = 0x045f33d100000000000000000000000000000000000000000000000000000000;
bytes32 private constant _LOAN_EXISTS = 0x14ec57fc00000000000000000000000000000000000000000000000000000000;

function _settle(Loan memory loan) internal {
uint256 loanId = loan.getId();
if (inactive(loanId)) {
revert InvalidLoan();
}
assembly {
mstore(0x0, loanId)
mstore(0x20, loanState.slot)

// loanState[loanId]

loanState[loanId] = uint256(FieldFlags.INACTIVE);
let loc := keccak256(0x0, 0x40)

// if (inactive(loanId)) {
if iszero(sload(loc)) {
//revert InvalidLoan()
mstore(0x0, _INVALID_LOAN)
revert(0x0, 0x04)
}

sstore(loc, LOAN_INACTIVE_FLAG)
}

emit Close(loanId);
}
Expand Down Expand Up @@ -533,11 +547,25 @@ contract Starport is PausableNonReentrant {
loan.originator = loan.originator != address(0) ? loan.originator : msg.sender;

uint256 loanId = loan.getId();
if (active(loanId)) {
revert LoanExists();
}
// if (active(loanId)) {
// revert LoanExists();
// }
//
assembly {
mstore(0x0, loanId)
mstore(0x20, loanState.slot)

//loanState[loanId]
let loc := keccak256(0x0, 0x40)
// if (active(loanId))
if iszero(iszero(sload(loc))) {
//revert LoanExists()
mstore(0x0, _LOAN_EXISTS)
revert(0x0, 0x04)
}

loanState[loanId] = uint256(FieldFlags.ACTIVE);
sstore(loc, LOAN_ACTIVE_FLAG)
}
emit Open(loanId, loan);
}
}
2 changes: 1 addition & 1 deletion src/enforcers/BorrowerEnforcer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract BorrowerEnforcer is CaveatEnforcer {
Details memory details
) internal pure {
details.loan.issuer = loan.issuer;

details.loan.originator = loan.originator;
if (keccak256(abi.encode(loan)) != keccak256(abi.encode(details.loan))) revert InvalidLoanTerms();

if (additionalTransfers.length > 0) {
Expand Down
2 changes: 1 addition & 1 deletion src/enforcers/BorrowerEnforcerBNPL.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ contract BorrowerEnforcerBNPL is CaveatEnforcer {
revert BorrowerOnlyEnforcer();
}
details.loan.issuer = loan.issuer;

details.loan.originator = loan.originator;
if (loanHash != keccak256(abi.encode(details.loan))) {
revert InvalidLoanTerms();
}
Expand Down
1 change: 1 addition & 0 deletions src/enforcers/LenderEnforcer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ contract LenderEnforcer is CaveatEnforcer {
Details memory details
) internal pure {
details.loan.borrower = loan.borrower;
details.loan.originator = loan.originator;

if (keccak256(abi.encode(loan)) != keccak256(abi.encode(details.loan))) {
revert InvalidLoanTerms();
Expand Down
1 change: 1 addition & 0 deletions test/StarportTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ contract StarportTest is BaseOrderTest {
) internal returns (Starport.Loan memory) {
(CaveatEnforcer.SignedCaveats memory borrowerCaveat, CaveatEnforcer.SignedCaveats memory lenderCaveat) =
newLoanOriginationSetup(loan, borrower, borrowerSalt, lender, lenderSalt, invalidateLender);
vm.roll(1);
return newLoan(loan, borrowerCaveat, lenderCaveat, fulfiller);
}

Expand Down
109 changes: 59 additions & 50 deletions test/fuzz-testing/TestFuzzStarport.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
using FixedPointMathLib for uint256;
using {StarportLib.getId} for Starport.Loan;

function setUp() public override {
function setUp() public virtual override {
super.setUp();

vm.warp(100_000);
Expand Down Expand Up @@ -107,7 +107,8 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
vm.stopPrank();
}

function boundPricingData(uint256 min) internal view returns (bytes memory pricingData) {
function boundPricingData(bytes memory boundPricingData) internal view virtual returns (bytes memory pricingData) {
uint256 min = abi.decode(boundPricingData, (uint256));
BasePricing.Details memory details = BasePricing.Details({
rate: _boundMax(min, (uint256(1e16) * 150)),
carryRate: _boundMax(0, uint256((1e16 * 100))),
Expand All @@ -116,13 +117,18 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
pricingData = abi.encode(details);
}

function boundStatusData() internal view returns (bytes memory statusData) {
function boundStatusData(bytes memory boundStatusData) internal view virtual returns (bytes memory statusData) {
FixedTermStatus.Details memory boundDetails =
FixedTermStatus.Details({loanDuration: _boundMax(1 hours, 1095 days)});
statusData = abi.encode(boundDetails);
}

function boundSettlementData() internal view returns (bytes memory settlementData) {
function boundSettlementData(bytes memory boundSettlementData)
internal
view
virtual
returns (bytes memory settlementData)
{
DutchAuctionSettlement.Details memory boundDetails = DutchAuctionSettlement.Details({
startingPrice: _boundMax(501 ether, 1000 ether),
endingPrice: _boundMax(1 ether, 500 ether),
Expand All @@ -131,13 +137,19 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
settlementData = abi.encode(boundDetails);
}

function boundFuzzLenderTerms(LoanBounds memory loanBounds) internal view returns (Starport.Terms memory terms) {
function boundFuzzLenderTerms(bytes memory loanBoundsData)
internal
view
virtual
returns (Starport.Terms memory terms)
{
LoanBounds memory loanBounds = abi.decode(loanBoundsData, (LoanBounds));
terms.status = address(status);
terms.settlement = address(settlement);
terms.pricing = address(pricing);
terms.pricingData = boundPricingData(loanBounds.minPricing);
terms.statusData = boundStatusData();
terms.settlementData = boundSettlementData();
terms.pricingData = boundPricingData(abi.encode(loanBounds.minPricing));
terms.statusData = boundStatusData("");
terms.settlementData = boundSettlementData("");
}

struct FuzzLoan {
Expand Down Expand Up @@ -173,12 +185,14 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
uint256 skipTime;
}

function boundFuzzLoan(FuzzLoan memory params, LoanBounds memory loanBounds)
function boundFuzzLoan(FuzzLoan memory params, bytes memory loanBoundsData)
internal
virtual
returns (Starport.Loan memory loan)
{
LoanBounds memory loanBounds = abi.decode(loanBoundsData, (LoanBounds));
uint256 length = _boundMax(1, 4);
loan.terms = boundFuzzLenderTerms(loanBounds);
loan.terms = boundFuzzLenderTerms(loanBoundsData);
uint256 i = 0;
if (length > params.collateral.length) {
length = params.collateral.length;
Expand Down Expand Up @@ -215,8 +229,7 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
return loan;
}

function willArithmeticOverflow(Starport.Loan memory loan) internal view returns (bool) {
FixedTermStatus.Details memory statusDetails = abi.decode(loan.terms.statusData, (FixedTermStatus.Details));
function willArithmeticOverflow(Starport.Loan memory loan) internal view virtual returns (bool) {
BasePricing.Details memory pricingDetails = abi.decode(loan.terms.pricingData, (BasePricing.Details));
try BasePricing(loan.terms.pricing).getPaymentConsideration(loan) returns (
SpentItem[] memory repayConsideration, SpentItem[] memory carryConsideration
Expand All @@ -233,8 +246,8 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
}
}

function testFuzzNewOrigination(FuzzLoan memory params) public {
fuzzNewLoanOrigination(params, LoanBounds(0));
function testFuzzNewOrigination(FuzzLoan memory params) public virtual {
_generateGoodLoan(params);
}

struct LoanBounds {
Expand All @@ -247,12 +260,12 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
uint256[] borrowerReceivedDebt;
}

function fuzzNewLoanOrigination(FuzzLoan memory params, LoanBounds memory loanBounds)
function fuzzNewLoanOrigination(FuzzLoan memory params, bytes memory loanBoundsData)
internal
returns (Starport.Loan memory goodLoan)
{
vm.assume(params.collateral.length > 1);
Starport.Loan memory loan = boundFuzzLoan(params, loanBounds);
Starport.Loan memory loan = boundFuzzLoan(params, loanBoundsData);
vm.assume(!willArithmeticOverflow(loan));

uint88 feeRake = uint88(_boundMax(0, 1e17));
Expand Down Expand Up @@ -292,9 +305,9 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
Fuzz.SpentItem[10] memory collateral,
Fuzz.SpentItem[10] memory debt,
address[3] memory badAddresses
) public returns (Starport.Loan memory loan) {
) public virtual returns (Starport.Loan memory loan) {
uint256 length = _boundMin(0, collateral.length);
loan.terms = boundFuzzLenderTerms(LoanBounds(0));
loan.terms = boundFuzzLenderTerms(abi.encode(LoanBounds(0)));
uint256 i = 0;
SpentItem[] memory ret = new SpentItem[](length);

Expand Down Expand Up @@ -326,25 +339,19 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
}

function testFuzzCustodianGeneratePreviewOrder(FuzzCustodian memory params) public {
// Starport.Loan memory badLoan = boundBadLoan(params.repayCollateral, params.repayDebt, params.badAddresses);
Starport.Loan memory goodLoan = fuzzNewLoanOrigination(params.origination, LoanBounds(0));

// badLoan.collateral = goodLoan.collateral;
// badLoan.debt = goodLoan.debt;
// badLoan.custodian = goodLoan.custodian;
Starport.Loan memory goodLoan = _generateGoodLoan(params.origination);

Custodian.Command memory cmd;

uint256 loanDuration = abi.decode(goodLoan.terms.statusData, (FixedTermStatus.Details)).loanDuration;
if (params.willRepay) {
skip(loanDuration - 1);
_skipToRepayment(goodLoan);
if (!params.wrongCommand) {
cmd = Custodian.Command(Actions.Repayment, goodLoan, "");
} else {
cmd = Custodian.Command(Actions.Settlement, goodLoan, "");
}
} else {
skip(loanDuration + 1);
_skipToSettlement(goodLoan);
if (!params.wrongCommand) {
cmd = Custodian.Command(Actions.Settlement, goodLoan, "");
} else {
Expand Down Expand Up @@ -373,7 +380,7 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {

function testFuzzLoanState(FuzzRepaymentLoan memory params) public {
Starport.Loan memory badLoan = boundBadLoan(params.repayCollateral, params.repayDebt, params.badAddresses);
Starport.Loan memory goodLoan = fuzzNewLoanOrigination(params.origination, LoanBounds(0));
Starport.Loan memory goodLoan = _generateGoodLoan(params.origination);

badLoan.start = goodLoan.start;
badLoan.originator = goodLoan.originator;
Expand All @@ -387,7 +394,7 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {

function testFuzzRepaymentFails(FuzzRepaymentLoan memory params) public {
Starport.Loan memory badLoan = boundBadLoan(params.repayCollateral, params.repayDebt, params.badAddresses);
Starport.Loan memory goodLoan = fuzzNewLoanOrigination(params.origination, LoanBounds(0));
Starport.Loan memory goodLoan = _generateGoodLoan(params.origination);

badLoan.collateral = goodLoan.collateral;
badLoan.debt = goodLoan.debt;
Expand Down Expand Up @@ -432,8 +439,8 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
}

function testFuzzRepaymentSuccess(FuzzRepaymentLoan memory params) public {
Starport.Loan memory goodLoan = fuzzNewLoanOrigination(params.origination, LoanBounds(0));
skip(_boundMax(1, abi.decode(goodLoan.terms.statusData, (FixedTermStatus.Details)).loanDuration - 1));
Starport.Loan memory goodLoan = _generateGoodLoan(params.origination);
_skipToRepayment(goodLoan);

(SpentItem[] memory offer, ReceivedItem[] memory paymentConsideration) = Custodian(payable(goodLoan.custodian))
.previewOrder(
Expand Down Expand Up @@ -472,19 +479,13 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {

function testFuzzSettlementFails(FuzzRepaymentLoan memory params) public {
Starport.Loan memory badLoan = boundBadLoan(params.repayCollateral, params.repayDebt, params.badAddresses);
Starport.Loan memory goodLoan = fuzzNewLoanOrigination(params.origination, LoanBounds(0));
Starport.Loan memory goodLoan = _generateGoodLoan(params.origination);

badLoan.collateral = goodLoan.collateral;
badLoan.debt = goodLoan.debt;
badLoan.custodian = goodLoan.custodian;

skip(
_bound(
params.skipTime,
abi.decode(goodLoan.terms.statusData, (FixedTermStatus.Details)).loanDuration,
1000 days
)
);
_skipToSettlement(goodLoan);

(SpentItem[] memory offer, ReceivedItem[] memory paymentConsideration) = Custodian(payable(goodLoan.custodian))
.previewOrder(
Expand Down Expand Up @@ -524,20 +525,28 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
vm.stopPrank();
}

function testFuzzSettlementSuccess(FuzzSettleLoan memory params) public {
Starport.Loan memory goodLoan = fuzzNewLoanOrigination(params.origination, LoanBounds(0));
function _generateGoodLoan(FuzzLoan memory params) internal virtual returns (Starport.Loan memory) {
return fuzzNewLoanOrigination(params, abi.encode(LoanBounds(0)));
}

address filler = _toAddress(_boundMin(_toUint(params.origination.fulfiller), 100));
vm.assume(filler.code.length == 0);
function _skipToSettlement(Starport.Loan memory goodLoan) internal virtual {
FixedTermStatus.Details memory statusDetails = abi.decode(goodLoan.terms.statusData, (FixedTermStatus.Details));

skip(
_bound(
params.skipTime,
abi.decode(goodLoan.terms.statusData, (FixedTermStatus.Details)).loanDuration,
uint256(1000 days)
)
_bound(0, abi.decode(goodLoan.terms.statusData, (FixedTermStatus.Details)).loanDuration, uint256(1000 days))
);
}

function _skipToRepayment(Starport.Loan memory goodLoan) internal virtual {
skip(_boundMax(1, abi.decode(goodLoan.terms.statusData, (FixedTermStatus.Details)).loanDuration - 1));
}

function testFuzzSettlementSuccess(FuzzSettleLoan memory params) public virtual {
Starport.Loan memory goodLoan = _generateGoodLoan(params.origination);

address filler = _toAddress(_boundMin(_toUint(params.origination.fulfiller), 100));
vm.assume(filler.code.length == 0);
_skipToSettlement(goodLoan);
(SpentItem[] memory offer, ReceivedItem[] memory paymentConsideration) = Custodian(payable(goodLoan.custodian))
.previewOrder(
address(consideration),
Expand Down Expand Up @@ -576,8 +585,8 @@ contract TestFuzzStarport is StarportTest, Bound, DeepEq {
vm.stopPrank();
}

function testFuzzRefinance(FuzzRefinanceLoan memory params) public {
Starport.Loan memory goodLoan = fuzzNewLoanOrigination(params.origination, LoanBounds(1));
function testFuzzRefinance(FuzzRefinanceLoan memory params) public virtual {
Starport.Loan memory goodLoan = fuzzNewLoanOrigination(params.origination, abi.encode(LoanBounds(1)));

BasePricing.Details memory oldDetails = abi.decode(goodLoan.terms.pricingData, (BasePricing.Details));

Expand Down
Loading

0 comments on commit 6001c62

Please sign in to comment.