# React

### Getting started

This package provides binding for [omniston-sdk](https://github.com/ston-fi/omniston-sdk/tree/main/packages/omniston-sdk) to the React ecosystem. Using this package, you can access all Omniston methods as hooks powered by [TanStack react query](https://tanstack.com/query/latest) (out-of-box loading states, retries, error handling, and match more)

📦 **NPM Package**: [@ston-fi/omniston-sdk-react](https://www.npmjs.com/package/@ston-fi/omniston-sdk-react)

Take a look onto our [demo app](https://github.com/ston-fi/omniston-sdk/tree/main/examples/next-js-app) that use NextJs and `omniston-sdk-react` package

### Installation

#### via NPM

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

#### via YARN

```sh
yarn add @ston-fi/omniston-sdk-react
```

#### via PNPM

```sh
pnpm install @ston-fi/omniston-sdk-react
```

### Wrap you app in Omniston provider

```tsx
import { Omniston, OmnistonProvider } from "@ston-fi/omniston-sdk-react";

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

const App = () => (
  <OmnistonProvider omniston={omniston}>{children}</OmnistonProvider>
);
```

The provider takes the following parameters:

| Name       | Type       | Description                                                       |
| ---------- | ---------- | ----------------------------------------------------------------- |
| `omniston` | `Omniston` | A pre-instantiated Omniston instance configured with your API URL |

#### Sandbox example (testing)

If you are developing or testing your integration, use the public sandbox environment instead of production.

```tsx
import { Omniston, OmnistonProvider } from "@ston-fi/omniston-sdk-react";

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

const App = () => (
  <OmnistonProvider omniston={omniston}>{children}</OmnistonProvider>
);
```

> Sandbox is intended for development and integration testing only. Do not use sandbox in production applications.

#### Custom Transport Configuration

You can now configure a custom `Transport` for more precise control over the WebSocket API connection:

```tsx
import { Omniston, OmnistonProvider, WebSocketTransport } from "@ston-fi/omniston-sdk-react";

const omnistonApiUrl = "wss://omni-ws.ston.fi";
const omnistonTransport = new WebSocketTransport(omnistonApiUrl);

const omniston = new Omniston({
  apiUrl: omnistonApiUrl,
  transport: omnistonTransport,
});

const App = () => (
  <OmnistonProvider omniston={omniston}>{children}</OmnistonProvider>
);

// You can control the transport connection:
// omnistonTransport.close();
// omnistonTransport.connect();
// omnistonTransport.connectionStatusEvents.subscribe(({ status }) => { /* ... */ })
```

### Send a quote request

Send a request for quote to swap an asset to another asset.

```tsx
import { useRfq, GaslessSettlement } from "@ston-fi/omniston-sdk-react";

const {
  isLoading,
  error,
  data: quoteResponse,
  ...restQuery
} = useRfq({
  settlementMethods: [SettlementMethod.SETTLEMENT_METHOD_SWAP],
  askAssetAddress: {
    blockchain: Blockchain.TON,
    address: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  },
  bidAssetAddress: {
    blockchain: Blockchain.TON,
    address: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", // USDT
  },
  amount: {
    bidUnits: "1000000", // 1 USDT
  },
  settlementParams: {
    maxPriceSlippageBps: 0,
    gaslessSettlement: GaslessSettlement.GASLESS_SETTLEMENT_POSSIBLE,
    maxOutgoingMessages: 4, // default for TON
    flexibleReferrerFee: true, // allow lower referrer fee if resolver improves the quote
  },
});

// Handle different response types
if (quoteResponse) {
  switch (quoteResponse.type) {
    case 'ack':
      console.log(`Quote request acknowledged with ID: ${quoteResponse.rfqId}`);
      break;
    case 'quoteUpdated':
      console.log(`Quote updated for request ${quoteResponse.rfqId}`);
      // Use quoteResponse.quote for the actual quote data
      break;
  }
}
```

A `QuoteRequest` has the following properties:

| Name                | Type                                           | Description                                                                              |
| ------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `settlementMethods` | `SettlementMethod[]`                           | Supported methods of swap settlement                                                     |
| `askAssetAddress`   | `Address`                                      | Blockchain-specific address of ask asset                                                 |
| `bidAssetAddress`   | `Address`                                      | Blockchain-specific address of bid asset                                                 |
| `amount`            | `{ bidUnits: string } \| { askUnits: string }` | Either the amount of bid asset or ask asset                                              |
| `amount.bidUnits`   | `string`                                       | The amount of bid asset the trader wants to pay, including all fees, in base asset units |
| `amount.askUnits`   | `string`                                       | The amount of ask asset the trader wants to get after all fees, in base asset units      |
| `referrerAddress`   | `Address \| undefined`                         | The address of referrer that will receive the fees                                       |
| `referrerFeeBps`    | `number \| undefined`                          | The amount of fees required by the referrer in basis points (1/10000 = 0.01%)            |
| `settlementParams`  | `RequestSettlementParams \| undefined`         | Additional parameters of RFQ related to settlement. See the table below.                 |

**RequestSettlementParams**

| Name                     | Type                           | Description                                                                                                                                                                                                                                                                                                |
| ------------------------ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `max_price_slippage_bps` | number \| undefined            | Maximum price slippage, in basis points (0.01%). For example, 100 = 1% slippage. Set to 0 to use recommended slippage.                                                                                                                                                                                     |
| `gasless_settlement`     | GaslessSettlement \| undefined | Gasless settlement preference. Options: `GASLESS_SETTLEMENT_PROHIBITED` (no gasless), `GASLESS_SETTLEMENT_POSSIBLE` (allow gasless), `GASLESS_SETTLEMENT_REQUIRED` (require gasless).                                                                                                                      |
| `max_outgoing_messages`  | number \| undefined            | Maximum number of outgoing messages supported by the wallet. For TON blockchain, this defaults to 4 if omitted.                                                                                                                                                                                            |
| `flexible_referrer_fee`  | boolean \| undefined           | Set to `true` to let resolvers lower the effective referrer fee below `referrerFeeBps` when offering a better rate. Defaults to `false`. In SDKs, pass this as `flexibleReferrerFee`. See [Flexible Referrer Fee](https://docs.ston.fi/developer-section/referral-fees#flexible-referrer-fee) for details. |

**Note**: Some parameters in `RequestSettlementParams` may only be relevant for certain blockchains or certain settlement methods. If your settlement method or wallet does not require them, you can omit them or set them to default values.

**Gasless Settlement Options**:

* `GASLESS_SETTLEMENT_PROHIBITED`: Disables gasless settlement completely
* `GASLESS_SETTLEMENT_POSSIBLE`: Allows gasless settlement if available (recommended)
* `GASLESS_SETTLEMENT_REQUIRED`: Forces gasless settlement (will fail if not available)

The server returns `Observable<QuoteResponseEvent>`, which is a stream of quote updates.

A `QuoteResponseEvent` might be one of the following:

* `{ type: "ack"; rfqId: string; }` - Acknowledgment that the quote request was received
* `{ type: "quoteUpdated"; quote: Quote; rfqId: string; }` - A quote update (includes rfqId after acknowledgment)
* `{ type: "noQuote"; }`
* `{ type: "unsubscribed"; rfqId: string; }` - Unsubscribed from the quote stream (includes rfqId after acknowledgment)

A `Quote` has the following properties:

| Name                      | Type                   | Description                                                                                 |
| ------------------------- | ---------------------- | ------------------------------------------------------------------------------------------- |
| `quoteId`                 | `string`               | ID of the quote generated by the platform (32 characters)                                   |
| `resolverId`              | `string`               | ID of the resolver                                                                          |
| `resolverName`            | `string`               | Name of the resolver                                                                        |
| `bidAssetAddress`         | `Address`              | Blockchain-specific address of bid asset                                                    |
| `askAssetAddress`         | `Address`              | Blockchain-specific address of ask asset                                                    |
| `bidUnits`                | `string`               | The amount of bid asset the trader must pay, including all fees                             |
| `askUnits`                | `string`               | The amount of ask asset the trader will get after all fees                                  |
| `referrerAddress`         | `Address \| undefined` | The address of referrer that will receive the fees                                          |
| `referrerFeeAsset`        | `Address`              | The asset of the fees that the referrer will get                                            |
| `referrerFeeUnits`        | `string`               | The amount of fees that the referrer will get (in units of `referrerFeeAsset`)              |
| `protocolFeeAsset`        | `Address`              | The asset of the fees charged by the protocol                                               |
| `protocolFeeUnits`        | `string`               | The amount of fees charged by the protocol (in units of `protocolFeeAsset`).                |
| `quoteTimestamp`          | `number`               | The timestamp (UTC seconds) of Quote sent by resolver                                       |
| `tradeStartDeadline`      | `number`               | Max timestamp (UTC seconds) of start of the trade                                           |
| `gasBudget`               | `string`               | The amount of gas budget for the trade                                                      |
| `estimatedGasConsumption` | `string`               | Estimated amount of gas units that will be spent to perform the trade                       |
| `params`                  | `object \| undefined`  | Various parameters specific to the settlement method. See the source code for more details. |

### Build a transaction

Now that we have a quote, we should request a server to build a transaction to initiate the trade that the user can verify and sign.

```tsx
import { useOmniston } from "@ston-fi/omniston-sdk-react";

const omniston = useOmniston();

const tx = await omniston.buildTransfer({
  quote,
  sourceAddress: {
    blockchain: Blockchain.TON,
    address: "", // sender wallet address on `offerBlockchain`
  },
  destinationAddress: {
    blockchain: Blockchain.TON,
    address: "", // receiver wallet address on `askBlockchain`
  },
  gasExcessAddress: {
    blockchain: Blockchain.TON,
    address: "", // the address that will receive the gas not spent by the trade
  },
  useRecommendedSlippage: true, // Use recommended slippage from the quote
});

const messages = tx.ton?.messages ?? [];
```

The `buildTransfer` method takes a `TransactionRequest` object as a parameter, having the following properties:

| Name                     | Type                   | Description                                                                            |
| ------------------------ | ---------------------- | -------------------------------------------------------------------------------------- |
| `quote`                  | `Quote`                | The valid quote received from `Omniston.requestForQuote`                               |
| `sourceAddress`          | `Address`              | The address on `offerBlockchain` that will send initial transaction to start the trade |
| `destinationAddress`     | `Address`              | The address on `askBlockchain` that will receive result of the trade                   |
| `gasExcessAddress`       | `Address \| undefined` | The address that will receive the gas not spent by the trade                           |
| `refundAddress`          | `Address \| undefined` | The address to which funds will be returned in case of an failure                      |
| `useRecommendedSlippage` | `boolean \| undefined` | Whether to use the recommended slippage from the quote. Defaults to false.             |

### Sign the transaction

You can found the instruction on how to send transaction using the `@tonconnect/ui-react` package [here](https://github.com/ton-connect/sdk/tree/main/packages/ui#send-transaction).

```tsx
import { useTonConnectUI } from "@tonconnect/ui-react";

const [tonConnect] = useTonConnectUI();

await tonConnect.sendTransaction({
  validUntil: Date.now() + 1000000,
  messages: messages.map((message) => ({
    address: message.targetAddress,
    amount: message.sendAmount,
    payload: message.payload,
  })),
});
```

### Get the outgoing transaction hash

See [TON Cookbook](https://docs.ton.org/develop/dapps/cookbook#how-to-find-transaction-for-a-certain-ton-connect-result) for detailed instructions.

For tutorial purposes, we'll use the `@ton/core` package to get the external transaction hash and find the internal hash with TonAPI v2.

```tsx
import { Cell } from "@ton/core";

const externalTxHash = Cell.fromBase64(boc).hash().toString("hex");
const response = await fetch(`https://tonapi.io/v2/traces/${externalTxHash}`);
const outgoingTxHash = (await response.json()).transaction.hash;
```

### Listen for trade status updates

After the user has signed and initiated the transaction, we can track the trade status.

```tsx
import { useTrackTrade } from "@ston-fi/omniston-sdk-react";
import { useTonAddress } from "@tonconnect/ui-react";

const walletAddress = useTonAddress();

const {
  isLoading,
  error,
  data: status,
  ...restQuery
} = useTrackTrade({
  quoteId: quote.quoteId,
  traderWalletAddress: {
    blockchain: Blockchain.TON,
    address: walletAddress,
  },
  outgoingTxHash: "", // Replace with the actual outgoingTxHash
});
```

The `trackTrade` method has the following parameters:

| Name                  | Type      | Description                                               |
| --------------------- | --------- | --------------------------------------------------------- |
| `quoteId`             | `string`  | ID of the quote received from `Omniston.requestFromQuote` |
| `traderWalletAddress` | `Address` | The address of trader's wallet that initiated transaction |
| `outgoingTxHash`      | `string`  | Hash of the transaction that initiated the trade          |

It returns `Observable<TradeStatus>`. For the different trade status values, see the source code. We are interested in the trade result enum which can be read from `status.tradeSettled?.result?` field. The enum has the following values:

| Name                            | Description                                                |
| ------------------------------- | ---------------------------------------------------------- |
| `TRADE_RESULT_FULLY_FILLED`     | The trade has been completed and fully filled.             |
| `TRADE_RESULT_PARTIALLY_FILLED` | The trade has been partially filled. Something went wrong. |
| `TRADE_RESULT_ABORTED`          | The trade has been aborted.                                |
| `TRADE_RESULT_UNKNOWN`          |                                                            |
| `UNRECOGNIZED`                  |                                                            |
