LogoLogo
ston-fi/docs
ston-fi/docs
  • User section
    • About
    • STON.fi Protocol
    • Fees
    • Glossary
    • Procedure for Adding Tokens to the Default List
    • Whitepaper
  • Developer section
    • Architecture
    • SDK
      • DEX v1 guide
        • reference
        • swap
        • provide liquidity
        • refund liquidity
        • burn liquidity tokens
      • DEX v2 guide
        • swap
        • provide liquidity
        • refund liquidity
        • burn liquidity tokens
        • withdraw fee from vault
      • Farm guide
        • stake in farm
        • claim farm rewards
        • unstake from farm
        • destroy farm NFT
      • Transaction sending guide
        • via ton
        • via tonweb
        • via tonconnect
      • v0.5 > v1.0.0 migration guide
      • v0.5 (deprecated)
        • DEX guide
          • swap
          • provide liquidity
          • refund liquidity
          • burn liquidity tokens
        • Farm guide
          • stake in farm
          • claim farm rewards
          • unstake from farm
          • destroy farm NFT
        • Transaction sending guide
          • via ton
          • via tonweb
          • via tonconnect
      • v0.4 > v0.5 migration guide
      • v0.4 (deprecated)
        • perform a swap operation
        • provide liquidity
        • refund liquidity
        • burn liquidity tokens
        • using get methods
        • create a custom router revision
    • API reference v1
      • Router
      • Pool
      • LpAccount
      • LpWallet
    • API reference v2
      • Router
      • Pool
      • LpAccount
      • LpWallet
      • Vault
      • Swap examples
      • LpProvide examples
      • Vault examples
      • Op Codes
    • DEX API
    • OMNISTON
      • Resolvers (How to become a resolver)
      • Swap overview
      • Swap extra
      • Swap grpc
      • React
      • Nodejs
      • Referral fees
    • Quickstart Guides
      • Swap Guide
      • Omniston Guide
  • Help
    • Contact Us
Powered by GitBook
On this page
  • Table of Contents
  • 1. Introduction
  • 2. Setting Up the Project
  • 2.1 Create a React App
  • 2.2 Installing the Required Packages
  • 3. Connecting the Wallet
  • 3.1 Add nessary providers
  • 3.2 Create the TonConnect Manifest
  • 3.3 Add the Connect Wallet Button
  • 4. Fetching Available Assets
  • 5. Requesting a Quote
  • 6. Building a Transaction and Sending It
  • 7. Tracking Your Trade
  • 7.1 Install the TON Package
  • 7.2 Using the useTrackTrade Hook
  • 8. Testing Your Swap
  • 9. Conclusion
  • 10. Live Demo
  • 11. Advanced Example App
Export as PDF
  1. Developer section
  2. Quickstart Guides

Omniston Guide

PreviousSwap GuideNextContact Us

Last updated 6 days ago

This guide will walk you through creating a basic token swap app using the Omniston protocol to swap assets across different DEXes (STON.fi V1, STON.fi V2, DeDust, etc.). We'll integrate wallet connectivity with TonConnect (via @tonconnect/ui-react) to allow users to connect their TON wallet and perform a swap. The guide is beginner-friendly and assumes minimal React experience.

Note: In this demo, we will leverage Tailwind CSS for styling instead of using custom CSS. The setup for Tailwind CSS is already included in the instructions below, so you don't need to set it up separately.

Note: You can use any package manager (npm, yarn, pnpm, or bun) to set up your React project. In this tutorial, we'll demonstrate with pnpm.


Table of Contents


1. Introduction

In this quickstart, we will build a minimal React app to:

  • Connect to a TON wallet (via TonConnect UI).

  • Fetch available tokens from STON.fi and display them in dropdowns.

  • Request a quote (RFQ) from Omniston for the best swap route (no separate "Simulate" step needed; Omniston fetches quotes automatically).

  • Build and execute a swap transaction across multiple DEXes.

  • Track the trade status until completion.

Currently, Omniston is in beta and enforces a swap limit of up to $1k to reduce potential losses in case of unforeseen issues.

We will use:

  • @ston-fi/omniston-sdk-react – React hooks to interact with Omniston (request quotes, track trades, etc.).

  • @ston-fi/api – Fetch token lists from STON.fi (and potentially other data).

  • @tonconnect/ui-react – Provides a React-based TON wallet connect button and utilities.

  • @ton/core – TON low-level library used for advanced functionality.


2. Setting Up the Project

2.1 Create a React App

First, let's check if pnpm is installed on your system:

pnpm --version

If you see a version number (like 10.4.0), pnpm is installed. If you get an error, you'll need to install pnpm first:

npm install -g pnpm

Now we'll create a new React project using Vite. However, you can use any React setup you prefer (Next.js, CRA, etc.).

Run the following command to create a new Vite-based React project:

pnpm create vite --template react

When prompted, type your desired project name (e.g., omniston-swap-app):

Project name: » omniston-swap-app

Then enter the folder:

cd omniston-swap-app

2.2 Installing the Required Packages

Within your new React project directory, install the Omniston SDK, TonConnect UI, the TON core library, and STON.fi API library:

pnpm add @ston-fi/omniston-sdk-react @tonconnect/ui-react @ton/core @ston-fi/api

Next, install Tailwind CSS and its Vite plugin:

pnpm add tailwindcss @tailwindcss/vite

Additionally, install the Node.js polyfills plugin for Vite, which is necessary to provide Buffer and other Node.js APIs in the browser environment (required by TON libraries):

pnpm add vite-plugin-node-polyfills

Configure the Vite plugin by updating vite.config.js file:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(), tailwindcss(), nodePolyfills()],
})

Then, import Tailwind CSS in your main CSS file. Open src/index.css and replace any existing code with:

@import "tailwindcss";

You can also remove src/App.css (we don't need it), and remove the import statement import './App.css' from src/App.jsx.

After making these changes, you can verify that your app still runs correctly by starting the development server:

pnpm install
pnpm dev

This should launch your app in development mode, typically at http://localhost:5173. You should see the Vite + React logo and text on a plain white background. Since we've removed the default styling (App.css), the page will look simpler than the default template.

If you see the logo and text, it means your Vite + React setup is working correctly. Make sure everything loads without errors before proceeding to the next step.


3. Connecting the Wallet

3.1 Add nessary providers

Open src/main.jsx (Vite's default entry point) and wrap your application with both the TonConnectUIProvider and OmnistonProvider. The TonConnectUIProvider makes the TonConnect context available to your app for wallet connectivity, while the OmnistonProvider enables Omniston's functionality throughout your application. Also, point the TonConnect provider to a manifest file (which we will create next) that describes your app to wallets.

// src/main.jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { TonConnectUIProvider } from '@tonconnect/ui-react';
import { OmnistonProvider } from '@ston-fi/omniston-sdk-react';
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <TonConnectUIProvider manifestUrl={`${window.location.origin}/tonconnect-manifest.json`}>
      <OmnistonProvider apiUrl="wss://omni-ws.ston.fi">
        <App />
      </OmnistonProvider>
    </TonConnectUIProvider>
  </StrictMode>,
)

3.2 Create the TonConnect Manifest

In the public folder of your project, create a file named tonconnect-manifest.json. This manifest provides wallet apps with information about your application (like name and icon). You should customize this manifest for your own application. Here's an example:

{
    "url": "https://ton.vote",
    "name": "TON Vote",
    "iconUrl": "https://ton.vote/logo.png"
}

Make sure to update these fields for your application:

  • url: The base URL where your app is served

  • name: Your application's display name (this is what wallets will show to users)

  • iconUrl: A link to your app's icon (should be a 180×180 PNG image)

Make sure this file is accessible. When the dev server runs, you should be able to fetch it in your browser at http://localhost:5173/tonconnect-manifest.json.


3.3 Add the Connect Wallet Button

In your main App component (e.g., src/App.jsx), import and include the TonConnectButton. For example:

// src/App.jsx
import { TonConnectButton } from '@tonconnect/ui-react';

function App() {
  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-4">
      <h1 className="text-2xl font-bold mb-4">Omniston Swap Demo</h1>
      <TonConnectButton />
    </div>
  );
}

export default App;

4. Fetching Available Assets

Next, let's dynamically retrieve the list of tokens (assets) that can be swapped on STON.fi. We use the STON.fi API client (@ston-fi/api) for this. Here's a simplified example that filters assets by liquidity (high to medium). We'll store them in state and present them in From/To dropdowns.

// src/App.jsx
import { useEffect, useState } from 'react';
import { StonApiClient, AssetTag } from '@ston-fi/api';
import { TonConnectButton } from '@tonconnect/ui-react';

function App() {
  const [assets, setAssets] = useState([]);
  const [fromAsset, setFromAsset] = useState(null);
  const [toAsset, setToAsset] = useState(null);
  const [amount, setAmount] = useState('');

  // fetch assets on mount
  useEffect(() => {
    const fetchAssets = async () => {
      try {
        const client = new StonApiClient();
        // Filter out top liquidity tokens for brevity
        const condition = [
          AssetTag.LiquidityVeryHigh,
          AssetTag.LiquidityHigh,
          AssetTag.LiquidityMedium
        ].join(' | ');
        const assetList = await client.queryAssets({ condition });

        setAssets(assetList);
        if (assetList.length > 0) {
          setFromAsset(assetList[0]);
        }
        if (assetList.length > 1) {
          setToAsset(assetList[1]);
        }
      } catch (err) {
        console.error('Failed to fetch assets:', err);
      }
    };
    fetchAssets();
  }, []);

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-blue-50 to-indigo-100 p-6">
      <div className="w-full max-w-md bg-white rounded-xl shadow-lg p-6 space-y-6">
        <div className="flex justify-between items-center">
          <h1 className="text-3xl font-bold text-indigo-700">Omniston Swap</h1>
          <TonConnectButton />
        </div>

        <div className="h-px bg-gray-200 w-full my-4"></div>

        {assets.length > 0 ? (
          <div className="space-y-6">
            {/* From */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                From
              </label>
              <select
                value={fromAsset?.contractAddress || ''}
                onChange={(e) => {
                  const selected = assets.find(a => a.contractAddress === e.target.value);
                  setFromAsset(selected);
                }}
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              >
                {assets.map(asset => (
                  <option
                    key={asset.contractAddress}
                    value={asset.contractAddress}
                  >
                    {asset.meta?.symbol || asset.meta?.displayName || 'token'}
                  </option>
                ))}
              </select>
            </div>

            {/* To */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                To
              </label>
              <select
                value={toAsset?.contractAddress || ''}
                onChange={(e) => {
                  const selected = assets.find(a => a.contractAddress === e.target.value);
                  setToAsset(selected);
                }}
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              >
                {assets.map(asset => (
                  <option
                    key={asset.contractAddress}
                    value={asset.contractAddress}
                  >
                    {asset.meta?.symbol || asset.meta?.displayName || 'token'}
                  </option>
                ))}
              </select>
            </div>

            {/* Amount */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                Amount
              </label>
              <input
                type="text"
                value={amount}
                onChange={(e) => setAmount(e.target.value)}
                placeholder="0.0"
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              />
            </div>
          </div>
        ) : (
          <div className="flex justify-center items-center py-10">
            <div className="animate-pulse flex space-x-2">
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
            </div>
            <p className="ml-3 text-gray-600">Loading assets...</p>
          </div>
        )}
      </div>

      <div className="mt-6 text-center text-xs text-gray-500">
        Powered by Ston.fi
      </div>
    </div>
  );
}

export default App;

5. Requesting a Quote

We'll use the useRfq hook from @ston-fi/omniston-sdk-react to request a quote. It will fetch quotes automatically based on the parameters given. The code below extends our existing component by calling useRfq with the user's "From" token, "To" token, and input amount.

// Step #5 changes in App.jsx
// keep all imports from step #4 and add
import { useRfq, SettlementMethod, Blockchain } from "@ston-fi/omniston-sdk-react";

function App() {
  // keep existing state from step #4 and useEffect with fetchAssets

  // Helper to get decimals from asset with default fallback
  const getDecimals = asset => asset?.meta?.decimals ?? 9;
  
  // Helper to calculate conversion factor based on decimals
  const getConversionFactor = asset => 10 ** getDecimals(asset);

  function toBaseUnits(asset, amt) {
    if (!asset || !amt) return '0';
    return Math.floor(parseFloat(amt) * getConversionFactor(asset)).toString();
  }

  function fromBaseUnits(asset, baseUnits) {
    if (!asset || !baseUnits) return '0';
    return (parseInt(baseUnits) / getConversionFactor(asset)).toFixed(2);
  }

  const { data: quote, isLoading: quoteLoading, error: quoteError } = useRfq({
    settlementMethods: [SettlementMethod.SETTLEMENT_METHOD_SWAP],
    offerAssetAddress: fromAsset
      ? { blockchain: Blockchain.TON, address: fromAsset.contractAddress }
      : undefined,
    askAssetAddress: toAsset
      ? { blockchain: Blockchain.TON, address: toAsset.contractAddress }
      : undefined,
    amount: {
      offerUnits: fromAsset ? toBaseUnits(fromAsset, amount) : '0'
    },
    settlementParams: {
      // example: allow up to 5% slippage, max 4 outgoing messages
      maxPriceSlippageBps: 500,
      maxOutgoingMessages: 4,
    },
  }, {
    enabled: !!fromAsset?.contractAddress && !!toAsset?.contractAddress && amount !== ''
  });

  return (
      // keep the same JSX wrapper from step #4
        {/* Add quote section */}
        <div className="pt-4">
          {quoteLoading && <p>Loading quote...</p>}
          {quoteError && <p className="text-red-500">Error: {String(quoteError)}</p>}
          {quote && (
            <div className="p-4 bg-gray-50 rounded-lg border border-gray-200">
              <p className="font-semibold text-gray-700">Quote Info</p>
              <p className="text-sm text-gray-600">Resolver: {quote.quote.resolverName}</p>
              <p className="text-sm text-gray-600">Offer Units: {fromBaseUnits(fromAsset, quote.quote.offerUnits)}  {fromAsset.meta?.symbol}</p>
              <p className="text-sm text-gray-600">Ask Units: {fromBaseUnits(toAsset, quote.quote.askUnits)} {toAsset.meta?.symbol}</p>
            </div>
          )}
        </div>
      </div>

      <div className="mt-6 text-center text-xs text-gray-500">
        Powered by Ston.fi
      </div>
    </div>
  );
}

export default App;

Any time the user changes the token or amount, useRfq automatically refreshes the quote.


6. Building a Transaction and Sending It

Once we have a quote, we can build the transaction that will execute the swap. We'll use the useOmniston hook to access the Omniston instance and build the transaction. Let's add a button that builds the transaction.

In App.jsx, near the top:

// Step #6 changes

// add extra imports:
import { TonConnectButton, useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";
import {
  useRfq,
  SettlementMethod,
  Blockchain,
  useOmniston,
} from "@ston-fi/omniston-sdk-react";

Inside the App component, after

const [amount, setAmount] = useState("");

add:

function App() {  
  // ... existing code ...
  const walletAddress = useTonAddress();
  const [tonConnect] = useTonConnectUI();

  const omniston = useOmniston();

  // add afeter useRfq hook
  async function buildTx(willTradedQuote) {
    if (!willTradedQuote || !walletAddress) {
      alert("Please connect your wallet and ensure a valid quote is loaded.");
      return null;
    }

    try {
      const tx = await omniston.buildTransfer({
        quote: willTradedQuote.quote,
        sourceAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // the wallet sending the offer token
        },
        destinationAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // the same wallet receiving the ask token
        },
      });

      return tx.ton?.messages || [];
    } catch (err) {
      console.error("Error building transaction:", err);
      alert("Failed to build transaction. Check console for details.");
      return null;
    }
  }

  async function handleSwap() {
    const willTradedQuote = quote;
    const messages = await buildTx(willTradedQuote);
    if (!messages) return;
    
    try {
      await tonConnect.sendTransaction({
        validUntil: Date.now() + 1000000,
        messages: messages.map((message) => ({
          address: message.targetAddress,
          amount: message.sendAmount,
          payload: message.payload,
        })),
      });
    } catch (err) {
      console.error("Error sending transaction:", err);
      alert("Failed to send transaction. Check console for details.");
    }
  }

  // in the render, near after quote section
  return (
    // ...
      <button
        onClick={handleSwap}
        className="mt-4 w-full bg-indigo-500 hover:bg-indigo-600 text-white font-medium py-3 px-4 rounded-lg transition-all"
      >
        Execute Swap
      </button>
    // ...
  );
}

7. Tracking Your Trade

7.1 Install the TON Package

We'll track trades using the useTrackTrade hook from @ston-fi/omniston-sdk-react. First, ensure you have the @ton/ton package installed if you haven't already:

pnpm add @ton/ton

7.2 Using the useTrackTrade Hook

After you've built and sent the swap transaction, you can track its status with useTrackTrade. This hook takes the quoteId of the trade, your wallet address, and the outgoing transaction hash. It periodically checks the trade's on-chain status, letting you know if it's pending, settled, or partially filled.

Here's an example snippet illustrating how to store the quote at trade time, capture the external BOC hash from the wallet transaction result, and then pass those values to useTrackTrade:

// src/App.jsx
// ... keep other imports ...
import { TonClient, Address, Cell, beginCell, storeMessage } from "@ton/ton";
import {
  useTrackTrade,
  // ... other imports ...
} from "@ston-fi/omniston-sdk-react";

function App() {
  // ... keep other state and hooks ...
   const [outgoingTxHash, setOutgoingTxHash] = useState("");
  const [tradedQuote, setTradedQuote] = useState(null);

  // Reset outgoingTxHash and tradedQuote when inputs change
  useEffect(() => {
    setTradedQuote("");
    setOutgoingTxHash("");
  }, [fromAsset, toAsset, amount]);



  const { isLoading, error, data: tradeStatus } = useTrackTrade({
    // ... current useRfq properties and values 
  }, {
      enabled:
        !!fromAsset?.contractAddress &&
        !!toAsset?.contractAddress &&
        amount !== "" &&
        // add this to stop getting new quoted when we make a transaction
        !outgoingTxHash,
  });


  const {
    isLoading: trackingLoading,
    error: trackingError,
    data: tradeStatus,
  } = useTrackTrade({
    quoteId: tradedQuote?.quote?.quoteId,
    traderWalletAddress: {
      blockchain: Blockchain.TON,
      address: walletAddress,
    },
    outgoingTxHash,
  }, {
    enabled: !!tradedQuote?.quote?.quoteId && !!walletAddress && !!outgoingTxHash,
  });

  // Now replace all other functions with new implementations

  // Function to translate trade result to human-readable text
  const getTradeResultText = (status) => {
    if (!status?.tradeSettled) return "";
    
    const result = status.tradeSettled.result;
    switch (result) {
      case "TRADE_RESULT_FULLY_FILLED":
        return "Trade completed successfully and fully filled";
      case "TRADE_RESULT_PARTIALLY_FILLED":
        return "Trade partially filled - something went wrong";
      case "TRADE_RESULT_ABORTED":
        return "Trade was aborted";
      case "TRADE_RESULT_UNKNOWN":
      case "UNRECOGNIZED":
      default:
        return "Unknown trade result";
    }
  };

  async function buildTx(willTradedQuote) {
    if (!willTradedQuote || !walletAddress) {
      alert("Please connect your wallet and ensure a valid quote is loaded.");
      return null;
    }

    try {
      const tx = await omniston.buildTransfer({
        quote: willTradedQuote.quote,
        sourceAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // the wallet sending the offer token
        },
        destinationAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // the same wallet receiving the ask token
        },
      });

      return tx.ton?.messages || [];
    } catch (err) {
      console.error("Error building transaction:", err);
      alert("Failed to build transaction. Check console for details.");
      return null;
    }
  }

  // Utility function to retry an async operation
const retry = async (fn, { retries = 5, delay = 1000 }) => {
  try {
    return await fn();
  } catch (error) {
    if (retries === 0) throw error;
    await new Promise(resolve => setTimeout(resolve, delay));
    return retry(fn, { retries: retries - 1, delay });
  }
};

const getTxByBOC = async (exBoc, walletAddress) => {
  if (!exBoc || !walletAddress) {
    throw new Error('Missing required parameters for transaction tracking');
  }

  const client = new TonClient({
    endpoint: 'https://toncenter.com/api/v2/jsonRPC'
  });

  const myAddress = Address.parse(walletAddress);

  return retry(async () => {
    const transactions = await client.getTransactions(myAddress, {
      limit: 5,
    });

    for (const tx of transactions) {
      const inMsg = tx.inMessage;
      if (inMsg?.info.type === 'external-in') {
        const inBOC = inMsg?.body;
        if (typeof inBOC === 'undefined') {
          continue;
        }

        const extHash = Cell.fromBase64(exBoc).hash().toString('hex');
        const inHash = beginCell().store(storeMessage(inMsg)).endCell().hash().toString('hex');

        if (extHash === inHash) {
          return tx.hash().toString('hex');
        }
      }
    }
    throw new Error('Transaction not found');
  }, { retries: 30, delay: 1000 });
}; 

  async function handleSwap() {
    const willTradedQuote = quote;
    const messages = await buildTx(willTradedQuote);
    if (!messages) return;
    
    try {
      // Store the quote at the time of trade
      setTradedQuote(willTradedQuote);
      
      const res = await tonConnect.sendTransaction({
        validUntil: Date.now() + 1000000,
        messages: messages.map((message) => ({
          address: message.targetAddress,
          amount: message.sendAmount,
          payload: message.payload,
        })),
      });

      const exBoc = res.boc;
      const txHash = await getTxByBOC(exBoc, walletAddress);
      setOutgoingTxHash(txHash);
    } catch (err) {
      setTradedQuote(null);
      console.error("Error sending transaction:", err);
      alert("Failed to send transaction. Check console for details.");
    }
  }



  return (
    // ...
    // in the render, after <div className="h-px bg-gray-200 w-full my-4"></div> add
    {/* Trade status */}
    <div className="mt-4 p-4 bg-gray-50 rounded-lg border border-gray-200">
      {trackingLoading && <p className="text-sm text-blue-600">Tracking trade...</p>}
      {trackingError && (
        <p className="text-sm text-orange-600">Trade tracking error: {String(trackingError)}</p>
      )}
      {tradeStatus?.status?.tradeSettled && (
        <p className="text-sm text-green-600">
          Trade Result: {getTradeResultText(tradeStatus.status)}
        </p>
      )}
    </div>
    )
}

8. Testing Your Swap

  1. Start the development server:

pnpm dev
  1. Open your app in the browser at http://localhost:5173.

  2. Connect your TON wallet via the "Connect Wallet" button.

  3. Select tokens from the dropdowns and enter an amount.

  4. Omniston automatically fetches and displays a quote. Confirm it's valid.

  5. Click "Execute Swap" to finalize the transaction. Approve it in your wallet.

  6. Once the swap completes on-chain, your wallet balances should update accordingly.


9. Conclusion

Congratulations! You've built a minimal React + Vite app with Tailwind CSS that:

  • Connects to a TON wallet using TonConnect.

  • Dynamically fetches available tokens from STON.fi.

  • Requests real-time quotes (RFQs) from Omniston automatically.

  • Builds and sends swap transactions.

Feel free to expand this demo with:

  • Custom slippage settings.

  • Better error-handling and success notifications.

  • Additional settlement methods or cross-chain logic.

Happy building with Omniston!

10. Live Demo

With this Replit demo, you can:

  • Open the project directly in your browser

  • Fork the Replit to make your own copy

  • Run the application to see it in action

  • Explore and modify the code to learn how it works

  • Experiment with different features and UI changes

Alternatively, you can run this example locally by cloning the GitHub repository:

git clone https://github.com/mrruby/omniston-swap-app.git
cd omniston-swap-app
pnpm install
pnpm dev

This will start the development server and you can access the app at http://localhost:5173.

11. Advanced Example App

For those seeking a feature-rich, more advanced approach, we also have a Next.js Omniston Demo App that:

  • Uses Next.js for a scalable framework

  • Utilizes hooks and providers for an elegant architecture

  • Demonstrates better error handling, robust state management, and additional STON.fi and Omniston features

You can explore the code in our repository:

Or see it in action at our live demo:

Learn how to add referral fees to your Omniston swaps by reading the .

Referral Fees guide
Omniston SDK Next.js Demo App
Omniston Demo App
Introduction
Setting Up the Project
Create a React App
Installing the Required Packages
Connecting the Wallet
Add nessary providers
Create the TonConnect Manifest
Add the Connect Wallet Button
Fetching Available Assets
Requesting a Quote
Building a Transaction
Tracking Your Trade
Install the TON Package
Using the useTrackTrade Hook
Testing Your Swap
Conclusion
Live Demo
Advanced Example App