-
Notifications
You must be signed in to change notification settings - Fork 4
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
Riskless trades due to delay check #67
Comments
GalloDaSballo marked the issue as primary issue |
Primary because of POC |
GainsGoblin marked the issue as sponsor confirmed |
The warden has shown how, through the combination of: finding a way to re-trigger the delayCheck, altering SL and TP prices, a trader can prevent their position from being closed, creating the opportunity for riskless trades. Because of the broken invariants, and the value extraction shown, I agree with High Severity |
GalloDaSballo marked the issue as selected for report |
Hey @GalloDaSballo and sponsor, thanks for reviewing my submission and accepting it! Unfortunately, I believe there to be a potential issue with the linked submission (#48 ). I'm sorry for sending this message so late in the process, I just got backstage access today. Essentially, I believe the linked issue to not be related to this issue. In short, my submission describes abusing the system to create something similar to a trailing order, where during upwards price movement, the positive value is captured by also moving up the stop-loss of the position, while during downwards price movement, the position cannot be liquidated even if falling below the previously set stop-loss, by resetting the The linked issue, if I understand the provided description correctly, describes a process where malicious values are injected into the contract to In summary, I believe the two issues to be unrelated and #48 not being a duplicate of this issue. Looking forward to hear your feedback! |
@Bobface Thank you for the flag, please check the updated Dup Classification |
@GalloDaSballo thanks for the swift resolution, looks good to me! |
Mitigation: code-423n4/2022-12-tigris#2 (comment) |
Lines of code
https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/Trading.sol#L573
Vulnerability details
Riskless trades due to delay check
Summary
Trading.limitClose()
uses_checkDelay()
. This allows for riskless trades, by capturing price rises through increasing the stop-loss, while preventing the underwater position to be closed in case of the price dropping by continuously increasing the delay.Detailed description
A malicious trader can exploit the
Trading
contract to achieve riskless trades. In the worst-case scenario, the trader can always close the trade break-even, while in a good scenario the trader captures all upside price movement.The exploit is based on three principles:
_checkDelay()
not being called inupdateTpSl()
limitClose
calling_checkDelay()
Based on these three principles, the following method can be used to perform riskless trades:
Assuming a current market price of 1,000 DAI, begin by opening a long limit order through
initiateLimitOrder()
at the current market price of 1,000 DAI and stop-loss at the exact market price of 1,000 DAI. Then immediately execute the limit order throughexecuteLimitOrder
.After the block delay has passed, MEV bots or other third parties interested in receiving a percentage reward for closing the order would call
limitClose
. However, we can prevent them from doing so by continuously callingaddToPosition
with 1 wei when the block delay comes close to running out [1], which will renew the delay and thus stopslimitClose
from being called.While the trader keeps renewing the delay to stop his position from being closed, he watches the price development:
liquidatePosition()
. If the price comes close to the liquidation zone, he stops renewing the delay and closes the position break-even for the initial stop-loss price even though the price is down significantly further. He can also choose to do that at any other point in time if he decides the price is unlikely to move upward again.updateTpSl()
to lock in the increased price. For example, if the price moves from 1,000 DAI to 2,000 DAI, he callsupdateTpSl()
with 2,000 DAI as stop-loss. Even if the price drops below 2,000 DAI again, the stop-loss is stored. This function can be called while the delay is still in place because there is no call to_checkDelay()
.The trader keeps calling
updateTpSl()
when the price reaches a new high since he opened the position initially to capture all upside movement. When he decides that the price has moved high enough, he finally lets the delay run out and callslimitClose()
to close the order at the peak stop-loss.Notes
[1]: Tigris Trade also plans to use L2s such as Arbitrum where there is one block per transaction. This could bring up the false impression that the trader would have to make lots of calls to
addToPosition
after every few transactions on the chain. However,block.number
, which is used by the contract, actually returns the L1 block number and not the L2 block number.Recommended mitigation
The core issue is that the position cannot be closed even if it is below the stop-loss due to constantly renewing the delay. The delay checking in
limitClose()
should be modified to also consider whether the position is below the stop-loss.PoC
Insert the following code as test into
test/07.Trading.js
and run it withnpx hardhat test test/07.Trading.js
:The text was updated successfully, but these errors were encountered: