|
| 1 | +# QLX Invoice Lock Time Limits |
| 2 | + |
| 3 | +> Audience: contributors who need the practical answer to “how long can an invoice remain locked before it is auto-released?” |
| 4 | +
|
| 5 | +The short version is: there is no single invoice lock timer that always auto-releases funds. QuickLendX has two lock styles, and both behave differently: |
| 6 | + |
| 7 | +- **Admin freeze**: a permanent lock that does not expire on its own. |
| 8 | +- **Escrow hold after funding**: a lock that remains until an explicit release, refund, or withdrawal action. |
| 9 | +- **Default eligibility**: the closest thing to a time-based release window, but it still requires an explicit trigger. |
| 10 | + |
| 11 | +## 1. Admin freeze: effectively indefinite |
| 12 | + |
| 13 | +An admin can freeze an invoice with the `freeze_invoice` entrypoint. Once set, the invoice is blocked for financial writes and is treated as locked until a separate on-chain intervention changes that state. |
| 14 | + |
| 15 | +### Concrete behaviour |
| 16 | + |
| 17 | +- Entry point: `freeze_invoice(env, admin, invoice_id)` |
| 18 | +- Effect: the invoice gets the `InvoiceFrozen` flag. |
| 19 | +- Result: operations such as `place_bid`, `accept_bid`, `record_payment`, and `settle_invoice` fail with `InvoiceFrozen`. |
| 20 | + |
| 21 | +There is no public `unfreeze_invoice` entrypoint in the contract interface. The underlying storage helper exists, but the public contract surface does not expose a time-based auto-release for this state. |
| 22 | + |
| 23 | +### Practical takeaway |
| 24 | + |
| 25 | +If you are tracing a lock that came from admin freeze, treat it as **indefinite until manual intervention** rather than as a countdown-based lock. |
| 26 | + |
| 27 | +## 2. Escrow hold: no auto-release timer |
| 28 | + |
| 29 | +When `accept_bid_and_fund` succeeds, the invoice moves into a funded/escrow-held state. The investor’s funds stay in escrow and the invoice remains effectively locked from a funds-release perspective until one of the following is called explicitly: |
| 30 | + |
| 31 | +- `release_escrow_funds` |
| 32 | +- `refund_escrow_funds` |
| 33 | +- `withdraw_investment` |
| 34 | + |
| 35 | +### Concrete behaviour |
| 36 | + |
| 37 | +- The escrow status becomes `Held` after funding. |
| 38 | +- The funds remain locked until one of the explicit terminal actions above is invoked. |
| 39 | +- There is no built-in expiry window for this hold. |
| 40 | + |
| 41 | +### Practical takeaway |
| 42 | + |
| 43 | +If you are debugging an invoice that is still “locked” after funding, the answer is usually “it is waiting for an explicit release or refund path,” not “it will expire automatically after $N$ seconds.” |
| 44 | + |
| 45 | +## 3. Default path: the only bounded time window |
| 46 | + |
| 47 | +The closest thing to a time-based lock release is the default path. Once the invoice reaches its due date and then passes the configured grace period, the invoice becomes eligible for default handling. |
| 48 | + |
| 49 | +### How the timer is resolved |
| 50 | + |
| 51 | +The grace deadline is resolved in this order: |
| 52 | + |
| 53 | +1. An explicit override passed to `mark_invoice_defaulted` |
| 54 | +2. The configured protocol grace period (`grace_period_seconds`) |
| 55 | +3. The default fallback of `7 days` (`604_800` seconds) |
| 56 | + |
| 57 | +The implementation also bounds the maximum grace period at `30 days` (`2_592_000` seconds). |
| 58 | + |
| 59 | +### Concrete example |
| 60 | + |
| 61 | +Suppose an invoice is due on `2026-07-15` and the protocol uses the default grace period of `7 days`. |
| 62 | + |
| 63 | +| Invoice due date | Grace period | Default becomes eligible after | |
| 64 | +|------------------|--------------|-------------------------------| |
| 65 | +| `2026-07-15` | `7 days` | `2026-07-22` | |
| 66 | + |
| 67 | +At that point, anyone can call the default entrypoint to transition the invoice to a defaulted state and refund the escrow. This is still a **manual trigger**, not an automatic on-chain release. |
| 68 | + |
| 69 | +## 4. Contributor guidance |
| 70 | + |
| 71 | +When you are implementing or reviewing invoice-lock logic, use this rule of thumb: |
| 72 | + |
| 73 | +- If the lock came from an admin freeze, assume it is **indefinite**. |
| 74 | +- If the lock came from funded escrow, assume it is **waiting for an explicit release/refund/withdraw action**. |
| 75 | +- If the invoice is past due, check the **grace period + default path** rather than looking for a general auto-release timer. |
| 76 | + |
| 77 | +## Related docs |
| 78 | + |
| 79 | +- [INVOICE_LOCK.md](INVOICE_LOCK.md) for the broader invoice-lock overview |
| 80 | +- [INVOICE_LIFECYCLE.md](INVOICE_LIFECYCLE.md) for the invoice state machine |
| 81 | +- [ESCROW.md](ESCROW.md) for the escrow lifecycle and terminal actions |
| 82 | +- [ERROR_CODES.md](ERROR_CODES.md) for the `InvoiceFrozen` error reference |
0 commit comments