# Riskless trades due to delay check

Tigris Trading: trader renews limitClose delay via 1-wei addToPosition and updates stop-loss without delay check, enabling riskless trades.

- Payout: Contest reward
- Severity: high
- Program: Tigris Trade
- Credited: Bobface
- Disclosed: 2023-02-17
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2022-12-tigris-findings/issues/67
- Library page: https://bugbountylibrary.com/r/c4%3A2022-12-tigris%3AH-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Trading.sol limit order / stop-loss / delay check
**Root cause:** updateTpSl() lacks _checkDelay() so stop-loss can be raised freely, while addToPosition() with 1 wei resets the block delay, indefinitely blocking limitClose() and liquidation.
**Steps:**
1. Open long limit order at market price 1000 DAI with stop-loss at 1000 DAI via `initiateLimitOrder()` then `executeLimitOrder()`.
2. Before block delay expires, call `addToPosition(1, "1", priceData, sig, ...)` with 1 wei to renew the delay.
3. Repeat addToPosition each time delay nears expiry; `limitClose()` reverts with checkDelay error `"0"`.
4. If price rises, call `updateTpSl(false, 1, newPrice, ...)` to lock in higher stop-loss (no delay check).
5. When satisfied, stop renewing delay, let it expire, call `limitClose()` at peak stop-loss; withdraw via `stablevault.withdraw()`.
**How found:** Code reading: noticed updateTpSl() omits _checkDelay() while limitClose() enforces it, and that addToPosition() resets the delay counter, combining into a griefing/riskless-trade primitive.
**Exploitation:** Trader holds position open indefinitely at zero risk: downside protected by original stop-loss (close break-even once delay allowed), upside captured by ratcheting stop-loss upward via updateTpSl. Cost of delay renewal is 1 wei per call.
**Escalation:** Repeated across positions and price cycles to extract unlimited risk-free profit from the protocol's liquidity providers.
**Insight:** When a delay/cooldown gates one action (close) but not state-mutating actions (stop-loss update) and can be reset cheaply by a dust-sized call, the gate is bypassable; check every state-changing entrypoint for the same guard.
