> For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/searchers-limit-orders/overview.md).

# Overview

**Titan's Limit Orders are designed to thrive in a competitive environment where searchers play a central role.** By participating as a searcher, you gain access to a marketplace of on-chain limit orders.

Limit orders are resting on-chain and can be partially or completely filled. **Fees are charged to takers** and are charged as `output_mint` tokens.

**Program address:** `TitanLozLMhczcwrioEguG2aAmiATAPXdYpBg3DbeKK`

***

## Order structure

Each limit order is a PDA derived from the maker's public key, input mint, output mint, and an order ID.

```rust
use pinocchio::pubkey::{create_program_address, Pubkey};
use bytemuck::{Pod, Zeroable};

/// Limit order structure
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct LimitOrder {
    // The public key of the order,
    pub maker: Pubkey,
    // Input mint of the limit order
    pub input_mint: Pubkey,
    // Output mint of the limit order
    pub output_mint: Pubkey,
    // Slot which the order was created
    pub creation_slot: u64,
    // The slot at which the order expires
    pub expiration_slot: u64,
    // The amount of input tokens to be exchanged
    pub amount: u64,
    // The amount of input tokens that have been filled
    pub amount_filled: u64,
    // The amount of output tokens that have been exchanged.
    pub out_amount_filled: u64,
    // The amount of output tokens that the maker has withdrawn.
    pub out_amount_withdrawn: u64,
    // The amount of fees paid in the smallest unit of from_token mint.
    pub fees_paid: u64,
    // Price base in the order, in the smallest unit of output token
    pub price_base: u64,
    // Price exponent, price is calculated as price_base * 10^(-price_exponent)
    pub price_exponent: u8,
    // The status of the order
    pub status: u8,
    // Bump seed for the limit order PDA
    pub bump: u8,
    // Unique identifier for the order, used to differentiate orders for same
    // (owner, input_mint, output_mint) tuple
    pub id: u8,
    // Bump seed for the input mint vault PDA
    pub input_mint_vault_bump: u8,
    // Bump seed for the output mint vault PDA
    pub output_mint_vault_bump: u8,
    // Time in order
    pub time_in_force: u8,
    // Fees ticks rate for the order from takers.
    pub fee_ticks: u8,
}
```

**PDA seeds:** `["order", maker, input_mint, output_mint, id, bump]`

**Account size:** 168 bytes.

### PDA derivation

```rust
impl LimitOrder {
    pub const SEEDS: &'static [u8] = b"order";
    pub const LEN: usize = 168;

    /// Get the pda address for the limit order, given the maker, input mint,
    /// output mint, id and bump.
    pub fn get_pda_address(
        maker: &Pubkey,
        input_mint: &Pubkey,
        output_mint: &Pubkey,
        id: u8,
        bump: u8,
    ) -> Result<Pubkey, ProgramError> {
        let b0 = &[id];
        let b1 = &[bump];
        let seeds_with_bump = [
            LimitOrder::SEEDS,
            maker.as_ref(),
            input_mint.as_ref(),
            output_mint.as_ref(),
            b0,
            b1,
        ]
        .to_vec();

        create_program_address(&seeds_with_bump, &crate::ID)
    }
}
```

***

## Price calculation

Price is stored as `price_base * 10^(-price_exponent)`. For example, a 100 USDC → 1 SOL order uses `price_base = 1` and `price_exponent = 2`, giving a price of `0.01` output tokens per input token.

```rust
impl LimitOrder {
    /// Calculate the costs and fees for a given amount and fee ticks.
    pub fn calculate_costs_and_fee(
        &self,
        amount: u64,
        fee_ticks: u8,
    ) -> Result<(u64, u64), ProgramError> {
        let amount_u128 = amount as u128;

        let price_base = self.price_base as u128;
        let price_exponent = 10u128.pow(self.price_exponent as u32);

        let fee_units_u128 = (fee_ticks as u16).saturating_mul(FEE_TICK_UNITS as u16) as u128;

        // Calculate the transfer amount and fee amount.
        // Should never overflow, since its u64 * u64
        // Use method to perform ceiling math division: (a + b - 1) / b
        let cost_u128 = amount_u128
            .saturating_mul(price_base)
            .checked_add(price_exponent.saturating_sub(1))
            .ok_or(ProgramError::ArithmeticOverflow)?
            .saturating_div(price_exponent);
        let cost: u64 = cost_u128
            .try_into()
            .map_err(|_| ProgramError::ArithmeticOverflow)?;

        let fees: u64 = cost_u128
            .saturating_mul(fee_units_u128)
            .saturating_div(FEE_TICK_DIVISOR)
            .try_into()
            .map_err(|_| ProgramError::ArithmeticOverflow)?;

        Ok((cost, fees.max(1)))
    }

    /// Amount left to be filled in the order.
    pub fn get_remaining_amount(&self) -> u64 {
        self.amount.saturating_sub(self.amount_filled)
    }
}
```

***

## Fees

Fees are charged to takers in the **output token**. The fee rate is stored as `fee_ticks` on the order.

```
fee_units = fee_ticks × 25
fee = cost × fee_units / 1,000,000
```

**The minimum fee is always 1 unit of the output token.**

```rust
/// Fee tick units
pub const FEE_TICK_UNITS: u8 = 25;
/// 1e6 units = 0.0001, used to convert fee tick rate to fee basis points
pub const FEE_TICK_DIVISOR: u128 = 1_000_000;
```

**Fee receiver address:** `Bq5ZzfiU3vTiJPrBJFcr98BnUy9Wc1dg9ASeycB2tX1C`

***

## Time-in-force

Each order carries a time-in-force policy that controls fill behavior.

```rust
/// Time in Force (TIF) for limit orders. Provides different behaviors for
/// how long an order remains active and how it can be filled.
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TimeInForce {
    /// Order is good until cancelled. Partial takes are allowed.
    GoodTillCancelled = 0,
    /// After taking any amount, order is closed.
    TakeCancelsOrder = 1,
    /// Takes must completely fill the order.
    AllOrNothing = 2,
    /// Same as TakeCancelsOrder but it must be filled at the same time of creation.
    ImmediateOrCancel = 3,
    /// Same as AllOrNothing but it must be filled at the same time of creation.
    FillOrKill = 4,
}
```

* **`GoodTillCancelled` (0)** — Remains open until fully filled or cancelled. **Partial fills allowed.**
* **`TakeCancelsOrder` (1)** — Closes after any take, regardless of fill amount.
* **`AllOrNothing` (2)** — Takes must completely fill the remaining amount.
* **`ImmediateOrCancel` (3)** — Same as `TakeCancelsOrder`, but **must be filled in the same slot as creation.**
* **`FillOrKill` (4)** — Same as `AllOrNothing`, but **must be filled in the same slot as creation.**

***

## Order status

```rust
/// Order status for limit orders. Indicates the current state of the order
/// and how it can be interacted with.
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum OrderStatus {
    /// Order is open, can be partially filled, filled, cancelled
    Open = 0,
    /// Order is partially filled, can be filled or cancelled
    PartiallyFilled = 1,
    /// Order is filled, terminates, used for event logging
    Filled = 2,
    /// Order is cancelled, terminates, used for event logging
    Cancelled = 3,
}
```

* **`Open` (0)** — Can be partially filled, fully filled, or cancelled.
* **`PartiallyFilled` (1)** — Some amount filled. Can still be filled or cancelled.
* **`Filled` (2)** — Fully filled. **Terminal state.**
* **`Cancelled` (3)** — Cancelled by maker. **Terminal state.**

***

## Related pages

* [Placing Taker Orders](/titan/developer-doc/searchers-limit-orders/take-order.md) — TakeOrder instruction, accounts, WSOL handling, and full execution code
* [Limit Order Events](/titan/developer-doc/searchers-limit-orders/events.md) — Event structure and parsing from program logs
* [Error Codes](/titan/developer-doc/searchers-limit-orders/error-codes.md) — Program error codes
