Server-side wallet

For bots, market-makers, and back-end services that hold their own keys. Same API, different broadcast path.

Install

bash
npm install @ton/ton @ton/core @ton/crypto

Build the wallet

wallet.ts
import { TonClient4, WalletContractV4 } from '@ton/ton';
import { mnemonicToWalletKey } from '@ton/crypto';

const client = new TonClient4({
  endpoint: 'https://mainnet-v4.tonhubapi.com',
});

const key = await mnemonicToWalletKey(MNEMONIC.split(' '));
const wallet = WalletContractV4.create({
  workchain: 0,
  publicKey: key.publicKey,
});
const opened = client.open(wallet);

Quote and broadcast

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

// 1. Get the tx body from chop
const tx = await fetch('https://chop.ag/api/v1/tx', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    assetIn: 'TON',
    assetOut: USDT_MASTER,
    amountIn: '1000000000', // 1 TON
    slippageBps: 100,
    userAddress: wallet.address.toString(),
  }),
}).then((r) => r.json());

// 2. Convert chop's messages into ton-core internal messages
const messages = tx.messages.map((m) =>
  internal({
    to: m.to,
    value: BigInt(m.value),
    body: Cell.fromBase64(m.bodyBoc),
    bounce: true,
  })
);

// 3. Sign and send via the wallet contract
const seqno = await opened.getSeqno();
await opened.sendTransfer({
  seqno,
  secretKey: key.secretKey,
  messages,
});

// 4. Wait for confirmation by polling seqno
while ((await opened.getSeqno()) === seqno) {
  await new Promise((r) => setTimeout(r, 2000));
}

Multi-message support

Wallet v4 supports up to 4 outgoing messages. Wallet v5 supports many more. Your wallet version must supporttx.messages.length — otherwise sendTransferwill throw.

Verifying actual fills

After the seqno advances, query the user’s Jetton wallet balance for the output asset. The DeDust SDK has a clean helper:

ts
import { JettonRoot } from '@dedust/sdk';

const root = client.open(JettonRoot.createFromAddress(USDT_MASTER));
const userJW = client.open(await root.getWallet(wallet.address));
const balance = await userJW.getBalance();

For multi-hop swaps, allow ~10–15 seconds after the wallet seqno advance — the second pool hop runs as a forwarded message and lands in the next block.

Self-hosting the API

chop’s API server is a thin Express wrapper around the aggregator library. If you don’t want to depend on chop.ag for availability, you can run it yourself — same code, same endpoints:

bash
git clone https://github.com/your-org/ton_aggregator
cd ton_aggregator
npm install
npm run serve   # listens on :3000