The TAC SDK enables seamless asset bridging between TON and TAC EVM chains. This guide explains how to transfer tokens between chains and work with token addresses across both ecosystems.
Asset bridging in TAC uses a lock-and-mint/burn-and-release mechanism to maintain token supply integrity across chains. When tokens move from TON to TAC, they’re locked on TON and minted on TAC. When moving back, they’re burned on TAC and released on TON.
Understanding Asset Bridging
When bridging assets between TON and TAC, there are several key operations:
- Address Mapping: Each token on one chain has a corresponding address on the other chain
- Token Transfers: Bridging tokens between chains
- Balance Management: Working with token balances on both chains
Token Address Mapping
To work with tokens across chains, you need to map addresses between TON and TAC EVM:
import { TacSdk, Network } from '@tonappchain/sdk';
async function getTokenAddresses() {
const tacSdk = await TacSdk.create({
network: Network.TESTNET
});
// Get EVM address for a TON token
const tonTokenAddress = "EQTokenAddress...";
const evmTokenAddress = await tacSdk.getEVMTokenAddress(tonTokenAddress);
console.log(`TON token ${tonTokenAddress} maps to EVM token ${evmTokenAddress}`);
// Get TON address for an EVM token
const tacTokenAddress = "0xTokenAddress...";
const tvmTokenAddress = await tacSdk.getTVMTokenAddress(tacTokenAddress);
console.log(`EVM token ${tacTokenAddress} maps to TON token ${tvmTokenAddress}`);
return { evmTokenAddress, tvmTokenAddress };
}
Working with Native Coins
You can also map native coins between chains:
async function getNativeCoinAddresses() {
const tacSdk = await TacSdk.create({
network: Network.TESTNET
});
// Get the indicator for native TON
const nativeTONIndicator = tacSdk.nativeTONAddress;
// Get EVM address for wrapped TON
const wrappedTONOnEVM = await tacSdk.getEVMTokenAddress(nativeTONIndicator);
console.log(`Native TON maps to EVM token ${wrappedTONOnEVM}`);
// Get the address for native TAC
const nativeTACAddress = await tacSdk.nativeTACAddress;
// Get TON address for wrapped TAC
const wrappedTACOnTON = await tacSdk.getTVMTokenAddress(nativeTACAddress);
console.log(`Native TAC maps to TON token ${wrappedTACOnTON}`);
return { wrappedTONOnEVM, wrappedTACOnTON };
}
Bridging Assets in Transactions
To bridge assets as part of a transaction, include them in the assets parameter of sendCrossChainTransaction:
import { TacSdk, Network, AssetBridgingData } from '@tonappchain/sdk';
async function bridgeTokens() {
const tacSdk = await TacSdk.create({
network: Network.TESTNET
});
const sender = /* your sender from wallet integration */;
// Bridge a single token
const assets: AssetBridgingData[] = [
{
address: "EQTokenAddress...", // TON token address
amount: 10.5 // User-friendly amount
}
];
// Send a transaction with bridged tokens
const transactionLinker = await tacSdk.sendCrossChainTransaction(
{
evmTargetAddress: "0xTargetAddress",
methodName: "depositTokens(bytes,bytes)",
encodedParameters: "0x..." // Encoded parameters
},
sender,
assets
);
return transactionLinker;
}
Bridging Multiple Tokens
You can bridge multiple tokens in a single transaction:
// Bridge multiple tokens
const multipleAssets: AssetBridgingData[] = [
{
address: "EQToken1Address...",
amount: 10.5
},
{
address: "EQToken2Address...",
amount: 20.25,
decimals: 9 // Optional, can be auto-detected
}
];
const transactionLinker = await tacSdk.sendCrossChainTransaction(
evmProxyMsg,
sender,
multipleAssets
);
Bridging Native TON
To bridge native TON, omit the address field:
// Bridge native TON
const bridgeTON: AssetBridgingData[] = [
{
// No address means native TON
amount: 1.5 // TON amount
}
];
const transactionLinker = await tacSdk.sendCrossChainTransaction(
evmProxyMsg,
sender,
bridgeTON
);
Using Raw Amounts
If you prefer to work with raw token amounts (including decimals):
const assetsWithRawAmount: AssetBridgingData[] = [
{
address: "EQTokenAddress...",
rawAmount: BigInt(10500000000) // Raw amount with decimals included
}
];
Checking Token Balances
The SDK provides methods to check jetton (token) balances on the TON side:
async function checkTokenBalance() {
const tacSdk = await TacSdk.create({
network: Network.TESTNET
});
const userAddress = "EQUserAddress...";
const tokenAddress = "EQTokenAddress...";
// Get simple balance
const balance = await tacSdk.getUserJettonBalance(userAddress, tokenAddress);
console.log(`Balance: ${balance} (raw amount)`);
// Get extended balance information
const extendedBalance = await tacSdk.getUserJettonBalanceExtended(userAddress, tokenAddress);
if (extendedBalance.exists) {
console.log(`Token exists for user`);
console.log(`Raw amount: ${extendedBalance.rawAmount}`);
console.log(`Decimals: ${extendedBalance.decimals}`);
console.log(`Formatted amount: ${extendedBalance.amount}`);
} else {
console.log(`User doesn't have this token yet`);
}
return extendedBalance;
}
Getting Jetton Wallet Address
You can also get the address of a user’s jetton wallet:
async function getJettonWalletAddress() {
const tacSdk = await TacSdk.create({
network: Network.TESTNET
});
const userAddress = "EQUserAddress...";
const tokenAddress = "EQTokenAddress...";
const walletAddress = await tacSdk.getUserJettonWalletAddress(userAddress, tokenAddress);
console.log(`Jetton wallet address: ${walletAddress}`);
return walletAddress;
}
Full implementation (Token Swap Example)
Here’s a complete example of bridging a token to perform a swap on an EVM DEX:
async function swapTokens() {
// Initialize SDK
const tacSdk = await TacSdk.create({
network: Network.TESTNET
});
// Get sender from wallet integration
const sender = /* your sender */;
// TON token to swap
const tonTokenAddress = "EQTokenAddress...";
// Get corresponding EVM address
const evmTokenAddress = await tacSdk.getEVMTokenAddress(tonTokenAddress);
// Destination token on EVM
const destTokenAddress = "0xDestTokenAddress...";
// Encode swap parameters
const abi = new ethers.AbiCoder();
const swapParams = abi.encode(
['tuple(uint256,uint256,address[],address,uint256)'],
[
[
ethers.parseUnits("10", 18), // amountIn
ethers.parseUnits("9.5", 18), // amountOutMin
[evmTokenAddress, destTokenAddress], // path
"0xUserEVMAddress", // recipient
Math.floor(Date.now() / 1000) + 3600 // deadline
]
]
);
// Create EVM message
const evmProxyMsg: EvmProxyMsg = {
evmTargetAddress: "0xDexProxyAddress",
methodName: "swapExactTokensForTokens(bytes,bytes)",
encodedParameters: swapParams
};
// Specify token to bridge
const assets: AssetBridgingData[] = [
{
address: tonTokenAddress,
amount: 10 // Amount to swap
}
];
// Send transaction
const transactionLinker = await tacSdk.sendCrossChainTransaction(
evmProxyMsg,
sender,
assets
);
console.log("Transaction sent! Linker:", transactionLinker);
return transactionLinker;
}