Setup

For this example we’ll use @ton/ton and @ton/crypto to interact with the contract, but you can replace it with any TON library of your choice.

Make sure to install it first:

yarn add @ton/ton @ton/crypto

Interacting with the Contract

To execute a bridge transaction, send a transfer with the following payload:

The commitmentId required by the contract corresponds to the quoteId returned by the API.

To obtain the commitmentId, first request a quote from the API, then commit it. The response will include the quoteId, which should be used as the commitmentId in the contract call.

See the API bridge example for details.

import {beginCell, toNano, Address, TonClient, internal, WalletContractV4} from '@ton/ton'
import {mnemonicToPrivateKey} from '@ton/crypto';
import getBridgeConfigs from './getBridgeConfigs'

const getJettonWalletAddress = async (tonWalletAddress, jettonMasterAddress, publicProvider) => {
  const jettonMaster = publicProvider.open(JettonMaster.create(jettonMasterAddress))

  return jettonMaster.getWalletAddress(tonWalletAddress)
}

export const callTonBridgeContract = async (
  {
    commitmentId,
    amount,
    token,
    tonWalletAddress,
  }) => {
  // See API Integration -> Fetch Bridge Configs for the full implementation
  const configs = await getBridgeConfigs()
  const chainConfig = configs[chain]

  // Replace with your wallet implementation
  const tonClient = new TonClient({
    endpoint: chainConfig.rpc,
  })
  const key = await mnemonicToPrivateKey(TON_MNEMONIC);
  const workchain = 0;
  const wallet = WalletContractV4.create({
    workchain,
    publicKey: keyPair.publicKey,
  })
  const walletProvider = tonClient.open(wallet)
  const tokenConfig = chainConfig.tokens[token]
  const amountWithDecimals = +amount * 10 ** tokenConfig.decimals
  const tokenAddress = tokenConfig.address
  const bridgeContractAddress = chainConfig.contractAddress

  const jettonWalletContract = await getJettonWalletAddress(
    Address.parse(tonWalletAddress),
    Address.parse(tokenAddress),
    tonClient
  )
  const sourceAddress = Address.parse(tonWalletAddress)
  const destinationAddress = Address.parse(bridgeContractAddress)

  const forwardPayload = beginCell()
    .storeUint(BigInt(`0x${commitmentId}`), 96)
    .endCell()

  const forwardAmount = 0.02

  const body = beginCell()
    .storeUint(0xf8a7ea5, 32) // opcode for jetton transfer
    .storeUint(0, 64) // query id
    .storeCoins(BigInt(amountWithDecimals)) // jetton amount, amount * 10^9
    .storeAddress(destinationAddress) // TON wallet destination address
    .storeAddress(sourceAddress) // response excess destination
    .storeBit(0) // no custom payload
    .storeCoins(toNano(forwardAmount)) // forward amount (if >0, will send notification message)
    .storeBit(1) // we store forwardPayload as a reference
    .storeRef(forwardPayload)
    .endCell()

  const seqno: number = await walletProvider.getSeqno();

  return await walletProvider.sendTransfer({
    seqno,
    secretKey: key.secretKey,
    timeout: Math.floor(Date.now() / 1000) + 360,
    messages: [
      internal({
        to: jettonWalletContract.toString(),
        value: toNano(0.1),
        body: body,
      }),
    ],
  })
}