A shorter can keep a unhealthy short position open by minting an NFT of it and front-running attempts to liquidate it with a transfer of this NFT (which transfers the short position to the new owner)
A Short Record (SR) is a struct representing a short position that has been opened by a user.
It holds different informations, such as how much collateral is backing the short, and how much debt it owe (this ratio is called Collateral Ratio or CR)
At any time, any user can flag someone's else SR as "dangerous", if its debt grows too much compared to its collateral.
This operation is accessible through MarginCallPrimaryFacet::flagShort
, which check through the onlyValidShortRecord
modifier that the SR isn't Cancelled
If the SR is valid, then its debt/collateral ratio is verified, and if its below a specific threshold, flagged.
But that also means that if a SR is considered invalid, it cannot be flagged.
And it seems there is a way for the owner of a SR to cancel its SR while still holding the position.
The owner of a SR can mint an NFT to represent it and make it transferable.
This is done in 5 steps:
TransferFrom
verify usual stuff regarding the NFT (ownership, allowance, valid receiver...)
LibShortRecord::transferShortRecord
is called
transferShortRecord
verify that SR is not flagged
nor Cancelled
SR is deleted (setting its status to Cancelled
)
a new SR is created with same parameters, but owned by the receiver.
Now, let's see what would happen if Alice has a SR_1 with a bad CR, and Bob tries to flag it.
Bob calls flagShort
on SR_1, the tx is sent to the mempool
Alice is watching the mempool, and don't want her SR to be flagged:
She front-run Bob's tx with a transfer of her SR_1 to another of the addresses she controls
Now Bob's tx will be executed after Alice's tx:
The SR_1 is "deleted" and its status set to Cancelled
Bob's tx is executed, and flagShort
reverts because of the onlyValidShortRecord
Alice can do this trick again to keep here undercol SR until it can become dangerous
But this is not over:
Even when her CR drops dangerously (CR<1.5), liquidateSecondary
is also DoS'd as it has the same check for SR.Cancelled
Because of this, a shorter could maintain the dangerous position (or multiple dangerous positions), while putting the protocol at risk.
Add these tests to ERC721Facet.t.sol
:
Manual review
SR with a bad CR shouldn't be transferable, the user should first make its position healthy before being allowed to transfer it.
I suggest to add a check in LibShortRecord::transferShortRecord
to ensure `CR > primaryLiquidationCR
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.