Browser + TonConnect

End-to-end integration of chop into a React dapp using the official TonConnect kit. Twenty lines of glue code; no backend required on your side.

Install

bash
npm install @tonconnect/ui-react

1. Add a manifest

TonConnect needs a manifest at a public URL describing your app. Drop this in public/tonconnect-manifest.json on your dapp:

json
{
  "url": "https://your-dapp.com",
  "name": "Your dapp name",
  "iconUrl": "https://your-dapp.com/logo.png"
}

2. Wrap your app

app/layout.tsx
import { TonConnectUIProvider } from '@tonconnect/ui-react';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <TonConnectUIProvider
          manifestUrl="https://your-dapp.com/tonconnect-manifest.json"
        >
          {children}
        </TonConnectUIProvider>
      </body>
    </html>
  );
}

3. Connect button + swap call

components/Swap.tsx
'use client';
import {
  TonConnectButton,
  useTonAddress,
  useTonConnectUI,
} from '@tonconnect/ui-react';

export function Swap() {
  const address = useTonAddress();
  const [tonConnectUI] = useTonConnectUI();

  async function swap() {
    if (!address) {
      tonConnectUI.openModal();
      return;
    }
    const res = await fetch('https://chop.ag/api/v1/tx', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        assetIn: 'TON',
        assetOut: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs',
        amountIn: '1000000000', // 1 TON
        slippageBps: 100,
        userAddress: address,
      }),
    }).then((r) => r.json());

    await tonConnectUI.sendTransaction(res.tonConnect);
  }

  return (
    <>
      <TonConnectButton />
      <button onClick={swap}>Swap 1 TON for USDT</button>
    </>
  );
}

Quote-then-build

For a polished swap UI, hit /v1/quote on debounced input changes to render a live preview, then call /v1/tx only when the user clicks Swap. The TX endpoint re-quotes internally, so the slippage limits will be fresh:

ts
// On amount/token change (debounced ~300ms):
const { quotes, bestRoute } = await api.quote({ assetIn, assetOut, amountIn });

// On click:
const tx = await api.tx({ assetIn, assetOut, amountIn, userAddress });
await tonConnectUI.sendTransaction(tx.tonConnect);

Error handling

sendTransaction throws if the user cancels the wallet’s signing modal, or if the wallet rejects (insufficient balance, etc.). Wrap in try/catch and show a friendly message — the user is always one click away from the wallet sheet.

ts
try {
  await tonConnectUI.sendTransaction(tx.tonConnect);
} catch (err) {
  if (err.code === 300) /* user rejected */ return;
  toast.error('Swap failed: ' + err.message);
}

Verifying success

TonConnect resolves as soon as the wallet broadcasts — not when the swap settles. To know the user actually received output, poll their Jetton wallet balance after ~10 seconds and look for the delta. The chop API does not currently return a transaction hash; that’s on the wallet.

For the alternative server-side broadcast flow, see Server-side wallet.