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;
}