> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sx.bet/llms.txt
> Use this file to discover all available pages before exploring further.

# Filling Orders

> How to fill existing orders on the SX Bet orderbook as a taker.

## Overview

Filling an order means taking the other side of an existing maker order on the orderbook. As a taker, you get immediate execution at the price a maker has posted.

SX Bet uses [`POST /orders/fill/v2`](/api-reference/post-fill-order) to submit fills. The exchange matches your fill against the best available orders after a short betting delay.

## Prerequisites

1. **Create an account** at [sx.bet](https://sx.bet) and export your private key from the [assets page](https://sx.bet/wallet/assets)
2. **Fund your account** with USDC
3. **Enable betting** — place a test bet through [sx.bet](https://sx.bet) (or [toronto.sx.bet](https://toronto.sx.bet) for testnet) with your account, or do it programmatically via [POST /orders/approve](/api-reference/post-approve)

## Fill fields

Each fill submitted to [`POST /orders/fill/v2`](/api-reference/post-fill-order) requires these fields:

| Field                      | Type    | Description                                                                                                         |
| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| `market`                   | string  | User-facing string for the market. Set to `"N/A"` when using the API                                                |
| `baseToken`                | string  | The token this fill is denominated in (e.g., USDC). See [References](/api-reference/references) for token addresses |
| `isTakerBettingOutcomeOne` | boolean | `true` if you're betting on outcome one, `false` for outcome two                                                    |
| `stakeWei`                 | string  | Your stake in base token units (Ethereum format). See [Unit Conversion](/api-reference/unit-conversion)             |
| `desiredOdds`              | string  | The worst taker odds you'll accept, in SX protocol format (`implied * 10^20`)                                       |
| `oddsSlippage`             | integer | Percentage tolerance (0–100) applied to `desiredOdds`. `0` means exact odds only                                    |
| `fillSalt`                 | string  | A random 32-byte value to identify this fill                                                                        |
| `taker`                    | string  | Your wallet address                                                                                                 |
| `takerSig`                 | string  | Your wallet signature over the fill payload                                                                         |
| `message`                  | string  | A user-facing message included in the signing payload. Can be anything (e.g., `"N/A"`)                              |

### Understanding `desiredOdds`

This is the implied probability **from your perspective as the taker**. It's the inverse of the maker's `percentageOdds`:

```
taker_implied = 10^20 - maker_percentageOdds
```

For example, if the best maker order on outcome two has `percentageOdds = "52500000000000000000"` (52.5%), the taker odds for betting outcome one are:

```
10^20 - 52500000000000000000 = 47500000000000000000 (47.5%)
```

You'd set `desiredOdds = "47500000000000000000"` to fill at that price.

<Info>Orders with taker odds **better** than your `desiredOdds` will also fill. The `desiredOdds` is a floor, not an exact match.</Info>

### Understanding `oddsSlippage`

The `oddsSlippage` field is an integer from 0 to 100 representing a percentage tolerance on your `desiredOdds`. This is useful for volatile in-play markets where odds shift quickly.

* `0` — only accept `desiredOdds` or better (no slippage)
* `5` — accept up to 5% worse than `desiredOdds`

For pre-game markets, `0` is usually fine. For in-play markets, a small slippage (2–5) helps ensure your fill goes through.

### Understanding `stakeWei`

This is the amount of tokens **you** are putting up for the bet. It's in Ethereum units — for USDC (6 decimals), `"50000000"` = 50 USDC.

If there isn't enough liquidity to fill your full stake, the exchange will partially fill you for the available amount.

<Info>The minimum taker stake is **1 USDC**.</Info>

## Step 1: Find orders to fill

Check what odds are available on the market you want to bet on using [`GET /orders/odds/best`](/api-reference/get-best-odds):

<CodeGroup>
  ```python Python theme={null}
  import requests

  BASE_URL = "https://api.sx.bet"  # Mainnet — use https://api.toronto.sx.bet for testnet
  ODDS_PRECISION = 10 ** 20

  market_hash = "YOUR_MARKET_HASH"
  base_token = "0x6629Ce1Cf35Cc1329ebB4F63202F3f197b3F050B"  # Mainnet USDC — see References for testnet address

  best = requests.get(
      f"{BASE_URL}/orders/odds/best",
      params={"marketHashes": market_hash, "baseToken": base_token}
  ).json()["data"]["bestOdds"][0]

  o1_maker = int(best["outcomeOne"]["percentageOdds"]) / ODDS_PRECISION
  o2_maker = int(best["outcomeTwo"]["percentageOdds"]) / ODDS_PRECISION

  # Taker odds are the inverse of the opposite outcome's maker odds
  print(f"Taker odds for outcome 1: {1 - o2_maker:.2%}")
  print(f"Taker odds for outcome 2: {1 - o1_maker:.2%}")
  ```

  ```javascript JavaScript theme={null}
  const BASE_URL = "https://api.sx.bet"; // Mainnet — use https://api.toronto.sx.bet for testnet
  const ODDS_PRECISION = 1e20;

  const marketHash = "YOUR_MARKET_HASH";
  const baseToken = "0x6629Ce1Cf35Cc1329ebB4F63202F3f197b3F050B"; // Mainnet USDC — see References for testnet address

  const best = await fetch(
    `${BASE_URL}/orders/odds/best?${new URLSearchParams({ marketHashes: marketHash, baseToken })}`
  ).then((r) => r.json());
  const odds = best.data.bestOdds[0];

  const o1Maker = Number(odds.outcomeOne.percentageOdds) / ODDS_PRECISION;
  const o2Maker = Number(odds.outcomeTwo.percentageOdds) / ODDS_PRECISION;

  // Taker odds are the inverse of the opposite outcome's maker odds
  console.log(`Taker odds for outcome 1: ${((1 - o2Maker) * 100).toFixed(2)}%`);
  console.log(`Taker odds for outcome 2: ${((1 - o1Maker) * 100).toFixed(2)}%`);
  ```
</CodeGroup>

For deeper orderbook inspection, use [`GET /orders`](/api-reference/get-orders) to see all open orders and their available sizes. See [Navigating the Orderbook](/developers/navigating-the-orderbook) for more on reading depth.

## Step 2: Sign and submit the fill

Fills require a typed data signature from your wallet. This is a different signing method from maker orders — the code below handles it for you. See [Order Signing](/api-reference/eip712-signing) for the full reference.

<Warning>
  The `verifyingContract`, `chainId`, and `baseToken` values below are hardcoded to mainnet. Using the wrong values will cause signature failures. Fetch `verifyingContract` from [`GET /metadata`](/api-reference/get-metadata) → `EIP712FillHasher`, and get the correct `baseToken` for your network from [References](/api-reference/references). See [Testnet & Mainnet](/developers/testnet-and-mainnet) for the recommended config pattern.
</Warning>

<CodeGroup>
  ```python Python theme={null}
  import os
  import secrets
  import requests
  from eth_account import Account

  BASE_URL = "https://api.sx.bet"  # Mainnet — use https://api.toronto.sx.bet for testnet
  account = Account.from_key(os.environ["SX_PRIVATE_KEY"])

  market_hash = "YOUR_MARKET_HASH"
  base_token = "0x6629Ce1Cf35Cc1329ebB4F63202F3f197b3F050B"  # Mainnet USDC — see References for testnet address
  stake_wei = "50000000"  # 50 USDC (6 decimals)

  # Taker odds: 10^20 - best maker odds on the opposite outcome
  desired_odds = "47500000000000000000"  # 47.5% taker implied

  fill_salt = int.from_bytes(secrets.token_bytes(32), "big")

  # --- Sign the fill ---
  DOMAIN = {
      "name": "SX Bet",
      "version": "6.0",
      "chainId": 4162,
      "verifyingContract": "0x845a2Da2D70fEDe8474b1C8518200798c60aC364",  # Mainnet — use GET /metadata → EIP712FillHasher for testnet
  }

  FILL_TYPES = {
      "Details": [
          {"name": "action", "type": "string"},
          {"name": "market", "type": "string"},
          {"name": "betting", "type": "string"},
          {"name": "stake", "type": "string"},
          {"name": "worstOdds", "type": "string"},
          {"name": "worstReturning", "type": "string"},
          {"name": "fills", "type": "FillObject"},
      ],
      "FillObject": [
          {"name": "stakeWei", "type": "string"},
          {"name": "marketHash", "type": "string"},
          {"name": "baseToken", "type": "string"},
          {"name": "desiredOdds", "type": "string"},
          {"name": "oddsSlippage", "type": "uint256"},
          {"name": "isTakerBettingOutcomeOne", "type": "bool"},
          {"name": "fillSalt", "type": "uint256"},
          {"name": "beneficiary", "type": "address"},
          {"name": "beneficiaryType", "type": "uint8"},
          {"name": "cashOutTarget", "type": "bytes32"},
      ],
  }

  signed = Account.sign_typed_data(
      account.key,
      domain_data=DOMAIN,
      message_types=FILL_TYPES,
      message_data={
          "action": "N/A",
          "market": market_hash,
          "betting": "N/A",
          "stake": "N/A",
          "worstOdds": "N/A",
          "worstReturning": "N/A",
          "fills": {
              "stakeWei": stake_wei,
              "marketHash": market_hash,
              "baseToken": base_token,
              "desiredOdds": desired_odds,
              "oddsSlippage": 0,
              "isTakerBettingOutcomeOne": True,
              "fillSalt": fill_salt,
              "beneficiary": "0x0000000000000000000000000000000000000000",
              "beneficiaryType": 0,
              "cashOutTarget": b"\x00" * 32,
          },
      },
  )
  taker_sig = "0x" + signed.signature.hex()

  # --- Submit ---
  response = requests.post(f"{BASE_URL}/orders/fill/v2", json={
      "market": market_hash,
      "baseToken": base_token,
      "isTakerBettingOutcomeOne": True,
      "stakeWei": stake_wei,
      "desiredOdds": desired_odds,
      "oddsSlippage": 0,
      "taker": account.address,
      "takerSig": taker_sig,
      "fillSalt": str(fill_salt),
  })

  result = response.json()
  print("Status:", result["status"])
  print("Data:", result["data"])
  ```

  ```javascript JavaScript theme={null}
  import "dotenv/config";
  import { Wallet, ZeroAddress, ZeroHash, randomBytes, hexlify } from "ethers";

  const BASE_URL = "https://api.sx.bet"; // Mainnet — use https://api.toronto.sx.bet for testnet

  const wallet = new Wallet(process.env.SX_PRIVATE_KEY);

  const marketHash = "YOUR_MARKET_HASH";
  const baseToken = "0x6629Ce1Cf35Cc1329ebB4F63202F3f197b3F050B"; // Mainnet USDC — see References for testnet address
  const stakeWei = "50000000"; // 50 USDC (6 decimals)

  // Taker odds: 10^20 - best maker odds on the opposite outcome
  const desiredOdds = "47500000000000000000"; // 47.5% taker implied

  const oddsSlippage = 0;
  const isTakerBettingOutcomeOne = true;
  const fillSalt = BigInt(hexlify(randomBytes(32))).toString();

  // --- Sign the fill ---
  const domain = {
    name: "SX Bet",
    version: "6.0",
    chainId: 4162,
    verifyingContract: "0x845a2Da2D70fEDe8474b1C8518200798c60aC364", // Mainnet — use GET /metadata → EIP712FillHasher for testnet
  };

  const types = {
    Details: [
      { name: "action", type: "string" },
      { name: "market", type: "string" },
      { name: "betting", type: "string" },
      { name: "stake", type: "string" },
      { name: "worstOdds", type: "string" },
      { name: "worstReturning", type: "string" },
      { name: "fills", type: "FillObject" },
    ],
    FillObject: [
      { name: "stakeWei", type: "string" },
      { name: "marketHash", type: "string" },
      { name: "baseToken", type: "string" },
      { name: "desiredOdds", type: "string" },
      { name: "oddsSlippage", type: "uint256" },
      { name: "isTakerBettingOutcomeOne", type: "bool" },
      { name: "fillSalt", type: "uint256" },
      { name: "beneficiary", type: "address" },
      { name: "beneficiaryType", type: "uint8" },
      { name: "cashOutTarget", type: "bytes32" },
    ],
  };

  const message = {
    action: "N/A",
    market: marketHash,
    betting: "N/A",
    stake: "N/A",
    worstOdds: "N/A",
    worstReturning: "N/A",
    fills: {
      stakeWei,
      marketHash,
      baseToken,
      desiredOdds,
      oddsSlippage,
      isTakerBettingOutcomeOne,
      fillSalt,
      beneficiary: ZeroAddress,
      beneficiaryType: 0,
      cashOutTarget: ZeroHash,
    },
  };

  const takerSig = await wallet.signTypedData(domain, types, message);

  // --- Submit ---
  const response = await fetch(`${BASE_URL}/orders/fill/v2`, {
    method: "POST",
    body: JSON.stringify({
      market: "N/A",
      baseToken,
      isTakerBettingOutcomeOne,
      stakeWei,
      desiredOdds,
      oddsSlippage,
      taker: wallet.address,
      takerSig,
      fillSalt,
      message: "N/A",
    }),
    headers: { "Content-Type": "application/json" },
  });

  const result = await response.json();
  console.log("Status:", result.status);
  console.log("Data:", result.data);
  ```
</CodeGroup>

## How filling works

After you submit a fill:

1. The exchange queues your fill and applies a short **betting delay** (a few seconds for pre-game, optimized for in-play)
2. After the delay, the exchange matches your fill against the best available maker orders at or better than your `desiredOdds` (accounting for `oddsSlippage`)
3. If enough liquidity exists, your fill executes. If not, you get a partial fill for the available amount
4. The resulting trade(s) appear in your [trade history](/api-reference/get-trades)

<Info>The betting delay prevents front-running on in-play markets where odds shift rapidly. Your fill is matched against the orderbook state **after** the delay, not at submission time.</Info>

## Monitoring your fills

Subscribe to the [`recent_trades:global` WebSocket channel](/api-reference/centrifugo-trade-updates) to get real-time notifications when fills execute. This is a global feed — filter messages to your address in the handler.

<Note>
  Each fill emits two messages on `recent_trades:global`: one with `tradeStatus: "PENDING"` when the trade is broadcast, and a second with `tradeStatus: "SUCCESS"` or `"FAILED"` once on-chain confirmation completes. You can also check `tradeStatus` directly via [`GET /trades`](/api-reference/get-trades).
</Note>

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { Centrifuge } from "centrifuge";

  const RELAYER_URL = "https://api.sx.bet"; // Mainnet — use https://api.toronto.sx.bet for testnet
  const WS_URL = "wss://realtime.sx.bet/connection/websocket"; // Mainnet — use wss://realtime.toronto.sx.bet/connection/websocket for testnet

  async function fetchToken() {
    const res = await fetch(`${RELAYER_URL}/user/realtime-token/api-key`, {
      headers: { "x-api-key": process.env.SX_API_KEY },
    });
    if (!res.ok) throw new Error(`Token endpoint returned ${res.status}`);
    const { token } = await res.json();
    return token;
  }

  const client = new Centrifuge(WS_URL, { getToken: fetchToken });
  const userAddress = wallet.address;

  const sub = client.newSubscription("recent_trades:global", {
    positioned: true,
    recoverable: true,
  });

  sub.on("publication", (ctx) => {
    const trades = Array.isArray(ctx.data) ? ctx.data : [ctx.data];
    for (const trade of trades) {
      if (trade.bettor.toLowerCase() !== userAddress.toLowerCase()) continue;
      console.log(`${trade.tradeStatus}: market=${trade.marketHash}, stake=${trade.stake}, odds=${trade.odds}`);
    }
  });

  sub.subscribe();
  client.connect();
  ```

  ```python Python theme={null}
  import asyncio
  import aiohttp
  from centrifuge import Client, PublicationContext, SubscriptionEventHandler, SubscriptionOptions

  RELAYER_URL = "https://api.sx.bet"  # Mainnet — use https://api.toronto.sx.bet for testnet
  WS_URL = "wss://realtime.sx.bet/connection/websocket"  # Mainnet — use wss://realtime.toronto.sx.bet/connection/websocket for testnet

  async def fetch_token():
      async with aiohttp.ClientSession() as session:
          async with session.get(
              f"{RELAYER_URL}/user/realtime-token/api-key",
              headers={"x-api-key": os.environ["SX_API_KEY"]},
          ) as resp:
              data = await resp.json()
              return data["token"]

  async def main():
      client = Client(WS_URL, get_token=fetch_token)
      user_address = account.address

      async def on_publication(ctx: PublicationContext) -> None:
          trades = ctx.data if isinstance(ctx.data, list) else [ctx.data]
          for trade in trades:
              if trade["bettor"].lower() != user_address.lower():
                  continue
              print(f"{trade['tradeStatus']}: market={trade['marketHash']}, stake={trade['stake']}, odds={trade['odds']}")

      handler = SubscriptionEventHandler(on_publication=on_publication)
      options = SubscriptionOptions(positioned=True, recoverable=True)
      sub = client.new_subscription("recent_trades:global", handler, options)

      await client.connect()
      await sub.subscribe()
      await asyncio.Future()

  asyncio.run(main())
  ```
</CodeGroup>

## Real-time orderbook and odds

If you're actively scanning markets before filling — especially in-play — polling REST adds latency. Two WebSocket channels are useful here:

* **`order_book:market_{marketHash}`** — full orderbook updates for a specific market. Subscribe with `positioned: true, recoverable: true` before fetching the snapshot to avoid gaps. See [Fetching Odds → Real-time orderbook](/developers/fetching-odds#real-time-orderbook) for the correct subscribe-then-fetch pattern.
* **`best_odds:global`** — fires whenever the best available odds change across any market. Useful for watching multiple markets without subscribing to each orderbook individually. See [Fetching Odds → Real-time best odds](/developers/fetching-odds#real-time-best-odds).

## Common issues

### Fill rejected — betting not enabled

You need to approve the `TokenTransferProxy` contract for each token you trade. The simplest way is to place a test bet through [sx.bet](https://sx.bet) (or [toronto.sx.bet](https://toronto.sx.bet) for testnet). To do it programmatically, use [POST /orders/approve](/api-reference/post-approve).

### Fill rejected — insufficient balance

Your wallet must have enough of the base token to cover `stakeWei`. Fund your account before submitting.

### Fill returns no trades

This means there wasn't enough liquidity at your `desiredOdds` (accounting for `oddsSlippage`). Either relax your odds, increase slippage, or wait for more maker orders to appear on the book.

### In-play fills timing out

For in-play markets, odds move quickly. Use a small `oddsSlippage` (2–5) to give your fill a better chance of executing. Check the latest odds via [`GET /orders/odds/best`](/api-reference/get-best-odds) before each fill.

## Related

<CardGroup cols={2}>
  <Card title="Order Signing →" icon="key" href="/api-reference/eip712-signing">
    Full signing reference for fills and orders.
  </Card>

  <Card title="POST /orders/fill/v2 →" icon="paper-plane" href="/api-reference/post-fill-order">
    Full API reference for the fill endpoint.
  </Card>

  <Card title="Navigating the Orderbook →" icon="book-open" href="/developers/navigating-the-orderbook">
    Reading depth and finding the best prices.
  </Card>

  <Card title="Unit Conversion →" icon="arrows-rotate" href="/api-reference/unit-conversion">
    Converting between raw values and human-readable amounts.
  </Card>
</CardGroup>
