ESC
Start typing to search...

sdk.ton — TON Blockchain

Complete reference for TON wallet, transfers, jettons, NFTs, and payment verification. Access via sdk.ton in your plugin.

Quick Example

Balance check, price lookup, and transfer
import type { PluginSDK } from "@teleton-agent/sdk";

export default {
  name: "ton-demo",
  version: "1.0.0",
  tools: [
    {
      name: "wallet_overview",
      description: "Show wallet balance and TON price",
      parameters: {},
      execute: async (_args: unknown, sdk: PluginSDK) => {
        const address = sdk.ton.getAddress();
        if (!address) return { error: "Wallet not initialized" };

        const [balance, price] = await Promise.all([
          sdk.ton.getBalance(),
          sdk.ton.getPrice(),
        ]);

        return {
          address,
          balance: balance?.balance ?? "unknown",
          balanceNano: balance?.balanceNano ?? "unknown",
          priceUSD: price?.usd ?? "unavailable",
          source: price?.source ?? "none",
        };
      },
    },
    {
      name: "send_payment",
      description: "Send TON to recipient",
      parameters: {
        type: "object",
        properties: {
          to: { type: "string", description: "Recipient TON address" },
          amount: { type: "number", description: "Amount in TON" },
          comment: { type: "string", description: "Transaction memo" },
        },
        required: ["to", "amount"],
      },
      execute: async (args: { to: string; amount: number; comment?: string }, sdk: PluginSDK) => {
        if (!sdk.ton.validateAddress(args.to)) {
          return { error: "Invalid TON address" };
        }
        const result = await sdk.ton.sendTON(args.to, args.amount, args.comment);
        return { txRef: result.txRef, amount: result.amount };
      },
    },
  ],
};

Wallet Methods

MethodReturnsDescription
getAddress()string | nullGet the bot's own TON wallet address. Returns null if the wallet is not initialized.
getBalance(address?)Promise<TonBalance | null>Get balance for a TON address. Defaults to the bot's own wallet if no address is provided.
validateAddress(address)booleanValidate a TON address format. Accepts EQ... and UQ... formats.
Wallet methods
// Get bot's own address
const myAddress = sdk.ton.getAddress();
// e.g. "EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG"

// Get own balance (no argument)
const myBalance = await sdk.ton.getBalance();
console.log(myBalance?.balance);     // "12.50"
console.log(myBalance?.balanceNano); // "12500000000"

// Get another address's balance
const otherBalance = await sdk.ton.getBalance(
  "UQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2"
);

// Validate before sending
if (!sdk.ton.validateAddress(userInput)) {
  return { error: "Invalid address format" };
}

Price

MethodReturnsDescription
getPrice()Promise<TonPrice | null>Get current TON/USD price. Uses TonAPI with CoinGecko fallback. Cached for 30 seconds internally.
Price lookup
const price = await sdk.ton.getPrice();
if (price) {
  console.log(`TON = $${price.usd}`);       // "TON = $3.42"
  console.log(`Source: ${price.source}`);     // "TonAPI" or "CoinGecko"
  console.log(`Fetched: ${new Date(price.timestamp).toISOString()}`);
}

// Compute portfolio value
const balance = await sdk.ton.getBalance();
if (balance && price) {
  const value = parseFloat(balance.balance) * price.usd;
  console.log(`Portfolio: $${value.toFixed(2)}`);
}

Transfers

MethodReturnsDescription
sendTON(to, amount, comment?)Promise<TonSendResult>Send TON to a recipient address. Irreversible. Uses PAY_GAS_SEPARATELY so the amount sent is exact.

Parameters:

  • to (string) — Recipient TON address (EQ... or UQ...)
  • amount (number) — Amount in TON (e.g. 1.5)
  • comment (string, optional) — Transaction comment/memo

Throws: PluginSDKError with code WALLET_NOT_INITIALIZED, INVALID_ADDRESS, or OPERATION_FAILED.

Send TON
try {
  const result = await sdk.ton.sendTON(
    "UQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2",
    2.5,
    "Payment for services"
  );
  console.log(`Sent! txRef: ${result.txRef}, amount: ${result.amount}`);
  // txRef format: "seqno_timestamp_amount" e.g. "42_1709312400_2.5"
} catch (err) {
  if (err.code === "WALLET_NOT_INITIALIZED") {
    return { error: "Bot wallet not configured" };
  }
  if (err.code === "INVALID_ADDRESS") {
    return { error: "Bad recipient address" };
  }
  throw err;
}

Transactions

MethodReturnsDescription
getTransactions(address, limit?)Promise<TonTransaction[]>Get transaction history for a TON address. Default limit: 10, max: 50.
Transaction history
const address = sdk.ton.getAddress()!;
const txs = await sdk.ton.getTransactions(address, 20);

for (const tx of txs) {
  console.log(`${tx.type}: ${tx.amount ?? "N/A"}`);
  console.log(`  Hash: ${tx.hash}`);
  console.log(`  From: ${tx.from ?? "unknown"}`);
  console.log(`  To: ${tx.to ?? "unknown"}`);
  console.log(`  Date: ${tx.date}`);
  console.log(`  Explorer: ${tx.explorer}`);
  if (tx.comment) console.log(`  Comment: ${tx.comment}`);
}

Jetton Core

MethodReturnsDescription
getJettonBalances(ownerAddress?)Promise<JettonBalance[]>Get jetton (token) balances. Defaults to bot's wallet.
getJettonInfo(jettonAddress)Promise<JettonInfo | null>Get jetton metadata: name, symbol, decimals, supply, holders, verification.
sendJetton(jettonAddress, to, amount, opts?)Promise<JettonSendResult>Transfer jetton tokens. Irreversible. Amount in human-readable units.
getJettonWalletAddress(ownerAddress, jettonAddress)Promise<string | null>Get the jetton wallet address for a specific owner and jetton.
Jetton operations
// List all jetton balances
const jettons = await sdk.ton.getJettonBalances();
for (const j of jettons) {
  console.log(`${j.symbol}: ${j.balanceFormatted} (verified: ${j.verified})`);
  if (j.usdPrice) console.log(`  Value: $${(parseFloat(j.balanceFormatted) * j.usdPrice).toFixed(2)}`);
}

// Get info about a specific jetton
const USDT = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";
const info = await sdk.ton.getJettonInfo(USDT);
if (info) {
  console.log(`${info.name} (${info.symbol})`);
  console.log(`Decimals: ${info.decimals}, Holders: ${info.holdersCount}`);
  console.log(`Total supply: ${info.totalSupply}`);
}

// Send 50 USDT
const result = await sdk.ton.sendJetton(
  USDT,
  "UQDtFpEwcFAEcRe5mLVh2N6C0x-_hJEM7W61_JLnSF74p4q2",
  50,
  { comment: "Invoice #1234" }
);
console.log(`Success: ${result.success}, Seqno: ${result.seqno}`);

// Get jetton wallet address
const jettonWallet = await sdk.ton.getJettonWalletAddress(
  "EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG",
  USDT
);

Jetton Analytics

MethodReturnsDescription
getJettonPrice(jettonAddress)Promise<JettonPrice | null>Current price in USD and TON with 24h/7d/30d change percentages.
getJettonHolders(jettonAddress, limit?)Promise<JettonHolder[]>Top holders ranked by balance. Default limit: 10, max: 100.
getJettonHistory(jettonAddress)Promise<JettonHistory | null>Market analytics: price changes, 24h volume, FDV, market cap, holder count.
Jetton analytics
const SCALE = "EQBlqsm144Dq6SjbPI4jjZvlmHDqumGs1SfAW3IqjvnBqSZ8";

// Price check
const price = await sdk.ton.getJettonPrice(SCALE);
if (price) {
  console.log(`Price: $${price.priceUSD} / ${price.priceTON} TON`);
  console.log(`24h: ${price.change24h}, 7d: ${price.change7d}, 30d: ${price.change30d}`);
}

// Top holders
const holders = await sdk.ton.getJettonHolders(SCALE, 5);
for (const h of holders) {
  console.log(`#${h.rank} ${h.name ?? h.address}: ${h.balance}`);
}

// Full market data
const history = await sdk.ton.getJettonHistory(SCALE);
if (history) {
  console.log(`${history.name} (${history.symbol})`);
  console.log(`Price: $${history.currentPrice} / ${history.currentPriceTON} TON`);
  console.log(`Volume 24h: $${history.volume24h}`);
  console.log(`FDV: $${history.fdv}, MCap: $${history.marketCap}`);
  console.log(`Holders: ${history.holders}`);
  console.log(`Changes: 24h=${history.changes["24h"]}, 7d=${history.changes["7d"]}, 30d=${history.changes["30d"]}`);
}

NFTs

MethodReturnsDescription
getNftItems(ownerAddress?)Promise<NftItem[]>Get NFT items owned by an address. Defaults to bot's wallet.
getNftInfo(nftAddress)Promise<NftItem | null>Get NFT item information by contract address.
NFT operations
// List owned NFTs
const nfts = await sdk.ton.getNftItems();
for (const nft of nfts) {
  console.log(`${nft.name ?? "Unnamed"} (#${nft.index})`);
  console.log(`  Collection: ${nft.collectionName ?? "none"}`);
  console.log(`  Address: ${nft.address}`);
  console.log(`  Verified: ${nft.verified}`);
}

// Get specific NFT info
const nft = await sdk.ton.getNftInfo("EQAbcDeFgHiJkLmNoPqRsTuVwXyZ...");
if (nft) {
  console.log(`Owner: ${nft.ownerAddress}`);
  console.log(`Image: ${nft.image}`);
}

Payment Verification

MethodReturnsDescription
verifyPayment(params)Promise<SDKPaymentVerification>Verify a TON payment with memo matching, amount tolerance (1%), time window, and replay protection.

Parameters (SDKVerifyPaymentParams):

FieldTypeDescription
amountnumberExpected payment amount in TON.
memostringExpected memo/comment (e.g. username, dealId). Case-insensitive match.
gameTypestringGame/operation type for replay protection grouping.
maxAgeMinutesnumberMaximum age of valid payments in minutes. Default: 10.

Returns (SDKPaymentVerification):

FieldTypeDescription
verifiedbooleanWhether a valid payment was found.
txHashstringBlockchain transaction hash (for replay protection).
amountnumberVerified amount in TON.
playerWalletstringSender's wallet address (useful for auto-payout).
datestringISO 8601 date string of the transaction.
secondsAgonumberSeconds since the transaction.
errorstringError message if verification failed.
Payment verification with replay protection
// IMPORTANT: Your plugin must export a migrate() function
// that creates the used_transactions table for replay protection.
export function migrate(db: Database) {
  db.exec(`
    CREATE TABLE IF NOT EXISTS used_transactions (
      tx_hash TEXT NOT NULL,
      game_type TEXT NOT NULL,
      used_at TEXT DEFAULT (datetime('now')),
      PRIMARY KEY (tx_hash, game_type)
    )
  `);
}

// In your tool:
const verification = await sdk.ton.verifyPayment({
  amount: 1.0,
  memo: "user_alice_bet",
  gameType: "coinflip",
  maxAgeMinutes: 5,
});

if (verification.verified) {
  console.log(`Payment confirmed: ${verification.amount} TON`);
  console.log(`From: ${verification.playerWallet}`);
  console.log(`TX: ${verification.txHash}`);
  // The transaction hash is automatically marked as used
  // Subsequent calls with the same tx will NOT verify
} else {
  console.log(`Payment not found: ${verification.error}`);
}

Utilities

MethodReturnsDescription
toNano(amount)bigintConvert TON amount to nanoTON. Accepts number | string.
fromNano(nano)stringConvert nanoTON to human-readable TON string. Accepts bigint | string.
validateAddress(address)booleanValidate a TON address format.
Unit conversion
// TON to nanoTON
const nano = sdk.ton.toNano(1.5);
console.log(nano); // 1500000000n

// nanoTON to TON
const ton = sdk.ton.fromNano(1500000000n);
console.log(ton); // "1.5"

// Also accepts strings (recommended for precision)
const precise = sdk.ton.toNano("0.123456789");
console.log(precise); // 123456789n

const back = sdk.ton.fromNano("123456789");
console.log(back); // "0.123456789"

Type Definitions

TransactionType
type TransactionType =
  | "ton_received"
  | "ton_sent"
  | "jetton_received"
  | "jetton_sent"
  | "nft_received"
  | "nft_sent"
  | "gas_refund"
  | "bounce"
  | "contract_call"
  | "multi_send";
TonBalance
interface TonBalance {
  balance: string;      // Human-readable (e.g. "12.50")
  balanceNano: string;  // In nanoTON as string
}
TonPrice
interface TonPrice {
  usd: number;        // Price in USD
  source: string;     // "TonAPI" or "CoinGecko"
  timestamp: number;  // ms since epoch
}
TonSendResult
interface TonSendResult {
  txRef: string;  // Format: "seqno_timestamp_amount"
  amount: number; // Amount sent in TON
}
TonTransaction
interface TonTransaction {
  type: TransactionType;
  hash: string;                    // Blockchain tx hash (hex)
  amount?: string;                 // e.g. "1.5 TON"
  from?: string;                   // Sender address
  to?: string;                     // Recipient address
  comment?: string | null;         // Transaction memo
  date: string;                    // ISO 8601
  secondsAgo: number;              // Seconds elapsed
  explorer: string;                // Tonviewer link
  jettonAmount?: string;           // Raw jetton amount
  jettonWallet?: string;           // Jetton wallet address
  nftAddress?: string;             // NFT address
  transfers?: TonTransaction[];    // For multi_send
}
JettonBalance
interface JettonBalance {
  jettonAddress: string;     // Jetton master contract address
  walletAddress: string;     // Owner's jetton wallet address
  balance: string;           // Raw units (avoids precision loss)
  balanceFormatted: string;  // Human-readable (e.g. "100.50")
  symbol: string;            // e.g. "USDT"
  name: string;              // e.g. "Tether USD"
  decimals: number;          // e.g. 6 for USDT, 9 for TON
  verified: boolean;         // Verified on TonAPI
  usdPrice?: number;         // USD price per token
}
JettonInfo
interface JettonInfo {
  address: string;       // Jetton master contract address
  name: string;          // Token name
  symbol: string;        // Token ticker
  decimals: number;      // Token decimals
  totalSupply: string;   // Total supply in raw units
  holdersCount: number;  // Unique holders
  verified: boolean;     // Verified on TonAPI
  description?: string;  // Token description
  image?: string;        // Token image URL
}
JettonSendResult
interface JettonSendResult {
  success: boolean;  // Whether tx was sent
  seqno: number;     // Wallet sequence number
}
NftItem
interface NftItem {
  address: string;            // NFT contract address
  index: number;              // Index within collection
  ownerAddress?: string;      // Current owner
  collectionAddress?: string; // Collection contract
  collectionName?: string;    // Collection name
  name?: string;              // NFT name
  description?: string;       // NFT description
  image?: string;             // NFT image URL
  verified: boolean;          // Whether verified
}
JettonPrice
interface JettonPrice {
  priceUSD: number | null;    // Price in USD
  priceTON: number | null;    // Price in TON
  change24h: string | null;   // e.g. "-2.5%"
  change7d: string | null;    // 7-day change
  change30d: string | null;   // 30-day change
}
JettonHolder
interface JettonHolder {
  rank: number;           // 1 = top holder
  address: string;        // Holder's TON address
  name: string | null;    // Known name (e.g. "Binance")
  balance: string;        // Formatted (e.g. "1,234.56")
  balanceRaw: string;     // Raw smallest units
}
JettonHistory
interface JettonHistory {
  symbol: string;
  name: string;
  currentPrice: string;      // USD
  currentPriceTON: string;   // TON
  changes: { "24h": string; "7d": string; "30d": string };
  volume24h: string;         // 24h trading volume (USD)
  fdv: string;               // Fully diluted valuation
  marketCap: string;
  holders: number;
}
SDKVerifyPaymentParams & SDKPaymentVerification
interface SDKVerifyPaymentParams {
  amount: number;           // Expected payment in TON
  memo: string;             // Expected memo (case-insensitive)
  gameType: string;         // Replay protection grouping
  maxAgeMinutes?: number;   // Time window (default: 10)
}

interface SDKPaymentVerification {
  verified: boolean;
  txHash?: string;          // For replay protection
  amount?: number;          // Verified amount in TON
  playerWallet?: string;    // Sender address (for auto-payout)
  date?: string;            // ISO 8601
  secondsAgo?: number;
  error?: string;           // Error message if failed
}

Error Codes

CodeThrown ByDescription
WALLET_NOT_INITIALIZEDsendTON, sendJetton, verifyPaymentThe bot's TON wallet is not configured. Set up wallet via CLI or config.
INVALID_ADDRESSsendTON, sendJettonThe provided TON address is not valid. Use validateAddress() before sending.
OPERATION_FAILEDAll mutating methodsBlockchain operation failed (network error, insufficient balance, contract error).
Error handling pattern
import type { PluginSDKError } from "@teleton-agent/sdk";

try {
  await sdk.ton.sendTON(recipient, amount);
} catch (err) {
  const sdkErr = err as PluginSDKError;
  switch (sdkErr.code) {
    case "WALLET_NOT_INITIALIZED":
      return { error: "Wallet not set up. Run /wallet init first." };
    case "INVALID_ADDRESS":
      return { error: `Invalid address: ${recipient}` };
    case "OPERATION_FAILED":
      return { error: `Transfer failed: ${sdkErr.message}` };
    default:
      throw err;
  }
}

Gotchas

  • sendTON uses PAY_GAS_SEPARATELY — the amount you specify is the exact amount the recipient receives. Gas fees are deducted from the sender's remaining balance, not from the sent amount.
  • Float precision — The SDK uses string-based decimal conversion internally, NOT Math.floor(amount * 10**decimals). This avoids floating-point precision errors like 1.1 * 10**9 = 1099999999. Always use toNano()/fromNano() for conversions.
  • getPrice() is cached for 30 seconds — multiple calls within 30 seconds return the same result without hitting the API. This is intentional to avoid rate limiting.
  • verifyPayment() requires used_transactions table — your plugin must export a migrate() function that creates this table. Without it, replay protection will not work and the same transaction could be verified multiple times.
  • All send operations are irreversible — there is no undo on the blockchain. Always validate addresses with validateAddress() and confirm amounts before calling sendTON() or sendJetton().