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

# TypeScript Bot Client

> Reference client pattern for server-side SaucerSwap V3 Orderbook API integrations

This page documents a TypeScript client pattern for server-side bots and market makers. It is meant to make the API flow concrete: authenticate, fetch market data, build orders, sign orders, save orders, subscribe to streams, and cancel orders.

<Warning>
  This pattern is for backend services and dedicated bot accounts. Do not ship private-key signing code in a browser app, and do not use a primary wallet key for automated trading.
</Warning>

## Dependencies

```bash theme={null}
npm install ethers@5 ws @hiero-ledger/sdk
npm install --save-dev @types/ws
```

The example client uses:

| Package             | Purpose                                          |
| ------------------- | ------------------------------------------------ |
| `ethers@5`          | EIP-712 signing and EVM account utilities        |
| `ws`                | Node.js WebSocket support                        |
| `@hiero-ledger/sdk` | Hedera account key handling for ED25519 accounts |

<Info>
  The example pins `ethers@5` intentionally for CommonJS-friendly bot projects. If your service already runs as ESM, you can adapt the signing helpers to `ethers@6+`, but do not upgrade the sample dependency without also reviewing the module format and API changes.
</Info>

## Recommended Client Surface

If you build a small wrapper around the API, use a surface like this:

| Method                                 | Purpose                                                                          |
| -------------------------------------- | -------------------------------------------------------------------------------- |
| `authenticate(accountId, privateKey)`  | Run challenge and verify flow; store the JWT                                     |
| `getOrderbooks(query)`                 | Discover orderbook token IDs, EVM addresses, decimals, status, and routing flags |
| `getDepth(orderbookId)`                | Fetch a depth snapshot                                                           |
| `getDomain()`                          | Fetch and cache the EIP-712 signing domain                                       |
| `buildOrders(requests)`                | Request nonce-assigned serialized orders                                         |
| `signOrder(order, privateKey, domain)` | Produce a prefixed signature for a built order                                   |
| `saveOrders(items)`                    | Submit signed orders                                                             |
| `cancel(orderIds)`                     | Request cancellation for specific order IDs                                      |
| `cancelAll(nonceFloor)`                | Emergency cancel by nonce floor                                                  |
| `connectDepthWs(books, handlers)`      | Subscribe to live depth updates                                                  |
| `connectUserEventsWs(books, handlers)` | Subscribe to authenticated order events                                          |

The examples below assume a wrapper with this shape. You can also call the REST and WebSocket endpoints directly.

## Minimal Limit Order Flow

```typescript theme={null}
const client = new OrderbookApiClient({ environment: 'testnet' })

async function placeLimitOrder() {
  await client.authenticate('0.0.123456', process.env.PRIVATE_KEY!)

  const domain = await client.getDomain()

  const requests = [
    {
      orderbookId: '3',
      type: 'LIMIT' as const,
      deadline: String(Math.floor(Date.now() / 1000) + 3600),
      inputToken: '0xbase-token-evm-address',
      inputAmount: '1000000',
      outputToken: '0xquote-token-evm-address',
      outputAmount: '950000',
      isAMMEnabled: true,
    },
  ]

  const builtOrders = await client.buildOrders(requests)

  const items = []
  for (let i = 0; i < builtOrders.length; i++) {
    const signature = await client.signOrder(
      builtOrders[i],
      process.env.PRIVATE_KEY!,
      domain,
    )

    items.push({
      order: builtOrders[i],
      signature,
      orderbookId: requests[i].orderbookId,
      type: requests[i].type,
    })
  }

  const { orders: saved } = await client.saveOrders(items)
  for (const order of saved) {
    console.log(
      `order ${order.meta?.id}: status=${order.meta?.status} nonce=${order.info.nonce}`,
    )
  }
}

placeLimitOrder().catch(console.error)
```

## Signing Behavior

The bot client signs the serialized order returned by `buildOrders()`.

For `ECDSA_SECP256K1` accounts:

1. Build the EIP-712 order payload.
2. Sign with `wallet._signTypedData(domain, types, order)`.
3. Prefix the signature with `0x00`.

For `ED25519` Hedera accounts:

1. Build the same EIP-712 order payload.
2. Hash it with `ethers.utils._TypedDataEncoder.hash`.
3. Sign the raw hash bytes with the Hiero SDK private key.
4. Prefix the signature bytes with `0x00`.

Wallet integrations that use Hedera personal sign should instead use the `0x01` signing mode and submit a HIP-632 `SignatureMap` around the raw wallet signature.

## Implementation Notes

* Cache the value returned by `getDomain()` for the current session.
* Reuse the same authenticated account for build, sign, save, and cancellation.
* Sign the order returned by `buildOrders()`, not the original request body.
* Preserve all integer fields as strings.
* Use `baseTokenDecimals` and `quoteTokenDecimals` from `getOrderbooks()` when converting between display amounts and raw token units.
* Keep the signature mode byte in the submitted signature.
* Re-authenticate before reconnecting WebSocket streams.

## WebSocket Example

```typescript theme={null}
await client.authenticate('0.0.123456', process.env.PRIVATE_KEY!)

const ws = client.connectDepthWs(['3'], {
  onOpen: () => console.log('depth stream connected'),
  onMessage: (message) => {
    const event = JSON.parse(message)
    console.log(event)
  },
  onClose: (code, reason) => {
    console.log(`depth stream closed: ${code} ${reason}`)
  },
  onError: (error) => {
    console.error(error)
  },
})
```

Production clients should re-authenticate before reconnecting and should not log full WebSocket URLs because the JWT is passed in the query string.

## Cancellation Example

```typescript theme={null}
await client.cancel([1234, 1235])
```

Cancellation is asynchronous. Wait for the user-event stream or fetch order history before treating an order as cancelled.

```typescript theme={null}
await client.cancelAll('100000000000000000000')
```

Use nonce-floor cancellation only for emergency recovery or deliberate session shutdown flows.
