DEV Community

Cover image for How to Display STON.fi Pool Data in a Web App

How to Display STON.fi Pool Data in a Web App

Build a small React and TypeScript dashboard that fetches STON.fi liquidity pools, resolves token metadata, formats reserves, and presents useful market data.

Displaying STON.fi pool data in a web app does not require reading TON smart contracts directly. For most dashboards, analytics pages, token explorers, and liquidity interfaces, the simplest route is the official STON.fi REST API or its TypeScript client, @ston-fi/api.

The API already organizes contract data into application-friendly structures. You can request pools, retrieve asset metadata, find pools for a specific token pair, and expose fields such as reserves, USD liquidity values, and 24-hour volume.

In this guide, we will build a small React interface that turns those responses into readable pool cards without adding wallet connections or transaction logic.

What we will build:

  • fetch the current STON.fi pool list
  • resolve token addresses into symbols
  • format reserves using each token's decimals
  • display liquidity and 24-hour volume
  • handle loading, errors, and stale data
  • keep the data layer ready for filtering or a larger dashboard

Why use the STON.fi API instead of reading contracts directly?

Why use the STON.fi API instead of reading contracts directly

A liquidity pool ultimately lives on-chain. STON.fi pool contracts contain reserves and other state, and the protocol documentation exposes contract getters such as get_pool_data. The V2 pool getter, for example, includes the LP token supply, both reserves, token wallet addresses, and fee-related fields.

You could query those contracts yourself. For a web interface whose job is simply to show pool information, however, that creates work you often do not need.

STON.fi provides a REST API specifically to organize protocol data for applications. Its current pool endpoints include:

GET  /v1/pools
GET  /v1/pools/{address}
GET  /v1/pools/by_market/{asset0}/{asset1}
POST /v1/pools/query
Enter fullscreen mode Exit fullscreen mode

The base URL is:

https://api.ston.fi
Enter fullscreen mode Exit fullscreen mode

The official TypeScript package wraps these endpoints with methods such as getPools(), getPool(), getPoolsByAssetPair(), and queryPools(). The current client also normalizes API response keys from snake_case to camelCase, which is convenient in a TypeScript frontend.

For a read-only pool dashboard, that is the layer we want.

Which pool fields are actually useful in a UI?

Which pool fields are actually useful in a UI?

The pool response contains considerably more information than most users need to see. The current STON.fi schema includes fields for the pool address, token addresses, reserves, LP supply, protocol fees, LP price, USD LP supply, recent APY values, 24-hour USD volume, router address, and parameters used by different pool types.

A compact market interface can start with just a few fields:

API value What it gives your UI
token0Address and token1Address Identifies the pool pair
reserve0 and reserve1 Current token reserves in base units
lpTotalSupplyUsd USD value associated with total LP supply
volume24hUsd Trading volume during the last 24 hours
address Pool contract address
deprecated Helps avoid presenting deprecated pools as normal active choices

There are two important display details here.

First, token addresses are not good labels. A visitor wants to see something like TON / USDT, not two long TON addresses. We therefore need the asset list as well as the pool list.

Second, reserves are raw blockchain quantities. Token metadata defines the number of decimals that must be applied to convert a base-unit balance into a human-readable amount. TON documentation explicitly warns applications to respect token decimals rather than assuming every asset uses the same value.

Set up the React project

A basic React and TypeScript application is enough for the example.

npm create vite@latest stonfi-pools -- --template react-ts
cd stonfi-pools
npm install
npm install @ston-fi/api
npm run dev
Enter fullscreen mode Exit fullscreen mode

For this dashboard, we do not need @ston-fi/sdk.

That distinction is useful. @ston-fi/api is the HTTP API client and is appropriate for retrieving application data. @ston-fi/sdk is aimed at interaction with STON.fi DEX contracts, including swaps and liquidity operations.

Create a small API module:

// src/stonfi.ts
import { StonApiClient } from "@ston-fi/api";

export const stonApi = new StonApiClient();
Enter fullscreen mode Exit fullscreen mode

The client requires no special configuration for the standard API endpoint. The package uses https://api.ston.fi as its default base URL.

Fetch pools and token metadata together

Fetch pools and token metadata together

Calling getPools() gives us the pool data, while getAssets() gives us the metadata required to turn token addresses into recognizable names and symbols.

Fetching both in parallel keeps the first render simple:

import { useEffect, useState } from "react";
import { stonApi } from "./stonfi";

type Pool = Awaited<
  ReturnType<typeof stonApi.getPools>
>[number];

type Asset = Awaited<
  ReturnType<typeof stonApi.getAssets>
>[number];

export default function App() {
  const [pools, setPools] = useState<Pool[]>([]);
  const [assets, setAssets] = useState<Asset[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let ignore = false;

    async function loadData() {
      try {
        setLoading(true);
        setError(null);

        const [poolList, assetList] = await Promise.all([
          stonApi.getPools(),
          stonApi.getAssets(),
        ]);

        if (ignore) return;

        setPools(poolList);
        setAssets(assetList);
      } catch (err) {
        if (!ignore) {
          setError(
            err instanceof Error
              ? err.message
              : "Unable to load STON.fi data"
          );
        }
      } finally {
        if (!ignore) {
          setLoading(false);
        }
      }
    }

    loadData();

    return () => {
      ignore = true;
    };
  }, []);

  if (loading) return <p>Loading STON.fi pools...</p>;
  if (error) return <p>Error: {error}</p>;

  return <p>Loaded {pools.length} pools.</p>;
}
Enter fullscreen mode Exit fullscreen mode

React documents useEffect as one way to synchronize a component with an external system, including manual data fetching. It also recommends cleanup logic so a response does not update a component after the relevant effect has been discarded.

For a production Next.js, Remix, or similar application, you may prefer the framework's server-side data fetching or a caching library. The STON.fi-specific part does not change.

Turn contract addresses into readable pool pairs

The asset response includes fields such as the contract address, symbol, display name, decimals, and price information. The STON.fi client converts the raw API keys into camelCase before returning them.

Build a lookup map once:

import { useMemo } from "react";

// inside App

const assetByAddress = useMemo(() => {
  return new Map(
    assets.map((asset) => [asset.contractAddress, asset])
  );
}, [assets]);
Enter fullscreen mode Exit fullscreen mode

Now each pool's token addresses can be resolved efficiently:

function getSymbol(
  address: string,
  assetByAddress: Map<string, Asset>
) {
  return assetByAddress.get(address)?.symbol ?? "Unknown";
}
Enter fullscreen mode Exit fullscreen mode

The same metadata lets us convert reserves into display values.

function formatUnits(value: string, decimals: number) {
  if (decimals === 0) return value;

  const padded = value.padStart(decimals + 1, "0");
  const whole = padded.slice(0, -decimals);

  const fraction = padded
    .slice(-decimals)
    .replace(/0+$/, "")
    .slice(0, 4);

  return fraction ? `${whole}.${fraction}` : whole;
}
Enter fullscreen mode Exit fullscreen mode

Do not silently assume nine decimals when metadata is unavailable. Nine is common on TON, but tokens can use another value. USDT, for example, uses six decimals.

For unknown assets, showing the raw amount or an explicit unavailable state is safer than presenting a confidently formatted but incorrect number.

Render the pool data

We can now turn the two API responses into a small dashboard.

Add a USD formatter:

const usd = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
});

function formatUsd(value?: string) {
  if (!value) return "N/A";

  const number = Number(value);

  return Number.isFinite(number)
    ? usd.format(number)
    : "N/A";
}
Enter fullscreen mode Exit fullscreen mode

Then render the pools:

return (
  <main>
    <h1>STON.fi Pools</h1>

    <div className="pool-grid">
      {pools
        .filter((pool) => !pool.deprecated)
        .sort(
          (a, b) =>
            Number(b.lpTotalSupplyUsd ?? 0) -
            Number(a.lpTotalSupplyUsd ?? 0)
        )
        .slice(0, 20)
        .map((pool) => {
          const token0 = assetByAddress.get(pool.token0Address);
          const token1 = assetByAddress.get(pool.token1Address);

          const reserve0 = token0
            ? formatUnits(pool.reserve0, token0.decimals)
            : `${pool.reserve0} base units`;

          const reserve1 = token1
            ? formatUnits(pool.reserve1, token1.decimals)
            : `${pool.reserve1} base units`;

          return (
            <article className="pool-card" key={pool.address}>
              <h2>
                {token0?.symbol ?? "Unknown"} /{" "}
                {token1?.symbol ?? "Unknown"}
              </h2>

              <p>
                <strong>Liquidity:</strong>{" "}
                {formatUsd(pool.lpTotalSupplyUsd)}
              </p>

              <p>
                <strong>24h volume:</strong>{" "}
                {formatUsd(pool.volume24hUsd)}
              </p>

              <p>
                <strong>Reserve 0:</strong>{" "}
                {reserve0} {token0?.symbol ?? ""}
              </p>

              <p>
                <strong>Reserve 1:</strong>{" "}
                {reserve1} {token1?.symbol ?? ""}
              </p>

              <small>
                Pool: {pool.address.slice(0, 10)}...
                {pool.address.slice(-6)}
              </small>
            </article>
          );
        })}
    </div>
  </main>
);
Enter fullscreen mode Exit fullscreen mode

At this point the app has moved from protocol-oriented data to UI-oriented data:

STON.fi API
    |
    +-- pools
    |    +-- reserves
    |    +-- liquidity value
    |    +-- volume
    |    +-- pool address
    |
    +-- assets
         +-- symbols
         +-- names
         +-- decimals
              |
              v
       React view model
              |
              v
       readable pool cards
Enter fullscreen mode Exit fullscreen mode

That transformation layer is worth keeping separate from the markup. It becomes much easier to add search, sorting, charts, or token filters later.

Fetch less data when the page becomes more specific

Fetch less data when the page becomes more specific

getPools() is a good starting point because it lets you explore the dataset. It should not automatically become the request used by every future screen.

If a page is about one pool, request one pool:

const pool = await stonApi.getPool(poolAddress);
Enter fullscreen mode Exit fullscreen mode

If the user has selected two assets, request pools for that pair:

const pools = await stonApi.getPoolsByAssetPair({
  asset0Address,
  asset1Address,
});
Enter fullscreen mode Exit fullscreen mode

For more advanced discovery, STON.fi also exposes queryPools(). The current client supports search terms, conditions, sorting, limits, wallet-specific context, and a DEX V2 option.

That gives you a useful progression:

  1. Use getPools() while building a general explorer.
  2. Move to pair-specific requests when the visitor selects assets.
  3. Use getPool() on detail pages.
  4. Use queryPools() when your interface needs server-side filtering or ranking.

The UI stays the same. Only the retrieval strategy becomes more precise.

Treat pool data as live application data

Treat pool data as live application data

A working first render is not the end of the integration. Pool reserves and trading volume change as swaps and liquidity operations occur.

Before shipping, decide how fresh the interface actually needs to be.

For a pool directory, refreshing every few minutes may be enough. A trading interface may need a different data path and much fresher quoting logic. Do not confuse a periodically refreshed pool dashboard with an executable swap quote.

A practical production checklist:

  • cache responses instead of requesting the complete pool list on every render
  • show a loading state during the initial request
  • preserve a clear error state when the API is unavailable
  • display when the data was last refreshed
  • remove or visually mark deprecated pools
  • never use rounded UI numbers as transaction inputs
  • keep original string values for financial calculations

The STON.fi documentation currently states that the DEX API has no rate limits, but that should not be treated as a reason to make wasteful requests. API behavior and operational limits can change, and caching makes the application faster regardless.

Also remember that the STON.fi REST API is intended for mainnet data. If your development workflow depends on testnet contract state, do not assume the same REST endpoint is a testnet indexer.

Common mistakes when displaying STON.fi pools

Common mistakes when displaying STON.fi pools

Showing raw addresses as the main identity. Addresses are essential identifiers, but they make poor primary labels. Resolve them through the asset metadata response and use addresses as secondary verification information.

Formatting every reserve with nine decimals. Token decimals belong to the token metadata. A wrong decimal assumption can produce a dramatically wrong displayed amount.

Calling getPools() after every state update. Pool data is external network data. Fetch it intentionally, cache it where appropriate, and separate filters that can run locally from filters that genuinely require another API request.

Treating lpTotalSupplyUsd as a universal definition of TVL without explanation. The API schema describes this field as the USD value of total LP token supply. If your product labels a metric "TVL," document exactly how your application defines it.

Mixing analytics values with transaction calculations. A rounded reserve or USD number that looks good in a card is not a substitute for a swap simulation or exact base-unit value.

For a useful first STON.fi integration, keep the exercise narrow: load pools, resolve their assets, inspect the exact API values in your browser, and compare the displayed reserves with the token decimals before adding more features. Once that layer is reliable, search, sorting, charts, pool detail pages, and wallet-specific liquidity views become incremental improvements rather than a rewrite.

Frequently Asked Questions

Can I display STON.fi pool data without connecting a wallet?

Yes. Public pool and asset information can be retrieved through the STON.fi REST API without asking the visitor to connect a wallet. A wallet becomes relevant when you want wallet-specific positions or when the application starts constructing transactions. A read-only pool explorer can remain completely independent of TonConnect.

Do I need @ston-fi/sdk to fetch liquidity pools?

No. For the workflow in this article, @ston-fi/api is the relevant package. It provides methods such as getPools(), getPool(), getAssets(), and getPoolsByAssetPair(). The separate DEX SDK is intended for interaction with protocol contracts and becomes useful when your application moves beyond displaying data.

What does GET /v1/pools return?

The endpoint returns the STON.fi pool collection. Pool records contain identifiers and state including pool and router addresses, token addresses, reserves, LP-related values, fee fields, recent APY fields, and 24-hour USD volume where available. The exact live schema should always be checked in the official Swagger interface.

How should I display token reserves correctly?

Retrieve the corresponding asset metadata and use its decimals value to convert the base-unit reserve into a human-readable amount. Do not assume that all tokens use the same number of decimals. Keep the original string or integer representation for calculations and perform formatting only at the presentation boundary.

Should I refresh pool data every second?

Usually not for a general pool dashboard. Choose a refresh interval that matches the purpose of the page, cache shared data, and avoid repeated full-list requests. A fast-changing execution interface should use purpose-built quote or simulation data rather than assuming that a periodically fetched pool card represents an executable price.

Can I request only pools for one token pair?

Yes. STON.fi exposes GET /v1/pools/by_market/{asset0}/{asset1}, and the TypeScript client wraps it with getPoolsByAssetPair(). That is preferable to loading every pool and filtering in the browser when the application already knows the two selected assets.

Can I show a user's STON.fi liquidity positions with the same API?

Yes. The REST API includes wallet-specific pool endpoints, and @ston-fi/api exposes getWalletPools() and getWalletPool(). That is a different UI state from the public pool directory because the response is enriched with information relevant to the specified wallet.

What should I verify before publishing a STON.fi pool dashboard?

Compare your field names against the current Swagger schema, verify token decimals, confirm that deprecated pools are handled correctly, and test missing optional values such as USD metrics. Also inspect the actual API response rather than copying an old example, since the STON.fi API and TypeScript client continue to evolve.

Sources and Further Reading

  • STON.fi REST API - Official overview of the REST interface, API viewers, and current usage notes
  • STON.fi API Reference - Official list of pool, asset, wallet, statistics, and other DEX endpoints
  • STON.fi Swagger UI - Current interactive REST API schema and response definitions
  • STON.fi API TypeScript client - Official source repository for @ston-fi/api, including pool and asset client methods
  • @ston-fi/api on npm - Package installation and TypeScript client usage examples
  • STON.fi V2 Pool contract reference - On-chain pool state and get_pool_data fields behind pool information
  • TON token metadata - Official explanation of token metadata and decimal handling for human-readable amounts
  • React useEffect reference - Official React guidance for synchronizing components with external systems and manual data fetching

Top comments (1)

Collapse
 
ivan_cryptovazimazima profile image
Ivan “Crypto Vazima” Zimanov

Hi. If you find an error in this article, please let me know in the comments so others reading it can benefit from your insights. Thank you for your support and assistance!