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
| Method | Returns | Description |
|---|---|---|
getAddress() | string | null | Get 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) | boolean | Validate 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
| Method | Returns | Description |
|---|---|---|
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
| Method | Returns | Description |
|---|---|---|
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
| Method | Returns | Description |
|---|---|---|
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
| Method | Returns | Description |
|---|---|---|
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
| Method | Returns | Description |
|---|---|---|
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
| Method | Returns | Description |
|---|---|---|
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
| Method | Returns | Description |
|---|---|---|
verifyPayment(params) | Promise<SDKPaymentVerification> | Verify a TON payment with memo matching, amount tolerance (1%), time window, and replay protection. |
Parameters (SDKVerifyPaymentParams):
| Field | Type | Description |
|---|---|---|
amount | number | Expected payment amount in TON. |
memo | string | Expected memo/comment (e.g. username, dealId). Case-insensitive match. |
gameType | string | Game/operation type for replay protection grouping. |
maxAgeMinutes | number | Maximum age of valid payments in minutes. Default: 10. |
Returns (SDKPaymentVerification):
| Field | Type | Description |
|---|---|---|
verified | boolean | Whether a valid payment was found. |
txHash | string | Blockchain transaction hash (for replay protection). |
amount | number | Verified amount in TON. |
playerWallet | string | Sender's wallet address (useful for auto-payout). |
date | string | ISO 8601 date string of the transaction. |
secondsAgo | number | Seconds since the transaction. |
error | string | Error 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
| Method | Returns | Description |
|---|---|---|
toNano(amount) | bigint | Convert TON amount to nanoTON. Accepts number | string. |
fromNano(nano) | string | Convert nanoTON to human-readable TON string. Accepts bigint | string. |
validateAddress(address) | boolean | Validate 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
| Code | Thrown By | Description |
|---|---|---|
WALLET_NOT_INITIALIZED | sendTON, sendJetton, verifyPayment | The bot's TON wallet is not configured. Set up wallet via CLI or config. |
INVALID_ADDRESS | sendTON, sendJetton | The provided TON address is not valid. Use validateAddress() before sending. |
OPERATION_FAILED | All mutating methods | Blockchain 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
sendTONusesPAY_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 like1.1 * 10**9 = 1099999999. Always usetoNano()/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()requiresused_transactionstable — your plugin must export amigrate()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 callingsendTON()orsendJetton().