> 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/take-order.md).

# Placing Taker Orders

**Searchers fill orders by invoking the `TakeOrder` instruction (discriminator `2`).** Orders can be partially or fully fulfilled — in both cases the taker receives their tokens immediately.

***

## Fill behavior

### Full fill

When an order is fully filled, the program automatically:

1. **Creates the maker's ATA** for the output tokens (if needed).
2. **Refunds the original rent** used in the Limit Order back to the maker.
3. **Reimburses any lamports** back to the taker if they had to pay for any ATA creation.
4. **For WSOL output** — returns funds back to the maker as SOL instead of WSOL.

### Partial fill

For partial fills, the taker receives their tokens immediately. The remaining input tokens stay in the program vault. **The maker can withdraw filled output tokens at any time.**

### Example: 100 USDC → 1 SOL with 5 BPS fee

1. User creates a limit order paying the rent and depositing 100 USDC into the vault. Price is set to `0.01`.
2. Adjusting for fees — when it is favourable to trade 1.0005 SOL → 100 USDC, the taker comes in and takes the full order.
3. Under the hood, taker deposits 1.0005 SOL into the vault. 100 USDC is moved from the vault to the taker's ATA, 0.0005 WSOL fee is moved to the fee receiver's wallet.
4. Contract determines the limit order is fulfilled. Since the output is SOL, the special WSOL edge case is handled:
   * The contract expects a **taker-owned WSOL (non-ATA) token account** is passed into the call.
   * The WSOL vault sends 1 SOL to this token account and **closes it out to the maker**, crediting their wallet balance with the 1 SOL.
   * The limit order is closed and rent is sent to the taker.
   * The taker sends the rent funds to the maker subtracting any rent they paid for the WSOL token account.

{% hint style="info" %}
**For partial fills**, the above example holds — just skip step 4. **For non-WSOL trades**, only step 4 differs: the program initializes the maker's ATA with the taker as rent payer. When the limit order closes, the taker is rebated accordingly.
{% endhint %}

***

## WSOL handling

When the output mint is WSOL and the order will close:

* The taker **must pass a seeded (non-ATA) WSOL token account** as the maker's output account.
* The program sends SOL to this account and **closes it to the maker**, crediting their wallet balance directly.
* The taker wraps SOL into their ATA before the take, and closes the ATA after.

***

## TakeOrder accounts

* **`0` — `taker`** — Taker wallet. **Writable, signer.**
* **`1` — `maker`** — Maker wallet (receives output tokens on full fill). **Writable.**
* **`2` — `inputMint`** — Input token mint. Read-only.
* **`3` — `outputMint`** — Output token mint. Read-only.
* **`4` — `limitOrder`** — Limit order PDA. **Writable.**
* **`5` — `takerInputMintTokenAccount`** — Taker's input token account (receives input tokens). **Writable.**
* **`6` — `takerOutputMintTokenAccount`** — Taker's output token account (sends output tokens + fees). **Writable.**
* **`7` — `makerOutputMintTokenAccount`** — Maker's output token account (or seeded account for WSOL). **Writable.**
* **`8` — `makerInputMintTokenAccount`** — Maker's input token account (for remaining balance on close). **Writable.**
* **`9` — `feeReceiverTokenAccount`** — Fee receiver's output token account. **Writable.**
* **`10` — `vaultManager`** — Vault manager PDA (`["vault_manager"]`). Read-only.
* **`11` — `inputMintVault`** — Vault's input token account. **Writable.**
* **`12` — `outputMintVault`** — Vault's output token account. **Writable.**
* **`13` — `systemProgram`** — System program. Read-only.
* **`14` — `inputMintProgram`** — Token program for input mint (SPL or SPL-2022). Read-only.
* **`15` — `outputMintProgram`** — Token program for output mint (SPL or SPL-2022). Read-only.
* **`16` — `associatedTokenProgram`** — Associated Token Program. Read-only.
* **`17` — `instructionsSysvar`** — Instructions sysvar. Read-only.

***

## Instruction data

* **Byte 0** — `discriminator` (`u8`) — Always `2` (TakeOrder).
* **Bytes 1–8** — `amount` (`u64`, little-endian) — Input token amount to take.
* **Bytes 9–16** — `max_cost_amount` (`u64`, little-endian) — Maximum output tokens the taker will pay. **Use `u64::MAX` for no limit.**
* **Byte 17** — `output_mint_token_account_bump` (`u8`) — PDA bump for maker's output token account.
* **Byte 18** — `input_mint_token_account_bump` (`u8`) — PDA bump for maker's input token account.
* **Byte 19** — `fee_receiver_output_mint_bump` (`u8`) — PDA bump for fee receiver's output token account.

***

## Full execution code

The following code shows how to create a complete set of instructions to execute a `TakeOrder`, including setup (ATA creation, WSOL wrapping) and cleanup (WSOL unwrapping).

```rust
/// Fees to be paid to this address
pub const FEE_RECEIVER_ADDRESS: Pubkey = pubkey!("Bq5ZzfiU3vTiJPrBJFcr98BnUy9Wc1dg9ASeycB2tX1C");

/// Derives the vault manager address and bump seed.
pub fn get_vault_manager_address_and_bump_seed() -> (Pubkey, u8) {
    Pubkey::find_program_address(&[b"vault_manager"], &TITAN_LIMIT_ORDER_PROGRAM_ID)
}

/// Derives the associated token address and bump seed for a given wallet and token mint.
pub fn get_associated_token_address_and_bump_seed(
    wallet_address: &Pubkey,
    token_mint_address: &Pubkey,
    token_program: &Pubkey,
) -> (Pubkey, u8) {
    Pubkey::find_program_address(
        &[
            &wallet_address.to_bytes(),
            &token_program.to_bytes(),
            &token_mint_address.to_bytes(),
        ],
        &ASSOCIATED_TOKEN_PROGRAM_ID,
    )
}

/// Creates an instruction bundle to create a token account with a seed.
pub fn create_token_account_with_seed_instructions(
    payer: &Pubkey,
    authority: &Pubkey,
    mint: &Pubkey,
    seed: &str,
    owner: &Pubkey,
) -> Result<(Pubkey, Vec<Instruction>), TitanSDKError> {
    let token_account = Pubkey::create_with_seed(payer, seed, owner)
        .map_err(|_| TitanSDKError::FailedToCreateInstruction)?;

    // Get minimum balance for rent exemption
    let token_account_space = spl_token::state::Account::LEN;
    let lamports = 2039280u64;

    // Create account with seed instruction
    let create_account_ix = create_account_with_seed(
        payer,
        &token_account,
        payer,
        seed,
        lamports,
        token_account_space as u64,
        owner,
    );

    // Initialize token account instruction
    let init_account_ix = initialize_account(&spl_token::id(), &token_account, mint, authority)
        .map_err(|_| TitanSDKError::FailedToCreateInstruction)?;

    Ok((token_account, vec![create_account_ix, init_account_ix]))
}

/// Discriminator for TakeOrder instruction.
mod TakeOrder {
    pub const DISCRIMINATOR: u8 = 2;
}

/// Represents a bundle of instructions, including setup instructions and the main instruction.
pub struct InstructionBundle {
    /// A vector of setup instructions that need to be executed before the main instruction.
    pub setup: Vec<Instruction>,
    /// The main instruction to be executed.
    pub instruction: Instruction,
    /// Cleanup instructions to be executed after the main instruction.
    pub cleanup: Vec<Instruction>,
}

pub fn create_take_order_instruction(
    // Taker of the order, signer.
    taker: Pubkey,
    // Input mint token account
    taker_input_mint_token_account: Pubkey,
    // Output mint token account w/ taker authority
    taker_output_mint_token_account: Pubkey,
    // Limit order state.
    limit_order: &LimitOrder,
    // Input mint amount to recieve
    amount: u64,
    // Max cost taken from output mint token account
    // If this is breached the ixn will fail.
    max_cost_limit: Option<u64>,
    // Input token program [spl / spl-2022]
    input_mint_program: Pubkey,
    // Output token program [spl / spl-2022]
    output_mint_program: Pubkey,
) -> Result<InstructionBundle> {
    let time_in_force = TimeInForce::try_from(limit_order.time_in_force)?;
    let max_cost_limit = max_cost_limit.unwrap_or(u64::MAX);

    let remaining_balance_left = amount != limit_order.get_remaining_amount();
    let order_will_close = !remaining_balance_left
        || time_in_force == TimeInForce::ImmediateOrCancel
        || time_in_force == TimeInForce::TakeCancelsOrder;

    let fee_ticks = limit_order.fee_ticks;
    let (cost, fee) = limit_order
        .calculate_costs_and_fee(amount, fee_ticks)?;

    let output_is_wsol = limit_order.output_mint.eq(&WRAPPED_SOL);
    let input_is_wsol = limit_order.input_mint.eq(&WRAPPED_SOL);

    let limit_order_address = derive_limit_order_address(limit_order);

    let (vault_manager_address, _) = get_vault_manager_address_and_bump_seed();

    let maker = Pubkey::new_from_array(limit_order.maker);
    let input_mint = Pubkey::new_from_array(limit_order.input_mint);
    let output_mint = Pubkey::new_from_array(limit_order.output_mint);

    let mut setup = vec![create_associated_token_account_idempotent(
        &taker,
        &Pubkey::new_from_array(FEE_RECEIVER_ADDRESS),
        &output_mint,
        &output_mint_program,
    )];
    let mut cleanup = vec![];

    if output_is_wsol {
        let wsol_ata = get_associated_token_address_with_program_id(
            &taker,
            &output_mint,
            &output_mint_program,
        );
        setup.extend_from_slice(&[
            create_associated_token_account_idempotent(
                &taker,
                &taker,
                &output_mint,
                &output_mint_program,
            ),
            transfer(&taker, &wsol_ata, cost.saturating_add(fee)),
            sync_native(&output_mint_program, &wsol_ata)?,
        ]);
        cleanup.push(
            close_account(&output_mint_program, &wsol_ata, &taker, &taker, &[])?,
        )
    }

    let (output_mint_token_account_address, output_mint_token_account_bump) =
        if order_will_close && output_is_wsol {
            // Handle the special case here
            let (pk, instructions) = create_token_account_with_seed_instructions(
                &taker,
                &taker,
                &output_mint,
                "token_seed",
                &output_mint_program,
            )?;
            setup.extend(instructions);
            (pk, 0) // Bump is not used in this case
        } else {
            // otherwise always assume its the makers output ata.
            get_associated_token_address_and_bump_seed(&maker, &output_mint, &output_mint_program)
        };

    let (input_mint_token_account_address, input_mint_token_account_bump) =
        if order_will_close && remaining_balance_left && input_is_wsol {
            // Handle the special case here
            let (pk, instructions) = create_token_account_with_seed_instructions(
                &taker,
                &taker,
                &input_mint,
                "token_seed",
                &input_mint_program,
            )?;
            setup.extend(instructions);
            (pk, 0) // Bump is not used in this case
        } else {
            // otherwise always assume its the makers output ata.
            get_associated_token_address_and_bump_seed(&maker, &input_mint, &input_mint_program)
        };

    let (input_mint_vault_address, _) = get_associated_token_address_and_bump_seed(
        &vault_manager_address,
        &input_mint,
        &input_mint_program,
    );
    let (output_mint_vault_address, _) = get_associated_token_address_and_bump_seed(
        &vault_manager_address,
        &output_mint,
        &output_mint_program,
    );

    // Create the vault manager output token account if it doesn't exist
    setup.push(create_associated_token_account_idempotent(
        &taker,
        &vault_manager_address,
        &output_mint,
        &output_mint_program,
    ));

    let fee_receiver = Pubkey::new_from_array(FEE_RECEIVER_ADDRESS);
    let (fee_receiver_output_mint_token_account, fee_reciever_output_mint_bump) =
        get_associated_token_address_and_bump_seed(
            &fee_receiver,
            &output_mint,
            &output_mint_program,
        );

    let mut data = vec![*instructions::TakeOrder::DISCRIMINATOR];
    data.extend_from_slice(&amount.to_le_bytes());
    data.extend_from_slice(&max_cost_limit.to_le_bytes());
    data.extend_from_slice(&[
        output_mint_token_account_bump,
        input_mint_token_account_bump,
        fee_reciever_output_mint_bump,
    ]);

    let accounts = vec![
        AccountMeta::new(taker, true),
        AccountMeta::new(maker, false),
        AccountMeta::new_readonly(input_mint, false),
        AccountMeta::new_readonly(output_mint, false),
        AccountMeta::new(limit_order_address, false),
        AccountMeta::new(taker_input_mint_token_account, false),
        AccountMeta::new(taker_output_mint_token_account, false),
        AccountMeta::new(output_mint_token_account_address, false),
        AccountMeta::new(input_mint_token_account_address, false),
        AccountMeta::new(fee_receiver_output_mint_token_account, false),
        AccountMeta::new_readonly(vault_manager_address, false),
        AccountMeta::new(input_mint_vault_address, false),
        AccountMeta::new(output_mint_vault_address, false),
        AccountMeta::new_readonly(solana_program::system_program::ID, false),
        AccountMeta::new_readonly(input_mint_program, false),
        AccountMeta::new_readonly(output_mint_program, false),
        AccountMeta::new_readonly(ASSOCIATED_TOKEN_PROGRAM_ID, false),
        AccountMeta::new_readonly(solana_program::sysvar::instructions::ID, false),
    ];

    Ok(InstructionBundle {
        setup,
        instruction: Instruction {
            program_id: TITAN_LIMIT_ORDER_PROGRAM_ID,
            accounts,
            data,
        },
        cleanup,
    })
}
```

***

## Related pages

* [Limit Orders Overview](/titan/developer-doc/searchers-limit-orders/overview.md) — Order structure, price calculation, time-in-force, fees
* [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
