> For the complete documentation index, see [llms.txt](https://docs.ston.fi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ston.fi/developer-section/omniston/sdk/nodejs.md).

# TypeScript and JavaScript

Follow the Omniston SDK workflow from RFQ and quote acceptance to wallet signing and trade tracking.

Use `@ston-fi/omniston-sdk` in Node.js scripts, bots, backend services, and other JavaScript or TypeScript applications. It is a typed wrapper over the [Omniston WebSocket API](/developer-section/omniston/swap/overview.md). It has Promise-based methods for individual requests. With observable subscriptions, you receive quote updates and settlement updates.

You can also use the SDK in the browser. React applications can use the [Omniston React SDK](/developer-section/omniston/sdk/react.md) for providers and hooks.

This guide uses Omniston API v1beta8 and Omniston SDK 0.8.x. The examples show the primary integration steps. Your application controls when to accept a quote. It must also supply wallet signing, transaction submission, and persistence.

The Omniston SDK builds unsigned transactions or order payloads. The wallet signs them.

## Installation

Install the [Omniston SDK package](https://www.npmjs.com/package/@ston-fi/omniston-sdk):

```sh
npm install @ston-fi/omniston-sdk
```

## Configuration

### API URL

Select an [environment](/developer-section/omniston/environments.md). Set `apiUrl` to its Trading / RFQ WebSocket URL when you create the `Omniston` client. Use the same client for requests and subscriptions in your application.

```ts
import { Omniston } from "@ston-fi/omniston-sdk";

const apiUrl = "wss://omni-ws.ston.fi";
const omniston = new Omniston({ apiUrl });
```

### Transport

**Connection**

The Omniston SDK automatically opens the WebSocket connection for the first Omniston API request. This applies to individual requests and streaming subscriptions. For streaming subscriptions, the SDK sends the request when you call `subscribe()`.

**Default transport**

By default, `Omniston` uses `AutoReconnectTransport` around `WebSocketTransport`. After a connection error, it makes a maximum of five reconnection attempts. The first attempt starts after one second. The delay doubles for each subsequent attempt.

The Omniston SDK keeps local stream subscriptions open during reconnection. After reconnection, it opens their server subscriptions again. An RFQ can receive a new `ack` with a new RFQ ID after reconnection. When the server sends `unsubscribed`, the stream ends. The SDK does not automatically open that stream again.

**Custom transport**

Pass `transport` to the `Omniston` constructor to replace the default transport. Select an implementation that meets your application's reconnection requirements:

* Configure `AutoReconnectTransport` around `WebSocketTransport` to use a different automatic retry policy.
* Use `new WebSocketTransport(apiUrl)` to let your application control reconnection.
* Implement the exported `Transport` interface for your own transport. Your transport must have the `messages` and `connectionStatusEvents` observables. It must also have the `connect()`, `reconnect()`, `send()`, and `close()` methods. The transport must send and receive the JSON-RPC messages for the Omniston WebSocket API. It must report connection status changes to let the Omniston SDK restore subscriptions.

For example, configure the automatic retry policy as follows:

```ts
import {
  AutoReconnectTransport,
  Omniston,
  WebSocketTransport,
} from "@ston-fi/omniston-sdk";

const omniston = new Omniston({
  apiUrl,
  transport: new AutoReconnectTransport({
    transport: new WebSocketTransport(apiUrl),
    maxRetries: 10,
    reconnectDelayMs: 500,
  }),
});
```

**Connection status**

Read `omniston.connectionStatus` for the current status. To receive status changes, subscribe to `omniston.connectionStatusEvents`. A new subscriber immediately receives the current status.

```ts
const connectionSubscription = omniston.connectionStatusEvents.subscribe(
  (connectionStatusEvent) => {
    console.log("Connection:", connectionStatusEvent.status);

    if (connectionStatusEvent.status === "error") {

      console.error(connectionStatusEvent.errorMessage);
      console.log("Reconnecting:", connectionStatusEvent.isReconnecting);
    }
  },
);
```

### Logging

The Omniston SDK does not write logs by default. For connection and Omniston API diagnostics, pass a compatible logger, such as `console`:

```ts
const omniston = new Omniston({ apiUrl, logger: console });
```

## Request for quote

An RFQ (request for quote) contains the assets, amount, and settlement methods that your application supports. Omniston returns a stream of quotes. A quote request does not submit a trade. For an overview of the Omniston protocol, see [How Omniston works](/developer-section/omniston/how-omniston-works.md).

Use an `AssetId` for each asset. Write the amount as a decimal string of integer base units. `inputUnits` sets the amount that the trader spends. `outputUnits` sets the net output amount after fees. See [Supported chains and assets](/developer-section/omniston/chains.md) and [Amounts and fee fields](/developer-section/omniston/swap/v1beta8.md#amounts-and-fee-fields).

### Settlement methods

A settlement method controls how the protocol executes a trade after the trader accepts a quote. The Omniston protocol supports two settlement methods:

* `swap` — available only for intrachain swaps on TON. It executes the trade through swap routes. It does not create a persistent order.
* `order` — supports cross-chain swaps with HTLC settlement. Resolvers fill escrow-backed or signed orders. This method also supports intrachain orders on TON.

Set the methods that your application supports in `settlementParams`. For TON intrachain swaps, you can enable the two methods. Use the method from `quote.settlementData.$case`. For cross-chain swaps, use `order`. To compare the methods, see [Settlement modes in How Omniston works](/developer-section/omniston/how-omniston-works.md#settlement-modes).

### Settlement params

The two settlement methods support optional integrator-fee settings through top-level `QuoteRequest` fields:

* `integratorAddress` — the address on the destination blockchain where the integrator receives fees.
* `integratorFeePips` — the requested fee rate in pips, where one pip is 0.0001%.

The protocol charges the fee in the output asset. See [Integrator fees](/developer-section/omniston/referral-fees.md) for fee configuration and collection mechanisms. Set method-specific options in `settlementParams`. The following examples show these options.

### Swap quote request

This RFQ exchanges 1 TON for USDT on TON. It uses swap settlement only.

```ts
import type { QuoteRequest } from "@ston-fi/omniston-sdk";

const quoteRequest: QuoteRequest = {
  inputAsset: { // Native TON
    chain: {
      $case: "ton",
      value: { kind: { $case: "native", value: {} } },
    },
  },
  outputAsset: { // USDT on TON
    chain: {
      $case: "ton",
      value: {
        kind: {
          $case: "jetton",
          value: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs",
        },
      },
    },
  },
  amount: { $case: "inputUnits", value: "1000000000" }, // 1 TON (9 decimals)
  settlementParams: [
    {
      params: {
        $case: "swap",
        value: {
          maxPriceSlippagePips: 10_000, // 1%
          flexibleIntegratorFee: true,
        },
      },
    },
  ],
};
```

`maxPriceSlippagePips` uses millionths: `10_000` is 1%. The `flexibleIntegratorFee` option applies only to swap settlement. With this option, the effective integrator fee can be lower if necessary to meet the route's fee constraints.

### Order quote request

This RFQ exchanges 1 USDT on TON for USDC on Base. It uses order settlement only. When you build the order, supply a Base destination address.

```ts
import type { QuoteRequest } from "@ston-fi/omniston-sdk";

const quoteRequest: QuoteRequest = {
  inputAsset: { // USDT on TON
    chain: {
      $case: "ton",
      value: {
        kind: {
          $case: "jetton",
          value: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs",
        },
      },
    },
  },
  outputAsset: { // USDC on Base
    chain: {
      $case: "base",
      value: {
        kind: {
          $case: "erc20",
          value: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        },
      },
    },
  },
  amount: { $case: "inputUnits", value: "1000000" }, // 1 USDT (6 decimals)
  settlementParams: [
    {
      params: {
        $case: "order",
        value: {},
      },
    },
  ],
};
```

Quotes are available only for supported chain pairs with available liquidity.

### Send an RFQ

Pass the `quoteRequest` object from one of the two examples to `requestForQuote()` to get the RFQ stream. In the next section, subscribe to this stream. The SDK then sends the quote request to the Omniston API.

```ts
const rfqStream = omniston.requestForQuote(quoteRequest);
```

## Handle quote updates

Subscribe to `rfqStream` to receive updates. The `next` callback receives quote update events. The `error` callback receives subscription failures.

Keep the latest quote available for the trader to accept. When no valid quote is available, clear the previous quote. If the subscription closes, clear the previous quote.

```ts
import type { Quote } from "@ston-fi/omniston-sdk";

let latestQuote: Quote | undefined;

const rfqSubscription = rfqStream.subscribe({
  next(rfqEvent) {
    switch (rfqEvent.$case) {
      case "ack":
        latestQuote = undefined;
        console.log("RFQ id:", rfqEvent.value.rfqId);
        break;
      case "quoteUpdated":
        latestQuote = rfqEvent.value;
        console.log("Quote:", latestQuote.quoteId, latestQuote.outputUnits);
        break;
      case "noQuote":
        latestQuote = undefined;
        console.log("No quote for RFQ:", rfqEvent.rfqId);
        break;
      case "unsubscribed":
        latestQuote = undefined;
        console.log("Quote stream closed by server");
        break;
    }
  },
  error(error) {
    latestQuote = undefined;
    console.error("RFQ stream failed", error);
  },
});
```

### Quote update events

| Event          | Meaning                                                                            | Application action                                                        |
| -------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `ack`          | The server accepted the RFQ and gave it `value.rfqId`.                             | Record the RFQ ID. Wait for a quote.                                      |
| `quoteUpdated` | `value` contains the current `Quote`, including its `quoteId` and settlement data. | Replace the previous quote.                                               |
| `noQuote`      | There is currently no valid quote for this RFQ.                                    | Clear the previous quote. Continue to receive updates.                    |
| `unsubscribed` | The server closed this subscription.                                               | Do not use the stream again. If necessary, create a new RFQ subscription. |

The Omniston SDK filters out `keepAlive` messages from the Omniston API. It adds `rfqId` to `quoteUpdated` and `noQuote` events. Subscription failures reach the `error` callback.

Each new quote replaces the previous quote. This can occur because the terms changed or the previous quote expired. Accept only the current valid quote. For order quotes, check `tradeStartDeadline` in the settlement data. See [Lifecycle and timing](/developer-section/omniston/how-omniston-works.md#lifecycle-and-timing).

## Start a trade

### Accept a quote

When the trader accepts a valid quote from a `quoteUpdated` event, more RFQ updates are not necessary. You can stop the RFQ subscription:

```ts
rfqSubscription.unsubscribe();
```

Use the accepted quote to build the transaction or order. Then, sign the transaction or order with the wallet.

In the following examples, `quote` is the accepted `Quote` from a `quoteUpdated` event. Use the branch for its settlement method and source chain. Replace the example wallet addresses with your wallet addresses on the applicable networks.

### Build and sign swap transaction

For the selected swap quote, use `tonBuildSwap()` to build an unsigned swap transaction:

```ts
import { isSwapQuote, type ChainAddress } from "@ston-fi/omniston-sdk";

if (!isSwapQuote(quote)) {
  throw new Error("Expected a swap quote");
}

const traderAddress: ChainAddress = {
  chain: { $case: "ton", value: "EQ..." },
};

const swapTx = await omniston.tonBuildSwap({
  quoteId: quote.quoteId,
  transferSrcAddress: traderAddress,
});
```

Sign all `swapTx.messages` with your wallet. Send all messages with your selected [TON transaction-sending method](/developer-section/common/transaction-sending.md). See [TON wallet integration](/developer-section/omniston/sdk/ton.md) to adapt the Omniston SDK transaction to your wallet.

For `outgoingTxQuery`, get the outgoing transaction hash or another supported identifier. Use this identifier to track the swap with `swapTrack()`. See [Submit and identify a swap](/developer-section/omniston/sdk/ton.md#submit-and-identify-a-swap).

### Build and sign order transaction

Select the build and submission flow for the order's source blockchain:

| Source blockchain | Build                      | Sign and submit                                                                        |
| ----------------- | -------------------------- | -------------------------------------------------------------------------------------- |
| [TON](#ton)       | `tonBuildEscrowTransfer()` | Sign the returned transaction messages with a TON wallet. Send the messages.           |
| [EVM](#evm)       | `evmBuildOrderPayload()`   | Sign the returned order payload. Call `orderRegisterSignedOrder()`.                    |
| [TRON](#tron)     | `evmBuildOrderPayload()`   | Sign the returned order payload with a TRON wallet. Call `orderRegisterSignedOrder()`. |

The cross-chain order examples use `secretHashes`. Your application supplies this `Uint8Array[]` of HTLC secret hashes. See [HTLC secrets](#htlc-secrets) for the relation between these hashes, the original secrets, and order tracking.

#### TON

TON supports bilateral orders within TON and HTLC orders across blockchains. The two order types use `tonBuildEscrowTransfer()`. Use the example for the selected order quote.

**Bilateral order**

A bilateral order is a TON → TON order that does not use HTLC secrets. For the selected bilateral order quote, build the escrow transfer as follows:

```ts
import {
  isOrderQuote,
  isHtlcOrderQuote,
  type ChainAddress,
} from "@ston-fi/omniston-sdk";

if (!isOrderQuote(quote) || isHtlcOrderQuote(quote)) {
  throw new Error("Expected a bilateral order quote");
}

const traderAddress: ChainAddress = {
  chain: { $case: "ton", value: "EQ..." },
};

const orderTx = await omniston.tonBuildEscrowTransfer({
  quoteId: quote.quoteId,
  ownerSrcAddress: traderAddress,
  traderDstAddress: traderAddress,
});
```

Track this bilateral order with `orderTrack()`. HTLC secret disclosure does not apply.

**HTLC order**

An HTLC order from TON to another blockchain uses HTLC secrets. See [HTLC secrets](#htlc-secrets) for an explanation of `secretHashes`. For the selected HTLC order quote, build the escrow transfer as follows:

```ts
import { isHtlcOrderQuote, type ChainAddress } from "@ston-fi/omniston-sdk";

if (!isHtlcOrderQuote(quote)) {
  throw new Error("Expected an HTLC order quote");
}

const traderAddress: ChainAddress = {
  chain: { $case: "ton", value: "EQ..." },
};

const destinationAddress: ChainAddress = {
  chain: { $case: "base", value: "0x..." },
};

const orderTx = await omniston.tonBuildEscrowTransfer({
  quoteId: quote.quoteId,
  ownerSrcAddress: traderAddress,
  traderDstAddress: destinationAddress,
  htlcSecrets: {
    secretMode: {
      $case: "provided",
      value: { hashes: secretHashes },
    },
  },
});
```

Sign `orderTx.messages` with your TON wallet. Send the messages as shown in [TON wallet integration](/developer-section/omniston/sdk/ton.md). Order tracking uses the source owner address and `quoteId`. The `outgoingTxQuery` identifier is not necessary.

#### EVM

For an order with an EVM source, use this procedure:

1. Build the order payload with `evmBuildOrderPayload()`.
2. Sign the payload with the source owner wallet.
3. Register the signed order with `orderRegisterSignedOrder()`.

In this example, `signEvmOrder()` is your application's function for EIP-712 signing and `SignedOrder` construction. See [EVM signature shapes](/developer-section/omniston/sdk/evm/evm-signature.md) for the signature requirements of different wallet kinds.

See [HTLC secrets](#htlc-secrets) for an explanation of `secretHashes`.

```ts
import {
  isHtlcOrderQuote,
  type ChainAddress,
} from "@ston-fi/omniston-sdk";

if (!isHtlcOrderQuote(quote)) {
  throw new Error("Expected an HTLC order quote");
}

const traderAddress: ChainAddress = {
  chain: { $case: "base", value: "0x..." },
};
const destinationAddress: ChainAddress = {
  chain: { $case: "ton", value: "EQ..." },
};

const orderPayload = await omniston.evmBuildOrderPayload({
  quoteId: quote.quoteId,
  ownerSrcAddress: traderAddress,
  traderDstAddress: destinationAddress,
  htlcSecrets: {
    secretMode: {
      $case: "provided",
      value: { hashes: secretHashes },
    },
  },
});

const signedOrder = await signEvmOrder(orderPayload);

await omniston.orderRegisterSignedOrder({
  quoteId: quote.quoteId,
  ownerSrcAddress: traderAddress,
  signedOrder,
  serializedOrderDetails: orderPayload.serializedOrderDetails,
});
```

Before registration, use one of these methods:

* Make sure that you have the necessary confirmed token allowance.
* Supply the supported permit fields when you build the payload.

Include `orderExtension` in the signed order. Pass `serializedOrderDetails` back without changes. See [EVM wallet integration](/developer-section/omniston/sdk/evm.md) for wallet kinds, allowance, and signature encoding.

#### TRON

For an order with a TRON source, use this procedure:

1. Build the order payload with `evmBuildOrderPayload()`.
2. Sign the payload with the source owner wallet.
3. Register the signed order with `orderRegisterSignedOrder()`.

In this example, `signTronOrder()` is your application's function for TRON wallet signing and `SignedOrder` construction. See [TRON wallet integration](/developer-section/omniston/sdk/tron.md) for the TRON-specific signing flow and reference implementation.

See [HTLC secrets](#htlc-secrets) for an explanation of `secretHashes`.

```ts
import {
  isHtlcOrderQuote,
  type ChainAddress,
} from "@ston-fi/omniston-sdk";

if (!isHtlcOrderQuote(quote)) {
  throw new Error("Expected an HTLC order quote");
}

const traderAddress: ChainAddress = {
  chain: { $case: "tron", value: "T..." },
};
const destinationAddress: ChainAddress = {
  chain: { $case: "ton", value: "EQ..." },
};

const orderPayload = await omniston.evmBuildOrderPayload({
  quoteId: quote.quoteId,
  ownerSrcAddress: traderAddress,
  traderDstAddress: destinationAddress,
  htlcSecrets: {
    secretMode: {
      $case: "provided",
      value: { hashes: secretHashes },
    },
  },
});

const signedOrder = await signTronOrder(orderPayload);

await omniston.orderRegisterSignedOrder({
  quoteId: quote.quoteId,
  ownerSrcAddress: traderAddress,
  signedOrder,
  serializedOrderDetails: orderPayload.serializedOrderDetails,
});
```

Before registration, make sure that you have a confirmed TRC-20 token allowance for the source-side protocol contract. Include `orderExtension` in the signed order. Pass `serializedOrderDetails` back without changes.

#### HTLC secrets

The number of secret hashes in an HTLC order sets the maximum number of executions. Each execution uses one hash. Omniston currently supports only one execution per HTLC order. Thus, `secretHashes` must contain one hash.

For trader-provided HTLC secrets, the data flow is:

1. Put the 32-byte secret hash in `secretHashes`. Use the quote's `htlcHashingFunction` to calculate the hash.
2. When you build the transaction or order payload, pass these hashes in `htlcSecrets.secretMode` with `$case: "provided"`.
3. Keep the original secrets with the accepted `quoteId` and their execution indices. The order contains the hashes. Keep the original secrets private until disclosure.
4. During `orderTrack()`, check the input position phase and output position phase for each execution. When the two positions meet the disclosure conditions, disclose that execution's original secret with `orderDiscloseHtlcSecret()`. See [HTLC secret disclosure](#htlc-secret-disclosure) for the necessary phases.

## Track trade status

After transaction broadcast or signed order registration, select the tracking method for the accepted quote's settlement method:

* For `swap`, use `swapTrack()`.
* For `order`, use `orderTrack()`.

The two methods give updates on the trade's progress and result.

### Track swap status

Pass these values to `swapTrack()`:

* The `quoteId` of the accepted swap `quote`.
* The trader address that you passed to `tonBuildSwap()`.
* The `outgoingTxQuery` identifier from the swap transaction submission.

```ts
const swapTrackSubscription = omniston.swapTrack({
  quoteId: quote.quoteId,
  traderAddress,
  outgoingTxQuery,
}).subscribe({
  next(swapEvent) {
    switch (swapEvent.$case) {
      case "awaitingTransfer":
        console.log("Waiting for the source transfer");
        break;
      case "progress":
        console.log("Swap status:", swapEvent.value.status);
        console.log("Routes:", swapEvent.value.routes);
        break;
      case "unsubscribed":
        console.log("Swap tracking closed by server");
        break;
    }
  },
  error: (error) => console.error("Swap tracking failed", error),
});
```

`progress` includes the aggregate trade status and route, step, and chunk results.

### Track order status

Use the source-side `traderAddress` that you passed as `ownerSrcAddress` in the selected order branch. TON-funded orders and signed orders from EVM or TRON use `orderTrack()`:

```ts
const orderTrackSubscription = omniston.orderTrack({
  quoteId: quote.quoteId,
  traderAddress,
}).subscribe({
  next(orderEvent) {
    switch (orderEvent.$case) {
      case "order":
        console.log("Order status:", orderEvent.value.status);
        console.log("Executions:", orderEvent.value.executions);
        // For HTLC orders, check execution phases before disclosing the secret.
        break;
      case "unsubscribed":
        console.log("Order tracking closed by server");
        break;
    }
  },
  error: (error) => console.error("Order tracking failed", error),
});
```

The primary outcomes in `Order.status` are:

* `TRADE_STATUS_FULLY_FILLED`
* `TRADE_STATUS_PARTIALLY_FILLED`
* `TRADE_STATUS_CANCELLED`
* `TRADE_STATUS_FAILED`

See [Reading completion in order settlement](/developer-section/omniston/how-omniston-works.md#reading-completion-in-order-settlement).

### HTLC secret disclosure

For HTLC orders, examine the executions in each `order` update from `orderTrack()`. Before disclosure, check the execution's input position phase and output position phase. The two phases must meet the conditions in [When to call `orderDiscloseHtlcSecret()`](/developer-section/omniston/how-omniston-works.md#when-to-call-orderdisclosehtlcsecret).

For each `execution` that meets the two conditions, use its `index` as `executionIndex`. Use the original `secret` whose hash you supplied for that execution when you built the order. Call `orderDiscloseHtlcSecret()` with these values:

```ts
await omniston.orderDiscloseHtlcSecret({
  quoteId: quote.quoteId,
  executionIndex: execution.index,
  secret,
});
```

Continue to receive `orderTrack()` updates to monitor the trade result. The disclosure response acknowledges the secret. It does not show that the trade is completed.

## Active orders and recovery

`orderGetActive()` returns a snapshot of active orders for a source-side trader address. Use it after a process restart or to update an active-order list. For periodic updates, poll `orderGetActive()`.

```ts
const { activeOrders } = await omniston.orderGetActive({ traderAddress });

for (const activeOrder of activeOrders) {
  console.log("Quote:", activeOrder.quote.quoteId);
  console.log("Status:", activeOrder.order.status);

  // Resume orderTrack() for orders your application needs to monitor.
}
```

Restore the secrets for trader-managed HTLC orders from your own storage. This snapshot does not recover secret preimages.

## Cleanup

When pricing or tracking updates are no longer necessary, unsubscribe from the applicable stream. For tracking streams, this stops observation. It does not cancel the on-chain trade or order.

```ts
connectionSubscription.unsubscribe();
omniston.transport.close();
```

Keep a shared client open while other requests or subscriptions still use it.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.ston.fi/developer-section/omniston/sdk/nodejs.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
