Skip to main content
T TON Adoption
Basics DEV · 2026

TON SDKs Compared: tonweb vs ton-core vs tonconnect-sdk (2026)

TypeScript SDKs for TON in 2026: tonweb (legacy), @ton/ton + @ton/core (recommended), @tonconnect/sdk. When to pick which and why.

Author
TON Adoption Team · research desk
Published
6 min read

TL;DR. By mid-2026 TON has three working TypeScript SDKs for distinct tasks: tonweb (old, legacy, not for new projects), @ton/ton + @ton/core (modern, recommended, fully typed and promise-based), @tonconnect/sdk + ui-react (for TON Connect 2.0 integration in dApps). A typical production project pairs @ton/ton (backend + on-chain logic) with @tonconnect/sdk (frontend user flow). Below — detailed comparison, scenario-based selection, and a full stack example.

Why SDK choice matters

You’re writing TypeScript/JavaScript and need to do one of:

  1. Read a balance or jetton metadata.
  2. Construct and send a transaction on behalf of a wallet.
  3. Connect a user’s TON wallet to your dApp.
  4. Parse Cell, BoC, message format.
  5. Sign data, verify signatures.
  6. Process an indexer webhook (new transactions).

Each of these has one or two “right” SDKs. Using the wrong one creates unnecessary pain.

Overview of the three main SDKs

1. tonweb — legacy

Origin: first widely used JS SDK for TON, developed 2020-2022 by the Tonkeeper team.

const TonWeb = require('tonweb');
const tonweb = new TonWeb();

// Get balance
const balance = await tonweb.provider.getBalance('EQA...');
console.log(balance);  // string in nanotons

Pros:

  • Well documented for basic flows.
  • Covers older wallet contracts (v3, v4).
  • Includes extra tools: ton-connect-mock, payment channels.

Cons:

  • No TypeScript types out of the box.
  • Callback-style in many places.
  • Cell API is awkward (.beginCell(), .storeUint() not as clean as @ton/core).
  • Inconsistent — some methods return Promises, others use callbacks.
  • No Wallet v5 support.

When to use: only for an existing tonweb codebase or one specific helper you really need. Never for new code.

2. @ton/ton + @ton/core — the modern stack

Origin: rewritten from scratch in 2023, maintained by TON Foundation + community.

import { TonClient, Address, beginCell, toNano } from '@ton/ton';

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

const address = Address.parse('EQA...');
const balance = await client.getBalance(address);
console.log(balance);  // bigint in nanotons

Packages:

  • @ton/core — Cell, Address, Slice, Builder types; BoC parsing; format helpers (toNano, fromNano).
  • @ton/crypto — mnemonic↔key, sign/verify, sha256/sha512.
  • @ton/ton — TonClient (RPC), wallet contracts V3/V4/V5, jetton/NFT helpers.

Pros:

  • Full TypeScript typing, IDE assistance works.
  • Promise-based, async/await everywhere.
  • Modular — install only what you need (backend with @ton/core alone for parsing, no TonClient).
  • Active development, new wallet versions added quickly.
  • Compatible with both RPCs: Toncenter and TonAPI.

Cons:

  • Docs sometimes incomplete — you read the source.
  • API evolves (minor releases can break compatibility).

When to use: default for everything new. No real alternative in 2026.

3. @tonconnect/sdk + @tonconnect/ui-react — for frontend

Origin: specialised SDK for the TON Connect 2.0 protocol.

import { TonConnectUI } from '@tonconnect/ui';

const tonConnectUI = new TonConnectUI({
  manifestUrl: 'https://yourdomain.com/tonconnect-manifest.json',
});

await tonConnectUI.openModal();
const wallet = await tonConnectUI.connector.connect({ ... });

Subpackages:

  • @tonconnect/sdk — low-level, no UI. For server scenarios and custom interfaces.
  • @tonconnect/ui — pre-built button + modal (vanilla).
  • @tonconnect/ui-react — React components (<TonConnectButton />, hooks useTonConnectUI, useTonAddress).

Pros:

  • Ready-to-use connect UI.
  • Supports all major TON wallets (Tonkeeper, MyTonWallet, Wallet in Telegram, Bitget, OKX).
  • TonProof v2 for Sign-in with TON built-in.
  • Reacts to disconnect/reconnect/network change.

Cons:

  • Largish bundle (~50KB gzipped even minimal).
  • Frontend / mini-app only.
  • Won’t parse transactions — that’s @ton/ton’s job.

When to use: always when dealing with user wallets in browser / Telegram Mini App. Not needed on the backend (use @ton/ton there).

Comparison table

Parametertonweb@ton/ton + @ton/core@tonconnect/sdk
Use caseRPC + offline parsingRPC + offline parsingdApp ↔ wallet connection
TypeScriptminimalyes, fullyes, full
Bundle size~120KB~80KB (min)~50KB (min)
Wallet v5 supportnoyesyes
Webhook supportnovia TonAPIn/a
Async stylemixedpromise/asyncpromise/async
Maintenancelegacyactiveactive
Used inold projectsmost new projectsany dApp with wallet

Typical 2026 stack

A full TypeScript TON project looks like:

// Frontend (Mini App in Telegram):
// - @tonconnect/ui-react for connect + sign
// - @ton/core for parsing incoming data
// - axios for backend calls

// Backend (Node.js):
// - @ton/ton for RPC to Toncenter/TonAPI
// - @ton/core for parsing Cells
// - @ton/crypto for mnemonic operations
// - Server does all sensitive ops (deploy, mint, signature verify)

// On-chain (smart contract):
// - Tolk / FunC / Tact code
// - Acton for testing and deploy
// - @ton/sandbox only if you don't use Acton (legacy)

Scenario 1: Simple dApp — wallet connect + send transaction

Frontend (React):

import { TonConnectUIProvider, TonConnectButton, useTonConnectUI, useTonAddress } from '@tonconnect/ui-react';
import { beginCell, toNano, Address } from '@ton/core';

function App() {
  return (
    <TonConnectUIProvider manifestUrl="/tonconnect-manifest.json">
      <SendButton />
    </TonConnectUIProvider>
  );
}

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

  async function sendOneTon() {
    const body = beginCell()
      .storeUint(0, 32)
      .storeStringTail('Hello TON!')
      .endCell();

    await tonConnectUI.sendTransaction({
      validUntil: Math.floor(Date.now() / 1000) + 300,
      messages: [
        {
          address: 'EQA...recipient',
          amount: toNano('1').toString(),
          payload: body.toBoc().toString('base64'),
        },
      ],
    });
  }

  return (
    <>
      <TonConnectButton />
      {address && <button onClick={sendOneTon}>Send 1 TON</button>}
    </>
  );
}

Used: @tonconnect/ui-react (connect + sendTransaction) + @ton/core (payload construction). Backend may have no TON code in this scenario.

Scenario 2: Backend for payment acceptance

Node.js:

import { TonClient, Address } from '@ton/ton';
import { TonApiClient } from '@ton-api/client';

const tonClient = new TonClient({
  endpoint: 'https://toncenter.com/api/v2/jsonRPC',
  apiKey: process.env.TONCENTER_API_KEY,
});

const tonapi = new TonApiClient({ apiKey: process.env.TONAPI_KEY });

const MY_ADDRESS = Address.parse('EQA...');

async function watchIncomingPayments() {
  let lastLt = 0n;
  while (true) {
    const txs = await tonapi.blockchain.getBlockchainAccountTransactions(
      MY_ADDRESS.toString(),
      { limit: 50, after_lt: lastLt.toString() },
    );

    for (const tx of txs.transactions) {
      const inMsg = tx.in_msg;
      if (inMsg && inMsg.value && BigInt(inMsg.value) > 0n) {
        const comment = inMsg.decoded_body?.text || '';
        console.log(`Received ${Number(inMsg.value) / 1e9} TON, comment: "${comment}"`);
        // Match comment with pending order, mark as paid
      }
      lastLt = BigInt(tx.lt);
    }

    await new Promise(r => setTimeout(r, 3000));
  }
}

watchIncomingPayments();

Stack: @ton/ton (base types and RPC fallback) + TonAPI (indexed transaction queries).

Scenario 3: Server-side send (no UI)

Fully server-side — e.g. a payout bot:

import { TonClient, WalletContractV5R1, internal } from '@ton/ton';
import { mnemonicToWalletKey } from '@ton/crypto';

async function sendFromBot() {
  const key = await mnemonicToWalletKey(
    process.env.BOT_MNEMONIC!.split(' '),
  );
  const wallet = WalletContractV5R1.create({
    workchain: 0,
    publicKey: key.publicKey,
  });

  const tonClient = new TonClient({
    endpoint: 'https://toncenter.com/api/v2/jsonRPC',
  });
  const contract = tonClient.open(wallet);

  await contract.sendTransfer({
    seqno: await contract.getSeqno(),
    secretKey: key.secretKey,
    messages: [
      internal({
        to: 'EQA...recipient',
        value: '0.5',
        body: 'Daily reward',
      }),
    ],
  });
}

Just @ton/ton + @ton/crypto. TonConnect not needed — wallet controlled directly via mnemonic.

Scenario 4: Parsing a complex jetton transfer

A webhook from TonAPI delivered a jetton transfer; extract metadata:

import { Cell, Address } from '@ton/core';

function parseJettonTransfer(bocBase64: string) {
  const cell = Cell.fromBase64(bocBase64);
  const slice = cell.beginParse();

  const op = slice.loadUint(32); // 0x0f8a7ea5 = jetton transfer
  const queryId = slice.loadUint(64);
  const amount = slice.loadCoins();
  const destination = slice.loadAddress();
  const responseDest = slice.loadAddress();

  return { op, queryId, amount, destination, responseDest };
}

Just @ton/core — that’s all you need (offline parsing).

Common pitfalls

  1. Using address.toString() without the bounceable flag. @ton/ton defaults to non-bounceable (UQ…), but many APIs expect bounceable (EQ…). Fix: address.toString({ bounceable: true }).

  2. Mixing tonweb and @ton/ton in one project. Cell formats are incompatible; conversions via BoC base64. Migrate fully, not partially.

  3. Forgetting gas on sendTransaction. If payload has value: '0.05' but the transfer needs 0.1 TON gas — it’ll bounce. Include gas headroom in value.

  4. Storing mnemonic in .env inside the repo. Never. Use a secret manager (Vault, AWS Secrets Manager) or HSM for production.

  5. Polling RPC instead of webhook. Many wallets to monitor — set up TonAPI webhooks instead of polling. Polling burns quota and reacts slower.

What to choose, in summary

  • New dApp (frontend + backend) with wallet connect → @tonconnect/ui-react + @ton/ton.
  • Backend bot for payments → @ton/ton + (optionally) TonAPI.
  • Indexer / Aggregator → @ton/core + TonAPI webhook + Redis.
  • Mass payout (single wallet, many recipients) → @ton/ton + @ton/crypto.
  • Telegram Mini App → @tonconnect/ui-react + @ton/core (minimal bundle).
  • Legacy codebase support → tonweb (but plan migration).

Bottom line

In 2026 the default for new TON projects is @ton/ton + @ton/core for everything non-UI, plus @tonconnect/sdk/ui-react for user wallet integration. tonweb only makes sense in legacy contexts. SDKs for other languages (Python pytoniq-core, Go tonkit-go) work but cover less — for critical-path code TypeScript remains the baseline.

Scenario-specific guides:

Frequently asked

tonweb is the original Tonkeeper SDK from 2020-2022, written in early-web3.js style (callback-based, no TypeScript typing, monolithic package). @ton/ton (aka ton-core) is the modern community SDK rewritten in 2023-2024 with full TypeScript, promise-based API, split into @ton/core (Cell/Address/Slice types), @ton/crypto (mnemonic + signing), @ton/ton (RPC client + wallet contracts). By mid-2026 90%+ of new projects use @ton/ton; tonweb remains only in legacy codebases.
@tonconnect/sdk and @tonconnect/ui-react implement TON Connect 2.0 in your dApp. NOT a replacement for @ton/ton — they complement: TonConnect is the wallet↔dApp protocol; @ton/ton handles everything else (Cell parsing, sending transactions, jetton work). A typical dApp uses both: @tonconnect for user connect + signing, @ton/ton for server-side verification and logic.
Technically yes — tonweb is maintained. Practically — not recommended: API isn't typed, methods inconsistent, docs lag behind the codebase. Backend Node.js or React/Vue mini-app — use @ton/ton. tonweb makes sense only when maintaining a legacy codebase or relying on a niche helper tonweb has uniquely.
Acton ships its native TVM emulator and Rust crates for tests — @ton/sandbox JS emulator isn't needed inside Acton. But for off-chain integration (deploy scripts, post-deploy verifications, frontend logic) you still use @ton/ton and @tonconnect/sdk — Acton doesn't replace them; it runs above. Acton auto-integrates a TypeScript project on `acton new ... --frontend react`.
@ton/core is ~5-10x faster on complex Cell structures (jetton transfer, nested) thanks to an optimised BoC parser. tonweb uses an older implementation. Single operations — millisecond differences. Backends handling hundreds of tx/sec — noticeable. Building a TON indexer — @ton/core is non-negotiable.
Python: pytonlib (from Toncenter), Go: tonkit-go, Rust: tonlib-rs + native Acton crates, Java: tonlib-java. All less popular than the TypeScript stack. If language is not a hard constraint — TypeScript remains the most covered TON SDK. For Python pytoniq-core (a @ton/core port) is solid; for Go — tonkit-go and xssnick/tonutils-go are the main ones.
TonAPI (tonapi.io) and Toncenter (toncenter.com) are the two main TON RPC endpoints, both compatible with @ton/ton. Differences: TonAPI has better jetton/NFT indexing, fast event webhooks, paid tier from $30/month. Toncenter — officially TON Foundation, free, a bit slower, simpler API. Production projects often use both: Toncenter for base queries, TonAPI for specialised (jetton transfers, NFT events).

Related