Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
type: added
area: docs
pr: 641
breaking: false
---

Add documentation guides for trading and positions, concepts for risk and order types, and visual regression test coverage for the docs workspace.
18 changes: 18 additions & 0 deletions apps/docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,24 @@ This builds the content and starts the Nitro dev server. Open the printed URL,
find your page in the sidebar, and check it in both light and dark themes at a
mobile and a desktop width.

## Visual regression testing

Visual regression specs under `e2e/docs-visual.spec.ts` test the layout shell, kitchen-sink MDX fixture, sidebar, TOC, search dialog, and 404 page across light and dark themes on desktop and mobile viewports.

To run the visual suite:

```bash
bun run test:e2e -- docs-visual
```

To update baselines after deliberate UI token or chrome changes:

```bash
bun run test:e2e -- docs-visual --update-snapshots
```

Review the resulting diffs in `e2e/docs-visual.spec.ts-snapshots/` before committing.

## Sourcing claims

Every statement about protocol mechanics must be traceable to contract code,
Expand Down
113 changes: 113 additions & 0 deletions apps/docs/content/concepts/order-types.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
title: Order types
description: Execution mechanics, lifecycle states, and guarantee limits for market, limit, and trigger stop-loss orders in SO4.
updated: 2026-08-31
status: stable
---

SO4 supports market orders, limit orders, trigger orders (take-profit and stop-loss), and spot swaps. Every order is recorded on Stellar via Soroban smart contracts and executed by off-chain keepers when price and slippage conditions are satisfied.

The execution guarantee differs between order types: a market order prioritises immediate execution over price certainty, whereas a limit or trigger order guarantees a price condition but carries no guarantee of execution.

## Supported order types

The protocol distinguishes between order types based on position direction and execution triggers:

| Protocol Order Type | Interface Name | Purpose | Execution Trigger |
| --- | --- | --- | --- |
| `MarketIncrease` | Market Long / Short | Open or expand a leveraged position immediately | Executed in the next ledger at current mark price |
| `LimitIncrease` | Limit Long / Short | Open or expand a position at a better price | Executed when mark price reaches or improves the limit |
| `MarketDecrease` | Market Close | Reduce or fully close an existing position | Executed in the next ledger at current mark price |
| `LimitDecrease` | Take-Profit (TP) | Close a position at a profitable price threshold | Executed when mark price crosses the profit target |
| `StopLossDecrease` | Stop-Loss (SL) | Cut losses when market moves against a position | Executed when mark price drops/rises to the stop price |
| `MarketSwap` / `LimitSwap` | Spot Swap | Exchange long/short tokens without leverage | Executed immediately or at target swap limit |

## Execution mechanics and guarantees

### Market orders (`MarketIncrease` / `MarketDecrease`)

- **What it does**: Requests immediate execution against current pool liquidity.
- **When it executes**: Keepers pick up the transaction in the next confirmed Stellar ledger (typically 3 to 5 seconds).
- **What is not guaranteed**: The exact fill price is **not guaranteed**. The fill price equals the oracle mark price at the ledger when the keeper executes the transaction, plus price impact. To protect against adverse moves between submission and execution, orders enforce an `acceptablePrice` parameter calculated from your slippage tolerance.

### Limit orders (`LimitIncrease` / `LimitDecrease`)

- **What it does**: Sits in [DataStore](/reference/data-store) until market prices meet the limit condition. For a long limit buy, the mark price must be less than or equal to the limit price; for a short limit sell, the mark price must be greater than or equal to the limit price.
- **When it executes**: Keepers monitor oracle feeds and submit execution transactions when the limit threshold is crossed.
- **What is not guaranteed**: Execution is **not guaranteed**. If the market touches your limit price briefly without sufficient keeper throughput or immediately reverses, the order remains unfilled.

### Trigger orders (`StopLossDecrease` / `StopIncrease`)

- **What it does**: Sits dormant in `DataStore` until the oracle mark price crosses the specified trigger threshold, at which point it converts into a market order to close or increase the position.
- **Trigger price versus execution price**: The **trigger price** is the activation condition that signals keepers to execute the order. The **execution price** is the market price at the moment the keeper transaction confirms on Stellar. In volatile markets, the execution price is often worse than the trigger price.
- **When a stop does not protect a position**:
1. **Gap risk**: If an asset price gaps downward across blocks without intermediate trades (for example, falling from 60,000 USD to 54,000 USD), a stop set at 58,000 USD executes at 54,000 USD, not 58,000 USD.
2. **Liquidation precedence**: If market price drops so quickly that position equity falls below maintenance margin before the keeper executes the stop, the position undergoes [Liquidation](/concepts/liquidation). Liquidation takes precedence over resting stop orders.
3. **Slippage freeze**: If the execution price exceeds the order's `acceptablePrice` limit, the contract freezes the order rather than filling it, leaving the position open until manually cancelled or liquidated.

## Order lifecycle and states

Orders follow a deterministic lifecycle implemented across the smart contracts and tracked by the indexer:

```text
┌──────────────┐
│ CREATED │ ◀── Order written to DataStore & OrderVault
└──────┬───────┘
┌──────────────┐ Keeper fill succeeds
│ ACTIVE │ ───────────────────────────▶ ┌──────────────┐
└──────┬───────┘ │ EXECUTED │
│ └──────────────┘
│ Price violates acceptablePrice
┌──────────────┐ User cancels order
│ FROZEN │ ───────────────────────────▶ ┌──────────────┐
└──────────────┘ │ CANCELLED │
└──────────────┘
```

### State mapping

The order states match implementation in `apps/s03-indexer/src/mappings/mappingHandlers.ts` and `apps/web/src/features/trade/hooks/useOrdersWithIndexer.ts`:

- `CREATED` (`ord_crt` event): The order is validated by [ExchangeRouter](/reference/exchange-router), collateral is locked in [OrderVault](/reference/order-vault), and parameters are stored in [DataStore](/reference/data-store).
- `ACTIVE` / `UPDATED` (`ord_upd` event): The order is pending in the keeper queue awaiting trigger conditions.
- `FROZEN` (`ord_frz` event): A keeper attempted execution, but the transaction reverted (for example, mark price exceeded `acceptablePrice` slippage limit). Frozen orders require manual cancellation to return reserved collateral.
- `EXECUTED` (`ord_exe` event): A keeper successfully filled the order on-chain, updating the position balance and emitting a position event.
- `CANCELLED` (`ord_can` event): The user called `ExchangeRouter.cancel_order`, returning reserved collateral from `OrderVault` to the user's wallet.

## Slippage and acceptable price

Every order sent to `ExchangeRouter` specifies an `acceptablePrice` parameter (scaled to 30 decimals):

- **Long increase / Short decrease**: `acceptablePrice = markPrice × (1 + slippagePct)`
- **Short increase / Long decrease**: `acceptablePrice = markPrice × (1 - slippagePct)`

If the oracle price at keeper execution is worse than `acceptablePrice`, the contract rejects the fill. This prevents front-running and catastrophic execution during sudden oracle spikes.

## Contract mapping

Orders map directly to functions on the SO4 core contracts:

- **Create order**: Invokes `ExchangeRouter.create_order` (see [ExchangeRouter reference](/reference/exchange-router#create_order)) with `CreateOrderParams`.
- **Cancel order**: Invokes `ExchangeRouter.cancel_order(caller, order_key)`.
- **Collateral custody**: Collateral tokens are transferred directly from the user's address to the [OrderVault](/reference/order-vault) contract.

## Worked example

A trader holds a 10x Long on `BTC/USD` entered at 60,000.00 USD with 100 USDC collateral (position size = 1,000.00 USD, 0.016667 BTC).

The trader sets a Stop-Loss trigger order at 57,000.00 USD with 1.0% slippage tolerance:

- **Trigger price**: 57,000.00 USD.
- **Acceptable price**: `57,000.00 × (1 - 0.01) = 56,430.00 USD`.
- **Scenario A (Normal execution)**: BTC price trends downward and reaches 56,950.00 USD. The keeper triggers and executes the stop at 56,920.00 USD (within the 56,430.00 USD limit). The loss is `0.016667 × (60,000.00 - 56,920.00) = 51.33 USD`. The remaining collateral of 48.67 USD is returned to the trader's wallet.
- **Scenario B (Gap move)**: BTC price gaps from 57,500.00 USD directly to 56,000.00 USD on an external exchange shock. When the keeper attempts execution at 56,000.00 USD, the price is below the 56,430.00 USD acceptable price. The order freezes. The trader must cancel the frozen order and market-close or risk subsequent liquidation.

## Related

- [Trading guide](/guides/trading) — How to place market, limit, and stop orders in the interface.
- [Positions guide](/guides/positions) — Managing open orders, tracking status, and modifying margin.
- [Liquidation](/concepts/liquidation) — Liquidation mechanics and precedence over resting stops.
- [Risk](/concepts/risk) — Gap risk, oracle staleness, and contract execution considerations.
102 changes: 97 additions & 5 deletions apps/docs/content/concepts/risk.mdx
Original file line number Diff line number Diff line change
@@ -1,12 +1,104 @@
---
title: Risk
description: A concise overview of market, liquidation, oracle, contract, network, interface, and custody risks in SO4.
updated: 2026-08-24
description: Honest enumeration of what can go wrong: market volatility, contract admin roles, oracle failures, LP exposure, and interface outages.
updated: 2026-08-31
status: stable
---

Using SO4 can result in partial or total loss of collateral, including through mechanisms outside the interface's control.
Using SO4 can result in the complete loss of your deposited collateral. Leveraged positions amplify price volatility and can be liquidated rapidly when market prices move against you, while protocol dependencies on external oracles, Stellar network validators, and smart contracts introduce operational and systemic risks that no interface can eliminate.

A leveraged position can cross its <Term id="maintenance-margin">maintenance margin</Term> as prices move and be closed near its <Term id="liquidation-price">liquidation price</Term>. Other risks include volatile prices, adverse price impact, funding, unavailable liquidity, stale or incorrect oracle data, smart-contract defects, Stellar or RPC disruption, wallet compromise, and interface outages.
This page provides an unhedged enumeration of the risks inherent in using SO4 as a trader or liquidity provider.

The interface prepares transactions; your wallet authorises them and the protocol executes them. Review the [terms and plain-language disclosure](/resources/terms) before using the interface.
## Audit status

**The SO4 smart contracts have not been audited by an independent security firm.** No external audit has been completed, and current deployments on Stellar are in testnet release phases.

If you interact with the contracts, you are using unaudited software. You should never deposit assets you cannot afford to lose. For disclosure instructions and security contacts, review [Security](/resources/security).

## Market and trading risks

### Leverage and liquidation

Leverage multiplies both potential gains and losses. When you deposit collateral to open a leveraged position, even small adverse price movements consume your margin equity:

- If your position equity drops below the maintenance margin requirement (typically 1.0% of position size), off-chain keepers or liquidators trigger a liquidation (see [Liquidation](/concepts/liquidation)).
- In a liquidation, your remaining position collateral is seized to cover pool losses and pay liquidation fees.

### Gap moves and slippage

During periods of extreme market volatility or thin liquidity, prices can gap abruptly from one price level to another without executing intermediate trades:

- A stop-loss or trigger order does not guarantee execution at your exact trigger price. If the market gaps through your stop level, the keeper executes at the next available oracle price, resulting in greater slippage than anticipated (see [Order types](/concepts/order-types)).
- If market prices gap past your liquidation price before a stop-loss executes, the position liquidates immediately.

### Funding costs over time

Perpetual markets use recurring funding fees to balance long and short open interest:

- If you hold a position on the majority side of an unbalanced market (for example, holding long when open interest is predominantly long), you continuously pay funding fees to the minority side (see [Funding and fees](/concepts/funding-and-fees)).
- Funding fees accrue on every settlement epoch and reduce your collateral equity over time, which can pull your liquidation price closer even if the spot market price does not change.

## Protocol and contract risks

### Smart contract bugs

SO4's core logic runs across Soroban smart contracts on Stellar, including [ExchangeRouter](/reference/exchange-router), [OrderVault](/reference/order-vault), [DataStore](/reference/data-store), and [SyntheticsReader](/reference/synthetics-reader):

- Unforeseen defects, arithmetic precision errors, or logic oversights in contract code can lead to stuck collateral, failed order execution, or unintended token transfers.
- Soroban execution environments enforce strict CPU and memory resource limits; transactions exceeding footprint or resource budgets revert on-chain.

### Admin capabilities and upgrade authority

The protocol architecture separates parameter control and contract execution:

- **DataStore roles**: Parameter changes (including base fee rates, maximum leverage caps, liquidation execution fees, and market enable flags) are restricted to addresses holding the `CONTROLLER` role in [DataStore](/reference/data-store).
- **Upgrade authority**: Soroban contracts are upgradeable by updating their executable WASM bytecode hashes. The contract admin keys hold the authority to execute contract upgrades or modify protocol role assignments.
- **Custody boundary**: Collateral deposited for active orders is locked in `OrderVault`. While `OrderVault` restricts withdrawals to validated contract operations, administrative authority over protocol routing contracts represents a centralized governance trust assumption.

## Oracle risks

SO4 relies on price oracles to value positions, determine liquidation eligibility, and validate order execution prices (see [Oracles](/concepts/oracles)):

1. **Source fallback**: Prices are fetched in priority order from the SO4 oracle proxy, Pyth Hermes, Binance REST, and GMX v2 feeds.
2. **Staleness**: If all networked oracle endpoints fail, the client falls back to static dummy prices, and keepers cannot settle trades on-chain.
3. **Price manipulation**: If an attacker manipulates the primary price source on an external venue, artificial price spikes could trigger unwarranted liquidations before normal market parity restores.

## Liquidity provider risks

Depositing assets into SO4 GM liquidity pools carries distinct risk factors:

- **Counterparty to trader PnL**: Liquidity providers act as the direct counterparty to platform traders. When traders make net profits, pool collateral decreases to pay those gains (see [Pools guide](/guides/pools)).
- **Asset exposure**: GM pool tokens represent a blended basket of index and collateral assets. Deposition subjects liquidity providers to underlying asset price declines.
- **Two-step withdrawal queue**: Liquidity withdrawals require a two-step process: submitting a withdrawal request followed by keeper execution on Stellar. You cannot exit instantly during extreme congestion.

## Operational and infrastructure risks

### RPC and indexer availability

- The interface relies on Stellar Horizon and Soroban RPC nodes to simulate transactions and broadcast signed envelopes. RPC outages prevent order submission or balance refreshing.
- If the SubQuery indexer (`apps/s03-indexer`) becomes unavailable, historical order and trade charts pause, although live contract data can still be read through RPC calls.

### What to do if the interface is down

The web application at `so4.market` is an open-source client for the underlying Soroban contracts:

- If the hosted website goes down, **your open positions and collateral remain safely recorded on the Stellar ledger.**
- **Recovery path**: Because SO4 contracts are fully permissionless on-chain, you can interact with them directly without the web UI:
1. Run the interface locally by cloning the public repository and executing `bun run --cwd apps/web dev`.
2. Use the Stellar CLI or Soroban SDK (`packages/contracts`) to invoke [ExchangeRouter](/reference/exchange-router) functions directly from your terminal (`create_order` to close positions, `cancel_order` to withdraw unexecuted collateral, or `claim_funding_fees` to extract accrued fees).

## Worked example

A trader opens a 10x leveraged Long position on `BTC/USD` with 100 USDC collateral at an entry price of 60,000 USD (position size = 1,000 USD):

- **Normal liquidation**: With a 1.0% maintenance margin ($10 minimum equity), liquidation occurs if BTC drops 9.0% to 54,600 USD.
- **Gap risk scenario**: A sudden market shock causes the oracle price to gap directly from 56,000 USD to 52,000 USD (a 13.33% drop) without intermediate quotes.
- **Loss outcome**: The position equity is completely wiped out (-$133.33 loss exceeds the $100.00 collateral). The position liquidates immediately at the gap price, leaving 0 USDC remaining.

## Related

- [Liquidation](/concepts/liquidation) — Detailed maintenance margin mathematics and liquidation process.
- [Funding and fees](/concepts/funding-and-fees) — Fee calculation formulas and funding rate mechanics.
- [Oracles](/concepts/oracles) — The multi-tier oracle hierarchy and staleness handling.
- [Security](/resources/security) — Vulnerability reporting policies and audit disclosures.
- [Terms of use](/resources/terms) — Legal terms and risk disclosures.
Loading