# Limit Orders

Place an order that opens a position when the market reaches your price. Collateral is locked when
the order is placed, and the order is executed by a keeper when the trigger condition is met.

## `openLimitOrderV2`

```solidity
function openLimitOrderV2(
    OpenDataInput memory data,
    OracleUpdateData calldata updateData,
    uint96 extraFee
) external payable returns (bytes32 orderHash);
```

Same [`OpenDataInput`](/onchain/market-orders#opendatainput) struct as a market order, with one
difference:

| Field | Market order | Limit order |
| :--- | :--- | :--- |
| `price` | Worst acceptable fill price | **The limit price** |

Everything else — `qty` at 1e10, `tokenIn`/`lvToken` pairing, `amountIn` covering margin plus fee —
behaves identically. `broker` and `extraFee` work exactly as they do for market orders; see
[Brokers & Referrals](/introduction/brokers).

Oracle data is required at placement time even though the order does not fill immediately; it is used
to validate the order against the current market.

## Example

```ts
import { parseUnits } from 'viem'
import { publicClient, walletClient, DIAMOND, USDC, LVUSD } from './config'
import { fetchOracleUpdate } from './oracle'

const OPEN_LIMIT_ABI = [{
  type: 'function',
  name: 'openLimitOrderV2',
  stateMutability: 'payable',
  inputs: [
    {
      name: 'data', type: 'tuple', components: [
        { name: 'pairBase', type: 'address' },
        { name: 'isLong', type: 'bool' },
        { name: 'tokenIn', type: 'address' },
        { name: 'lvToken', type: 'address' },
        { name: 'amountIn', type: 'uint96' },
        { name: 'qty', type: 'uint128' },
        { name: 'price', type: 'uint128' },
        { name: 'stopLoss', type: 'uint128' },
        { name: 'takeProfit', type: 'uint128' },
        { name: 'broker', type: 'uint24' },
      ],
    },
    {
      name: 'updateData', type: 'tuple', components: [
        { name: 'pythPriceUpdateData', type: 'bytes[]' },
        { name: 'pythProPriceUpdateData', type: 'bytes[]' },
      ],
    },
    { name: 'extraFee', type: 'uint96' },
  ],
  outputs: [{ name: 'orderHash', type: 'bytes32' }],
}] as const

// Buy $100 of BTC if it drops to $95,000, with a stop at $90,000.
const limitPrice = 95_000
const oracle = await fetchOracleUpdate(pairBase, USDC)

const hash = await walletClient.writeContract({
  address: DIAMOND,
  abi: OPEN_LIMIT_ABI,
  functionName: 'openLimitOrderV2',
  args: [
    {
      pairBase,
      isLong: true,
      tokenIn: USDC,
      lvToken: LVUSD,
      amountIn: parseUnits('10', 6),
      qty: parseUnits((100 / limitPrice).toFixed(10), 10),
      price: parseUnits(String(limitPrice), 18),
      stopLoss: parseUnits('90000', 18),
      takeProfit: 0n,
      broker: 0,
    },
    oracle.updateData,
    0n,
  ],
  value: oracle.value,
})

await publicClient.waitForTransactionReceipt({ hash })
```

The `orderHash` is emitted in the `OpenLimitOrder` event and returned by
[`getLimitOrders`](/onchain/reading-data#getlimitorders).

## Updating TP/SL

Adjust the take-profit and stop-loss that will be applied when the order fills. No oracle data
needed.

```solidity
function updateOrderTp(bytes32 orderHash, uint128 takeProfit) external;
function updateOrderSl(bytes32 orderHash, uint128 stopLoss) external;
function updateOrderTpAndSl(bytes32 orderHash, uint128 takeProfit, uint128 stopLoss) external;
```

```ts
const UPDATE_ORDER_ABI = [{
  type: 'function',
  name: 'updateOrderTpAndSl',
  stateMutability: 'nonpayable',
  inputs: [
    { name: 'orderHash', type: 'bytes32' },
    { name: 'takeProfit', type: 'uint128' },
    { name: 'stopLoss', type: 'uint128' },
  ],
  outputs: [],
}] as const

await walletClient.writeContract({
  address: DIAMOND,
  abi: UPDATE_ORDER_ABI,
  functionName: 'updateOrderTpAndSl',
  args: [orderHash, parseUnits('120000', 18), parseUnits('90000', 18)],
})
```

Pass `0` to clear a stop-loss.

## Cancelling

```solidity
function cancelLimitOrder(bytes32 orderHash) external;
function batchCancelLimitOrders(bytes32[] calldata orderHashes) external;
```

Cancelling returns the locked collateral. No oracle data needed.

```ts
const CANCEL_ABI = [
  {
    type: 'function',
    name: 'cancelLimitOrder',
    stateMutability: 'nonpayable',
    inputs: [{ name: 'orderHash', type: 'bytes32' }],
    outputs: [],
  },
  {
    type: 'function',
    name: 'batchCancelLimitOrders',
    stateMutability: 'nonpayable',
    inputs: [{ name: 'orderHashs', type: 'bytes32[]' }],
    outputs: [],
  },
] as const

await walletClient.writeContract({
  address: DIAMOND,
  abi: CANCEL_ABI,
  functionName: 'batchCancelLimitOrders',
  args: [[orderHashA, orderHashB]],
})
```

## Order lifecycle

| Event | Meaning |
| :--- | :--- |
| `OpenLimitOrder` | Order placed, collateral locked |
| `ExecuteLimitOrderSuccessful` | Trigger hit, position opened |
| `ExecuteLimitOrderRejected` | Trigger hit but the open failed validation; order stays or is refunded |
| `LimitOrderRefund` | Collateral returned |
| `CancelLimitOrder` | Cancelled by the trader |
| `UpdateOrderTp` / `UpdateOrderSl` | TP/SL changed |

See [Events](/onchain/events#limit-orders) for signatures, and
[Reading Data](/onchain/reading-data#getlimitorders) for querying live orders.
