Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This guide shows you how to sign TON transactions using Dynamic’s Node SDK. You build your transaction cell using @ton/ton, serialize it to a base64 string, and pass it to signTransaction(). The method returns a base64-encoded Ed25519 signature that you then attach to the cell before broadcasting.

Prerequisites

Step 1: Install Dependencies

bun add @ton/ton @ton/crypto

Step 2: Build, Sign, and Send a Transaction

The approach for signing transactions depends on how you created your wallet: If you created your wallet with backUpToClientShareService: true, you can sign directly without retrieving key shares:
import { DynamicTonWalletClient } from '@dynamic-labs-wallet/node-ton';
import { WalletContractV5R1, internal, beginCell, toNano } from '@ton/ton';

export const authenticatedTonClient = async () => {
  const client = new DynamicTonWalletClient({
    environmentId: process.env.DYNAMIC_ENVIRONMENT_ID!,
  });

  await client.authenticateApiToken(process.env.DYNAMIC_AUTH_TOKEN!);
  return client;
};

const tonClient = await authenticatedTonClient();

// Build the transfer message body
const transferBody = beginCell()
  .storeUint(0, 32)   // op (0 = text comment)
  .storeStringTail('Hello, TON!')
  .endCell();

// Serialize to base64 string for signing
const transactionString = transferBody.toBoc().toString('base64');

// ✅ Simple signing - no externalServerKeyShares needed
// Returns a base64-encoded Ed25519 signature
const signature = await tonClient.signTransaction({
  senderAddress: 'YourTonWalletAddress',
  transaction: transactionString,
});

console.log('Signature (base64):', signature);

With Manual Backup

If you created your wallet with backUpToClientShareService: false, you must retrieve and provide external key shares:
// ⚠️ First, get external server key shares (required for manual backup)
const keyShares = await tonClient.getExternalServerKeyShares({
  accountAddress: 'YourTonWalletAddress',
});

const signature = await tonClient.signTransaction({
  senderAddress: 'YourTonWalletAddress',
  externalServerKeyShares: keyShares, // Required for manual backup!
  transaction: transactionString,
  password: 'your-password', // Only if wallet was created with password
});
Password Handling Notes:
  • If your wallet was created without a password, omit the password parameter
  • If your wallet was created with a password, you must provide it for all operations
  • The password parameter is always optional in the API, but required if the wallet is password-protected

Step 3: Broadcast the Transaction

After signing, attach the signature to the cell and broadcast via the TON HTTP API:
import { WalletContractV5R1, Cell } from '@ton/ton';

// Reconstruct the signed external message
const signatureBuffer = Buffer.from(signature, 'base64');

// Build the signed message cell
const signedCell = beginCell()
  .storeBuffer(signatureBuffer)
  .storeSlice(transferBody.beginParse())
  .endCell();

const boc = signedCell.toBoc().toString('base64');

const response = await fetch('https://toncenter.com/api/v2/sendBoc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ boc }),
});

const result = await response.json();
console.log('Broadcast result:', result);

Common Transaction Bodies

Simple TON Transfer (no comment)

const transferBody = beginCell().endCell();
const transactionString = transferBody.toBoc().toString('base64');

TON Transfer with Comment

const transferBody = beginCell()
  .storeUint(0, 32)
  .storeStringTail('Payment for services')
  .endCell();
const transactionString = transferBody.toBoc().toString('base64');

Next Steps