-
Notifications
You must be signed in to change notification settings - Fork 10
escrow offer: duration => max duration #153
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
base: develop
Are you sure you want to change the base?
Conversation
…ated event to include maxDuration.
src/EscrowSupplierNFT.sol
Outdated
| // The offer is reduced (which is used to repay the previous supplier) | ||
| // A new escrow ID is minted. | ||
| newEscrowId = _startEscrow(offerId, previousEscrow.escrowed, newFees, newLoanId); | ||
| newEscrowId = _startEscrow(offerId, previousEscrow.escrowed, newFees, newLoanId, previousEscrow.duration); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not confident we should reuse the old duration here. but not sure how else to handle it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think switchEscrow should take a newDuration argument for the new position being created. It's used during a roll, and if provider durations aren't stable anymore, it makes sense that a new roll implementation will also be needed, and it will create a position with some newDuration.
In Loans, the newDuration can be loaded from the taker position using _takerId(newLoadId) after the roll.
If we add this now, it would work with current rolls, and with new rolls when it is needed, without having to redeploy escrows twice and migrate their users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A great first draft! Noted a few issues and suggestions for changes.
Additional notes for further work on this:
- version strings need to be updated for the touched contracts
- changelog entries
- audits briefing (for any known issues)
- branch unit test coverage back to 100% if is reduced by the end
- deploy script changes
- fork test changes
src/EscrowSupplierNFT.sol
Outdated
| return nextTokenId; | ||
| } | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: forge fmt
src/EscrowSupplierNFT.sol
Outdated
| uint lateFeeAPR, | ||
| uint minEscrow | ||
| uint minEscrow, | ||
| uint maxDuration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
info: better to keep the same order everywhere for easier visual comparison (types used, nothing skipped, etc) between inputs and assignments later.
src/EscrowSupplierNFT.sol
Outdated
| * @param offerId The ID of the offer to update | ||
| * @param newMaxDuration The new max duration in seconds | ||
| */ | ||
| function updateOfferMaxDuration(uint offerId, uint newMaxDuration) external { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
low: currently almost all stored values are immutable in all contracts, with the exception of offered amounts. Making the maxDuration modifiable adds another exception to this "values are immutable once set" rule, making it more difficult to ensure and review safe use of loaded / derived values. An example of this is the med issue with not validating duration during switchEscrow below (if it wasn't mutable, it would be low).
This also creates a slight (mostly negligible) race condition (frontrunning risk) between updating maxDuration and taking offers. It already exists for offer amounts, but is unavoidable.
It's better that if different max duration is needed, a new offer is created instead. I don't think there are any drawbacks to this (other than requiring two transactions instead of one, can be batched, is an edge case need anyway). Less code -> less mutability, less bugs, cheaper audits, less tests to write.
Additionally, this mutability creates a need to index another event and add correct handling of offer mutability in more places (backed, fronted, integrations)
src/EscrowSupplierNFT.sol
Outdated
| // The offer is reduced (which is used to repay the previous supplier) | ||
| // A new escrow ID is minted. | ||
| newEscrowId = _startEscrow(offerId, previousEscrow.escrowed, newFees, newLoanId); | ||
| newEscrowId = _startEscrow(offerId, previousEscrow.escrowed, newFees, newLoanId, previousEscrow.duration); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think switchEscrow should take a newDuration argument for the new position being created. It's used during a roll, and if provider durations aren't stable anymore, it makes sense that a new roll implementation will also be needed, and it will create a position with some newDuration.
In Loans, the newDuration can be loaded from the taker position using _takerId(newLoadId) after the roll.
If we add this now, it would work with current rolls, and with new rolls when it is needed, without having to redeploy escrows twice and migrate their users.
src/EscrowSupplierNFT.sol
Outdated
| { | ||
| // Check if duration is within the offer's max duration | ||
| Offer memory offer = getOffer(offerId); | ||
| require(duration <= offer.maxDuration, "escrow: duration exceeds offer's max duration"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
med: this check needs to be in the validations in _startEscrow() instead. Currently it's not checked during switchEscrow. In this version of the code (with the ability to udpate maxDuration) it means a roll can ignore a new maxDuration value. If modifying duration is removed, in a future version of the code - if switchEscrow takes a newDuration as recommended (instead of using previous value) - it means that switchEscrow won't check the newDuration vs. maxDuration.
src/LoansNFT.sol
Outdated
| // ----- Conditional escrow mutative methods ----- // | ||
|
|
||
| function _conditionalOpenEscrow(bool usesEscrow, uint escrowed, EscrowOffer memory offer, uint fees) | ||
| function _conditionalOpenEscrow(bool usesEscrow, uint escrowed, EscrowOffer memory offer, uint fees, ProviderOffer memory providerOffer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: forge fmt
| uint escrowed; | ||
| uint feesHeld; | ||
| uint withdrawable; | ||
| uint32 duration; // duration for this escrow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
info: can fit into the first slot (after bool realeased), saving gas.
src/EscrowSupplierNFT.sol
Outdated
| * @param gracePeriod The maximum grace period duration in seconds | ||
| * @param lateFeeAPR The annual late fee rate in basis points | ||
| * @param minEscrow The minimum escrow amount. Protection from dust mints. | ||
| * @param maxDuration The duration in seconds for the escrow (calculated from loan expiration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
info: natspec outdated
… duration checks to _startEscrow so its used by rolls as well. remove mutation of maxDuration. get newDuration in switchEscrow from taker position.
high level problem:
offers in general are not working as well as we hoped they would.
approach:
This change focuses on helping make escrow offers more flexible to support the uniqueness of the mm offers that we see happening.
note:
think of this PR as a conversation starter and assume there are plenty of discussions, reviews and code changes most likely needed in order to complete this effort (should we all agree on the direction).