Cookbook
TON Transfer
This recipe provides an example of sending TON using a mnemonic for signing. It covers deriving a wallet key, constructing a transaction, signing it, and broadcasting it to the blockchain.
import { WalletContractV5R1, internal, SendMode } from '@ton/ton';
import { mnemonicToPrivateKey } from '@ton/crypto';
import { TonApiClient } from '@ton-api/client';
import { ContractAdapter } from '@ton-api/ton-adapter';
// Initialize TonApi client
const ta = new TonApiClient({
baseUrl: 'https://tonapi.io',
apiKey: 'YOUR_API_KEY', // Optional, improves limits and access
});
// Create an adapter for interacting with contracts
const adapter = new ContractAdapter(ta);
// Convert mnemonic phrase to a private key
const mnemonics = 'word1 word2 ...'.split(' '); // Replace with your mnemonic phrase
const keyPair = await mnemonicToPrivateKey(mnemonics); // Generate key pair
// Create a wallet contract (Wallet V5R1, other versions or contract types can be used)
const wallet = WalletContractV5R1.create({ workchain: 0, publicKey: keyPair.publicKey });
const contract = adapter.open(wallet); // Open the contract using the adapter
// Retrieve the current seqno (sequence number) for the transaction
const seqno = await contract.getSeqno(); // Required for transaction signing
// Send a transfer
await contract.sendTransfer({
secretKey: keyPair.secretKey, // Sign transaction with the private key
seqno, // Use the latest seqno
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, // Specify sending mode
messages: [
internal({
to: 'EQApwowlR6X54bXoso6orKCzCNm9ily8pAFy5vTwmsQ2Wqin', // Recipient address
value: '0.05', // Amount of TON to send
body: 'Example transfer body' // Optional message body
})
]
});
Jetton Transfer
This recipe provides an example of sending Jettons using a mnemonic for signing. It covers deriving a wallet key, constructing a Jetton transfer transaction, signing it, and broadcasting it to the blockchain.
import { WalletContractV5R1, Address, beginCell, internal, toNano, SendMode } from '@ton/ton';
import { mnemonicToPrivateKey } from '@ton/crypto';
import { TonApiClient } from '@ton-api/client';
import { ContractAdapter } from '@ton-api/ton-adapter';
// Initialize TonApi client
const ta = new TonApiClient({
baseUrl: 'https://tonapi.io',
apiKey: 'YOUR_API_KEY', // Optional, improves request limits and access
});
// Create an adapter for interacting with contracts
const adapter = new ContractAdapter(ta);
// Base gas fee required for the jetton transfer
const BASE_JETTON_SEND_AMOUNT = toNano(0.05);
// Define recipient and jetton master contract addresses
const destination = Address.parse('EQApwowlR6X54bXoso6orKCzCNm9ily8pAFy5vTwmsQ2Wqin'); // Replace with the actual recipient address
const jettonMaster = Address.parse('EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'); // USDt jetton master contract
// Define jetton transfer amount in the smallest jetton units (1 USDt)
const jettonAmount = 1_000_000n;
// Convert mnemonic phrase to a private key
const mnemonics = 'word1 word2 ...'.split(' '); // Wallet seed phrase (24 words)
const keyPair = await mnemonicToPrivateKey(mnemonics); // Generate key pair
// Create a wallet contract (Wallet V5R1, other versions or contract types can be used)
const wallet = WalletContractV5R1.create({ workchain: 0, publicKey: keyPair.publicKey });
const contract = adapter.open(wallet); // Open the wallet contract using the adapter
// Get the sender's jetton wallet address from the jetton master contract
const jettonWalletAddressResult = await ta.blockchain.execGetMethodForBlockchainAccount(
jettonMaster,
'get_wallet_address',
{ args: [wallet.address.toRawString()] }
);
const jettonWallet = Address.parse(jettonWalletAddressResult.decoded.jetton_wallet_address); // Extract the jetton wallet address
// Create payload for the jetton transfer
const jettonTransferPayload = beginCell()
.storeUint(0xf8a7ea5, 32) // JETTON_TRANSFER_OP_CODE (operation identifier)
.storeUint(0, 64) // Query ID (0 for new transactions)
.storeCoins(jettonAmount) // Amount to transfer (1 USDt)
.storeAddress(destination) // Recipient address
.storeAddress(wallet.address) // Address to receive excess funds (usually address of sender)
.storeBit(false) // No custom payload
.storeCoins(1n) // Forward fee in nanoTON (for send notify to wallet)
.storeMaybeRef(undefined)
.endCell();
// Get the current seqno (sequence number) for the wallet transaction
const seqno = await contract.getSeqno();
// Send the transfer transaction
await contract.sendTransfer({
seqno, // Required to ensure transaction uniqueness
secretKey: keyPair.secretKey, // Sign the transaction with the private key
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, // Specify send mode
messages: [
internal({
to: jettonWallet, // Sending to the sender's jetton wallet
value: BASE_JETTON_SEND_AMOUNT, // Gas fee
body: jettonTransferPayload // Jetton transfer payload
})
]
});
Emmulation
Originating from the Emulation section of the REST API documentation, this recipe provides implementation examples for emulating transactions in TypeScript. Developers can use these examples to integrate emulation into their applications.
Swagger documentation for Emulation operations can be found here.
import { WalletContractV4 } from '@ton/ton';
import { Address, beginCell, internal, toNano, SendMode, external, storeMessage } from '@ton/core';
import { mnemonicNew, mnemonicToPrivateKey } from '@ton/crypto';
import { TonApiClient } from '@ton-api/client';
// if you need to send lots of requests in parallel,
// make sure you use a tonapi token.
const ta = new TonApiClient({
baseUrl: 'https://tonapi.io'
// apiKey: 'YOUR_API_KEY',
});
// Emulate transaction from wallet_v4 address
const emulateTransaction = async () => {
// Sender's wallet address
const senderAddress = Address.parse('UQAQxxpzxmEVU0Lu8U0zNTxBzXIWPvo263TIN1OQM9YvxsnV');
const recipientAddress = Address.parse('UQDNzlh0XSZdb5_Qrlx5QjyZHVAO74v5oMeVVrtF_5Vt1rIt');
// Get wallet's seqno and public key
const { seqno } = await ta.wallet.getAccountSeqno(senderAddress);
const { publicKey: publicKeyHex } = await ta.accounts.getAccountPublicKey(senderAddress);
const wallet = WalletContractV4.create({
workchain: 0,
publicKey: Buffer.from(publicKeyHex, 'hex')
});
// Create dummy private key
const dummyKey = (await mnemonicToPrivateKey(await mnemonicNew())).secretKey;
// Generate payload for NFT transfer
const body = beginCell()
.storeUint(0x5fcc3d14, 32) // Operation code for NFT transfer
.storeUint(0, 64) // Query ID
.storeAddress(recipientAddress) // Recipient address
.storeAddress(senderAddress) // Sender address
.storeUint(0, 1)
.storeCoins(toNano('0.0000001')) // Small transfer fee
.storeBit(0) // No custom payload
.endCell();
// Create transfer for emulation
const tr = wallet.createTransfer({
seqno,
secretKey: dummyKey,
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
messages: [
internal({
value: toNano('0.5'),
to: Address.parse('EQDvK4AbmarjScYfcfF95XLX5y1ges3zPWgOZavXo0SMmqH9'),
body: body
})
]
});
// Create external message for emulation
const bocExternalMessage = beginCell()
.store(
storeMessage(
external({
to: senderAddress,
init: undefined,
body: tr
})
)
)
.endCell();
// Emulate transaction
const emulateTrace = await ta.emulation.emulateMessageToTrace(
{ boc: bocExternalMessage },
{ ignore_signature_check: true } // Ignore signature for execute message from other account
);
console.log(emulateTrace);
};
emulateTransaction();
Gasless Transfer
Originating from the Gasless section of the REST API documentation, this recipe provides implementation examples for gasless transactions in TypeScript, Go and Python. Developers can use these examples to integrate gasless transactions into their applications.
Gasless transactions are currently not supported through TonConnect.
Swagger documentation for Gasless operations can be found here.
import { WalletContractV5R1 } from '@ton/ton';
import {
Address,
beginCell,
internal,
toNano,
SendMode,
external,
storeMessage,
storeMessageRelaxed
} from '@ton/core';
import { mnemonicToPrivateKey } from '@ton/crypto';
import { TonApiClient } from '@ton-api/client';
import { ContractAdapter } from '@ton-api/ton-adapter';
// if you need to send lots of requests in parallel,
// make sure you use a tonapi token.
const ta = new TonApiClient({
baseUrl: 'https://tonapi.io',
// apiKey: 'YOUR_API_KEY',
});
const provider = new ContractAdapter(ta);
const OP_CODES = {
TK_RELAYER_FEE: 0x878da6e3,
JETTON_TRANSFER: 0xf8a7ea5
};
// Amount for jetton transfer. Usually 0.05 TON is enough for most jetton transfers without forwardBody
const BASE_JETTON_SEND_AMOUNT = toNano(0.05);
const main = async () => {
// this is a simple example of how to send a gasless transfer.
// you only need to specify your seed and a destination address.
// the seed is not sent to the network, it is used to sign messages locally.
const seed = '..!!! REPLACE THIS WITH YOUR SEED !!! ..'; // wallet seed `word1 word2 word3 ... word24`
const destination = Address.parse('..!!! REPLACE THIS WITH A CORRECT DESTINATION !!!..'); // replace with a correct recipient address
const usdtMaster = Address.parse('EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'); // USDt jetton master.
const jettonAmount = 1_000_000n; // amount in the smallest jetton units. This is 1 USDt.
const keyPair = await mnemonicToPrivateKey(seed.split(' '));
const workchain = 0;
const wallet = WalletContractV5R1.create({
workchain,
publicKey: keyPair.publicKey
});
const contract = provider.open(wallet);
console.log('Wallet address:', wallet.address.toString());
const jettonWalletAddressResult = await ta.blockchain.execGetMethodForBlockchainAccount(
usdtMaster,
'get_wallet_address',
{
args: [wallet.address.toRawString()]
}
);
const jettonWallet = Address.parse(jettonWalletAddressResult.decoded.jettonWalletAddress);
// we use USDt in this example,
// so we just print all supported gas jettons and get the relay address.
// we have to send excess to the relay address in order to make a transfer cheaper.
const relayerAddress = await printConfigAndReturnRelayAddress();
// Create payload for jetton transfer
const tetherTransferPayload = beginCell()
.storeUint(OP_CODES.JETTON_TRANSFER, 32)
.storeUint(0, 64)
.storeCoins(jettonAmount) // 1 USDT
.storeAddress(destination) // address for receiver
.storeAddress(relayerAddress) // address for excesses
.storeBit(false) // null custom_payload
.storeCoins(1n) // count of forward transfers in nanoton
.storeMaybeRef(undefined)
.endCell();
const messageToEstimate = beginCell()
.storeWritable(
storeMessageRelaxed(
internal({
to: jettonWallet,
bounce: true,
value: BASE_JETTON_SEND_AMOUNT,
body: tetherTransferPayload
})
)
)
.endCell();
// we send a single message containing a transfer from our wallet to a desired destination.
// as a result of estimation, TonAPI returns a list of messages that we need to sign.
// the first message is a fee transfer to the relay address, the second message is our original transfer.
const params = await ta.gasless.gaslessEstimate(usdtMaster, {
walletAddress: wallet.address,
walletPublicKey: keyPair.publicKey.toString('hex'),
messages: [{ boc: messageToEstimate }]
}); //.catch(error => console.error(error));
console.log('Estimated transfer:', params);
const seqno = await contract.getSeqno();
// params is the same structure as params in tonconnect
const tetherTransferForSend = wallet.createTransfer({
seqno,
authType: 'internal',
timeout: Math.ceil(Date.now() / 1000) + 5 * 60,
secretKey: keyPair.secretKey,
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
messages: params.messages.map(message =>
internal({
to: message.address,
value: BigInt(message.amount),
body: message.payload
})
)
});
const extMessage = beginCell()
.storeWritable(
storeMessage(
external({
to: contract.address,
init: seqno === 0 ? contract.init : undefined,
body: tetherTransferForSend
})
)
)
.endCell();
// Send a gasless transfer
ta.gasless
.gaslessSend({
walletPublicKey: keyPair.publicKey.toString('hex'),
boc: extMessage
})
.then(() => console.log('A gasless transfer sent!'))
.catch(error => console.error(error.message));
};
async function printConfigAndReturnRelayAddress(): Promise<Address> {
const cfg = await ta.gasless.gaslessConfig();
console.log('Available jettons for gasless transfer');
console.log(cfg.gasJettons.map(gasJetton => gasJetton.masterId));
console.log(`Relay address to send fees to: ${cfg.relayAddress}`);
return cfg.relayAddress;
}
main().catch(console.error);