-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: implement ratio lender * feat: tests for ratio lender
- Loading branch information
1 parent
3796779
commit da69f76
Showing
4 changed files
with
470 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,68 @@ | ||
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallAuctionFailLenderClaim() (gas: 569882) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallBase() (gas: 910054) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLenderClaimRandomFulfiller() (gas: 567197) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralRecallNotBlockedBorrower() (gas: 473813) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralRecallNotBlockedLender() (gas: 473538) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralRecallerNotBorrowerOrLender() (gas: 446127) | ||
TestAstariaV1Pricing:testGetPaymentConsiderationIncrementation() (gas: 57195) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationAsBorrowerZeroRate() (gas: 60433) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationInsufficientRefinance() (gas: 95727) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationInvalidRefinance() (gas: 59300) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationValidEqualRate() (gas: 97989) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationValidHigherRate() (gas: 108970) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationValidLowerRate() (gas: 104617) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationZeroRate() (gas: 64686) | ||
TestAstariaV1Pricing:testGetRefinanceNewDecimalMismatch() (gas: 64618) | ||
TestAstariaV1Pricing:testV1PricingValidateInvalid() (gas: 56870) | ||
TestAstariaV1Pricing:testV1PricingValidateValid() (gas: 57388) | ||
TestAstariaV1Settlement:testGetSettlementConsideration() (gas: 440480) | ||
TestAstariaV1Settlement:testV1SettlementHandlerValidate() (gas: 436565) | ||
TestAstariaV1Settlement:testV1SettlementValidateInvalid() (gas: 51055) | ||
TestAstariaV1Settlement:testV1SettlementValidateValid() (gas: 50398) | ||
TestAstariaV1Status:testCannotRecallTwice() (gas: 530871) | ||
TestAstariaV1Status:testInvalidRecallLoanDoesNotExist() (gas: 497581) | ||
TestAstariaV1Status:testIsActive() (gas: 446151) | ||
TestAstariaV1Status:testIsRecalledInsideWindow() (gas: 541272) | ||
TestAstariaV1Status:testIsRecalledOutsideWindow() (gas: 538810) | ||
TestAstariaV1Status:testRecallAndRefinanceInsideWindow() (gas: 683957) | ||
TestAstariaV1Status:testRecallAndRefinanceWithLenderCaveat() (gas: 742794) | ||
TestAstariaV1Status:testRecallPauseable() (gas: 19669) | ||
TestAstariaV1Status:testRecallRateActiveRecall() (gas: 527199) | ||
TestAstariaV1Status:testRecallRateEmptyRecall() (gas: 446889) | ||
TestAstariaV1Status:testV1StatusValidateInValid() (gas: 58479) | ||
TestAstariaV1Status:testV1StatusValidateValid() (gas: 51916) | ||
TestCompoundInterest:testDecimalsTooHigh() (gas: 3318) | ||
TestCompoundInterest:testInterestAccrual() (gas: 52624) | ||
TestCompoundInterest:testMaxAmountDecimals() (gas: 7891) | ||
TestCompoundInterest:testRateExceedsMaxRecallRate() (gas: 3297) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallAuctionFailLenderClaim() (gas: 569913) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallBase() (gas: 910085) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralDefaultTermsRecallLenderClaimRandomFulfiller() (gas: 567097) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralRecallNotBlockedBorrower() (gas: 473733) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralRecallNotBlockedLender() (gas: 473569) | ||
TestAstariaV1Loan:testNewLoanERC721CollateralRecallerNotBorrowerOrLender() (gas: 446158) | ||
TestAstariaV1Pricing:testGetPaymentConsiderationIncrementation() (gas: 57204) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationAsBorrowerZeroRate() (gas: 60464) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationInsufficientRefinance() (gas: 95736) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationInvalidRefinance() (gas: 59309) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationValidEqualRate() (gas: 97998) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationValidHigherRate() (gas: 109001) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationValidLowerRate() (gas: 104626) | ||
TestAstariaV1Pricing:testGetRefinanceConsiderationZeroRate() (gas: 64695) | ||
TestAstariaV1Pricing:testGetRefinanceNewDecimalMismatch() (gas: 64649) | ||
TestAstariaV1Pricing:testV1PricingValidateInvalid() (gas: 56901) | ||
TestAstariaV1Pricing:testV1PricingValidateValid() (gas: 57419) | ||
TestAstariaV1Settlement:testGetSettlementConsideration() (gas: 440400) | ||
TestAstariaV1Settlement:testV1SettlementHandlerValidate() (gas: 436596) | ||
TestAstariaV1Settlement:testV1SettlementValidateInvalid() (gas: 51064) | ||
TestAstariaV1Settlement:testV1SettlementValidateValid() (gas: 50429) | ||
TestAstariaV1Status:testCannotRecallTwice() (gas: 530858) | ||
TestAstariaV1Status:testInvalidRecallLoanDoesNotExist() (gas: 497612) | ||
TestAstariaV1Status:testIsActive() (gas: 446160) | ||
TestAstariaV1Status:testIsRecalledInsideWindow() (gas: 541303) | ||
TestAstariaV1Status:testIsRecalledOutsideWindow() (gas: 538841) | ||
TestAstariaV1Status:testRecallAndRefinanceInsideWindow() (gas: 683988) | ||
TestAstariaV1Status:testRecallAndRefinanceWithLenderCaveat() (gas: 742715) | ||
TestAstariaV1Status:testRecallPauseable() (gas: 19691) | ||
TestAstariaV1Status:testRecallRateActiveRecall() (gas: 527208) | ||
TestAstariaV1Status:testRecallRateEmptyRecall() (gas: 446920) | ||
TestAstariaV1Status:testV1StatusValidateInValid() (gas: 58510) | ||
TestAstariaV1Status:testV1StatusValidateValid() (gas: 51925) | ||
TestCompoundInterest:testDecimalsTooHigh() (gas: 3340) | ||
TestCompoundInterest:testInterestAccrual() (gas: 52646) | ||
TestCompoundInterest:testMaxAmountDecimals() (gas: 7913) | ||
TestCompoundInterest:testRateExceedsMaxRecallRate() (gas: 3319) | ||
TestCompoundInterest:testRateTooLowZero() (gas: 3413) | ||
TestV1BorrowerEnforcer:testFuzzRateMethods((uint256,uint256,uint256),uint256) (runs: 10000, μ: 1223, ~: 1223) | ||
TestV1BorrowerEnforcer:testRevertLocateCurrentRateAndAmount() (gas: 50278) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerCollateralAmountOOB() (gas: 112121) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerDebtAmountOOB() (gas: 99409) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerDebtBundlesNotSupported() (gas: 57321) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerEnd() (gas: 106805) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerHalfway() (gas: 101968) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerRateGTCurrent() (gas: 81834) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerRateLTCurrent() (gas: 89323) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerStart() (gas: 86476) | ||
TestV1LenderEnforcer:testV1LenderEnforcerAdditionalTransfers() (gas: 105167) | ||
TestV1LenderEnforcer:testV1LenderEnforcerAmount() (gas: 173982) | ||
TestV1LenderEnforcer:testV1LenderEnforcerDebtBundlesNotSupported() (gas: 63352) | ||
TestV1LenderEnforcer:testV1LenderEnforcerMatchIdentifier() (gas: 109746) | ||
TestV1LenderEnforcer:testV1LenderEnforcerMinDebtExceedsMax() (gas: 77387) | ||
TestV1LenderEnforcer:testV1LenderEnforcerRate() (gas: 102337) | ||
TestV1BorrowerEnforcer:testFuzzRateMethods((uint256,uint256,uint256),uint256) (runs: 10000, μ: 1245, ~: 1245) | ||
TestV1BorrowerEnforcer:testRevertLocateCurrentRateAndAmount() (gas: 50309) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerCollateralAmountOOB() (gas: 112152) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerDebtAmountOOB() (gas: 99418) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerDebtBundlesNotSupported() (gas: 57330) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerEnd() (gas: 106836) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerHalfway() (gas: 101999) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerRateGTCurrent() (gas: 81865) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerRateLTCurrent() (gas: 89310) | ||
TestV1BorrowerEnforcer:testV1BorrowerEnforcerStart() (gas: 86485) | ||
TestV1LenderEnforcer:testV1LenderEnforcerAdditionalTransfers() (gas: 105176) | ||
TestV1LenderEnforcer:testV1LenderEnforcerAmount() (gas: 173991) | ||
TestV1LenderEnforcer:testV1LenderEnforcerDebtBundlesNotSupported() (gas: 63361) | ||
TestV1LenderEnforcer:testV1LenderEnforcerMatchIdentifier() (gas: 109755) | ||
TestV1LenderEnforcer:testV1LenderEnforcerMinDebtExceedsMax() (gas: 77418) | ||
TestV1LenderEnforcer:testV1LenderEnforcerRate() (gas: 102346) | ||
TestV1RatioLenderEnforcer:testV1LenderEnforcerCopyBorrower() (gas: 82012) | ||
TestV1RatioLenderEnforcer:testV1LenderEnforcerCopyCollateralAmount() (gas: 81517) | ||
TestV1RatioLenderEnforcer:testV1LenderEnforcerCopyDebtAmount() (gas: 81325) | ||
TestV1RatioLenderEnforcer:testV1LenderEnforcerCopyOriginator() (gas: 84119) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderCollateralBundle() (gas: 72008) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderDebtAmountExceedsDebtMax() (gas: 79937) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderDebtBundle() (gas: 71732) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderDefault() (gas: 83916) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderEnforcerAdditionalTransfers() (gas: 116076) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderEnforcerMatchIdentifier() (gas: 112205) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderEnforcerRate() (gas: 104962) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderLoanRateLessThanCaveatRate() (gas: 79672) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderMaxDebtOrCollateralToDebtRatioZero() (gas: 79502) | ||
TestV1RatioLenderEnforcer:testV1RatioLenderMinCollateralAmount() (gas: 105956) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// █████╗ ███████╗████████╗ █████╗ ██████╗ ██╗ █████╗ ██╗ ██╗ ██╗ | ||
// ██╔══██╗██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██║██╔══██╗ ██║ ██║███║ | ||
// ███████║███████╗ ██║ ███████║██████╔╝██║███████║ ██║ ██║╚██║ | ||
// ██╔══██║╚════██║ ██║ ██╔══██║██╔══██╗██║██╔══██║ ╚██╗ ██╔╝ ██║ | ||
// ██║ ██║███████║ ██║ ██║ ██║██║ ██║██║██║ ██║ ╚████╔╝ ██║ | ||
// ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝ | ||
// | ||
// Astaria v1 Lending | ||
// Built on Starport https://github.com/astariaXYZ/starport | ||
// Designed with love by Astaria Labs, Inc | ||
|
||
pragma solidity ^0.8.17; | ||
|
||
import {Starport} from "starport-core/Starport.sol"; | ||
import {CaveatEnforcer} from "starport-core/enforcers/CaveatEnforcer.sol"; | ||
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 {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol"; | ||
|
||
contract AstariaV1RatioLenderEnforcer is CaveatEnforcer { | ||
using FixedPointMathLib for uint256; | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* CUSTOM ERRORS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
error InvalidLoanTerms(); | ||
error InvalidAdditionalTransfer(); | ||
error LoanRateLessThanCaveatRate(); | ||
error DebtBundlesNotSupported(); | ||
error CollateralBundlesNotSupported(); | ||
error DebtAmountExceedsDebtMax(uint256 maxDebt, uint256 debtAmount); | ||
error BelowMinCollateralAmount(); | ||
error MaxDebtOrCollateralToDebtRatioZero(); | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* STRUCTS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
struct Details { | ||
bool matchIdentifier; | ||
uint256 minCollateralAmount; | ||
uint256 collateralToDebtRatio; // WAD | ||
Starport.Loan loan; | ||
} | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* PUBLIC FUNCTIONS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @notice Validates a loan against a caveat, w/ a minCollateralAmount, collateralToDebtRatio, and a matchIdentifier | ||
/// @dev collateralToDebtRatio is a 1e18 value allowing a ratio conversion from collateral to debt, 1e18 was used to allow flexibility on the collateral units lower bound | ||
/// @dev Collateral bundle support is not implemented, and will revert | ||
/// @dev Debt bundle support is not implemented, and will revert | ||
/// @dev matchIdentifier = false will allow the loan to have a different identifier than the caveat | ||
/// @dev Only viable for use w/ AstariaV1Pricing and AstariaV1Status modules | ||
function validate( | ||
AdditionalTransfer[] calldata additionalTransfers, | ||
Starport.Loan calldata loan, | ||
bytes calldata caveatData | ||
) public view virtual override returns (bytes4 selector) { | ||
if (loan.debt.length > 1) { | ||
revert DebtBundlesNotSupported(); | ||
} | ||
|
||
if (loan.collateral.length > 1) { | ||
revert CollateralBundlesNotSupported(); | ||
} | ||
|
||
Starport.Terms calldata loanTerms = loan.terms; | ||
uint256 loanRate = abi.decode(loanTerms.pricingData, (BasePricing.Details)).rate; | ||
uint256 debtAmount = loan.debt[0].amount; | ||
AstariaV1Lib.validateCompoundInterest( | ||
debtAmount, | ||
loanRate, | ||
AstariaV1Lib.getBaseRecallMax(loanTerms.statusData), | ||
AstariaV1Lib.getBasePricingDecimals(loanTerms.pricingData) | ||
); | ||
|
||
Details memory details = abi.decode(caveatData, (Details)); | ||
|
||
uint256 collateralAmount = loan.collateral[0].amount; | ||
if (details.minCollateralAmount > collateralAmount) { | ||
revert BelowMinCollateralAmount(); | ||
} | ||
|
||
uint256 maxDebt = collateralAmount.mulWad(details.collateralToDebtRatio); | ||
if (debtAmount > maxDebt) { | ||
revert DebtAmountExceedsDebtMax(maxDebt, debtAmount); | ||
} | ||
|
||
if (maxDebt == 0) { | ||
revert MaxDebtOrCollateralToDebtRatioZero(); | ||
} | ||
|
||
bytes memory caveatPricingData = details.loan.terms.pricingData; | ||
if (loanRate < AstariaV1Lib.getBasePricingRate(caveatPricingData)) { | ||
// Loan rate is less than the caveatDebt rate | ||
revert LoanRateLessThanCaveatRate(); | ||
} | ||
|
||
// Update the caveat loan rate | ||
AstariaV1Lib.setBasePricingRate(caveatPricingData, loanRate); | ||
Starport.Loan memory caveatLoan = details.loan; | ||
|
||
if (!details.matchIdentifier) { | ||
// Update the caveat loan identifier | ||
caveatLoan.collateral[0].identifier = loan.collateral[0].identifier; | ||
} | ||
|
||
// Update the caveat debt and collateral amounts | ||
caveatLoan.debt[0].amount = debtAmount; | ||
caveatLoan.collateral[0].amount = collateralAmount; | ||
|
||
// Hash and match w/ expected borrower and originator | ||
_validate(additionalTransfers, loan, caveatLoan); | ||
selector = CaveatEnforcer.validate.selector; | ||
} | ||
|
||
function _validate( | ||
AdditionalTransfer[] calldata additionalTransfers, | ||
Starport.Loan calldata loan, | ||
Starport.Loan memory caveatLoan | ||
) internal pure { | ||
caveatLoan.borrower = loan.borrower; | ||
caveatLoan.originator = loan.originator; | ||
|
||
if (keccak256(abi.encode(loan)) != keccak256(abi.encode(caveatLoan))) { | ||
revert InvalidLoanTerms(); | ||
} | ||
|
||
if (additionalTransfers.length > 0) { | ||
uint256 i = 0; | ||
for (; i < additionalTransfers.length;) { | ||
if (additionalTransfers[i].from == loan.issuer) revert InvalidAdditionalTransfer(); | ||
unchecked { | ||
++i; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.