-
Notifications
You must be signed in to change notification settings - Fork 174
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
feat(EMP contracts): Subsequent liquidations reset withdrawal timer #1859
Conversation
Signed-off-by: Christopher Maree <christopher.maree@gmail.com>
if ( | ||
positionToLiquidate.withdrawalRequestPassTimestamp > 0 && // this is a subsequent liquidation. | ||
positionToLiquidate.withdrawalRequestPassTimestamp < getCurrentTime() && // liveness has not passed yet. | ||
maxTokensToLiquidate.isGreaterThan(minSponsorTokens) // above the minimum threshold. |
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: I think we treat the threshold as a >= threshold rather than a > threshold (look at create()
so I think this should be isGreaterThanOrEqualTo
. But it doesn't really matter if it really makes the testing more difficult
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 agree that we should be consistent here.
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 have implemented this change. PTAL.
// then re-set the liveness. This enables a liquidation against a withdraw request to be "dragged out" if the position is very large. | ||
if ( | ||
positionToLiquidate.withdrawalRequestPassTimestamp > 0 && // this is a subsequent liquidation. | ||
positionToLiquidate.withdrawalRequestPassTimestamp < getCurrentTime() && // liveness has not passed yet. |
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.
This should be <= instead of < based on how withdraw()
handles comparising the request timestamp to getCurrentTime()
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.
Hmmmm, why would we only do this positions that have not yet passed the liveness? Do we see this being meaningfully helpful to UX? I don't think it necessarily matters that much -- it just seems like it makes the casework more complex and harder to explain, so I think we'd need to have a good reason to add this complexity over just doing it in all withdrawal-based liquidations.
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.
My main logic with this was if you had passed the liquidation liveness you should not be able to re-set this again. If this restriction was not added then a passed withdrawal request could have it's liveness re-set. This might actually be desired, in the case that the sponsor is slow to withdraw.
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.
This should be <= instead of < based on how withdraw() handles comparising the request timestamp to getCurrentTime()
Any response to this?
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.
This should be <= instead of < based on how withdraw() handles comparising the request timestamp to getCurrentTime()
Any response to this?
Yup. To be strictly consistent I agree with you.
); | ||
|
||
// Create a subsequent liquidation partial and check that it also advances the withdrawal request timer | ||
await liquidationContract.setCurrentTime(liquidationTime.add(liquidationLiveness.divn(2)).toString()); |
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.
Can you add in a couple tests here for:
- liquidations below
minSponsorTokens
should not advance the timer - once the withdraw request timetsamp has passed liveness, a liquidation above
minSponsorTokens
should not advance the timer
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.
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.
This is so much cleaner than the withdraw & GCR defense, realyl curious what @daywiss @kendricktan @mrice32 can think of in terms of adverse/unintended consequences
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.
Looks great! I agree with @nicholaspai that I think this could be a really clean solution to the whale issue. As discussed IRL, I think we probably want team feedback around this feature, especially from non-eng wrt how this might affect usability and ease of explanation for the withdraw request process.
if ( | ||
positionToLiquidate.withdrawalRequestPassTimestamp > 0 && // this is a subsequent liquidation. | ||
positionToLiquidate.withdrawalRequestPassTimestamp < getCurrentTime() && // liveness has not passed yet. | ||
maxTokensToLiquidate.isGreaterThan(minSponsorTokens) // above the minimum threshold. |
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 agree that we should be consistent here.
// then re-set the liveness. This enables a liquidation against a withdraw request to be "dragged out" if the position is very large. | ||
if ( | ||
positionToLiquidate.withdrawalRequestPassTimestamp > 0 && // this is a subsequent liquidation. | ||
positionToLiquidate.withdrawalRequestPassTimestamp < getCurrentTime() && // liveness has not passed yet. |
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.
Hmmmm, why would we only do this positions that have not yet passed the liveness? Do we see this being meaningfully helpful to UX? I don't think it necessarily matters that much -- it just seems like it makes the casework more complex and harder to explain, so I think we'd need to have a good reason to add this complexity over just doing it in all withdrawal-based liquidations.
so if i am understanding the intention behind this change, its that i can initiate a liquidation at a small price, essentially buying myself some more time to get more cash and send another liquidation, and so on, until the entire position can be liquidated? just as some general thoughts an not with the intention of changing implementation:
i will defer to others in eng on the technical implementation of this |
This would certainly be a factor if the contract were undercollateralized (i.e. every token is backed by < 100% of its value in collateral), but the contract being > 100% collateralized is a core assumption we make. In this weird state, sponsors would grief each other in order to "race" to get their collateral out of the contract.
Is the argument for a max time limit to protect sponsors from being griefed? The counter against a max time limit is that it sets a limit on the amount of time you can delay a malicious whale sponsor from withdrawing their collateral. However, we'd need to make sure to prevent the possibility that whales would split their positions amongst many smaller positions to take advantage of shortened extension times. Independent of that, scaling the time extensions by position size is really interesting and makes sense intuitively (larger positions should get longer time extensions). Finally, should we consider increasing the withdrawal liveness by the existing withdrawal liveness plus some time? This way, malicious whales get penalized more for invalid withdrawals. A simple implementation would set |
Signed-off-by: Christopher Maree <christopher.maree@gmail.com>
Signed-off-by: Christopher Maree <christopher.maree@gmail.com>
Signed-off-by: Christopher Maree <christopher.maree@gmail.com>
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.
This is awesome! Very nice and minimal change :).
Just wanted to note some things we discussed IRL:
- We decided that the liveness inequality makes sense. Timer should not reset if the liveness has already passed.
- We decided that the min sponsor threshold is slightly inelegant, but makes sense.
I think this is good to go once we add a test for the post-liveness withdraw that we decided we would keep.
Signed-off-by: Christopher Maree <christopher.maree@gmail.com>
What happens if the newly extended withdrawal pass timestamp is after expiration? Could repeated liquidation nibbles extend it indefinitely? |
Motivation
Issue: #1858
Summary
Very large positions are difficult to liquidate in the current EMP contracts as liquidators are required to pay off all sponsor debt when liquidating. This problem is particularly acute in the request withdrawal case.
This PR implements a change to the EMP contracts that re-sets the requested withdrawal liveness timer on every subsequent liquidation against a position.
Details
Each time a position has a liquidation submitted against the contracts check if there is a pending withdrawal request. If there is one, and it is not expired, then the sponsor's withdrawal request timestamp is re-set to the current time + the liquidation liveness.
Discussion
I don't check pending transfer requests. I think this is fine. Any thoughts on this point?