> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# TON: How to interact with Jettons

> Interact with TON Jettons using TonWeb SDK. Fetch metadata, user balances, and transaction histories with JavaScript and Chainstack endpoints.

**TLDR:**

* Demonstrates how to use TonWeb (JavaScript SDK) with a Chainstack endpoint to fetch Jetton metadata, user balances, and transaction histories on the TON blockchain.
* Shows step-by-step examples of requesting token data via TonWeb or HTTP, highlighting the modular approach for Jetton data, balance, and transfer queries.
* Emphasizes the difference between the minter contract (master) and wallet contracts, and how to verify or fetch wallet addresses and check balances.
* Provides working code snippets to integrate these concepts easily into your application.

## Introduction

Jettons are the standard token implementation on the TON blockchain, analogous to ERC-20 tokens on Ethereum. They enable developers to create and manage custom tokens that can represent a variety of assets or utilities within decentralized applications. The standard for Jettons on TON is described in [TEP64](https://github.com/ton-blockchain/TEPs).

<Check>
  ### Get your own node endpoint today

  [Start for free](https://console.chainstack.com/) and get your app to production levels immediately. No credit card required.

  You can sign up with your GitHub, X, Google, or Microsoft account.
</Check>

### TON APIs

To interact with the TON blockchain, developers can use either HTTP APIs or the [ADNL protocol](https://docs.ton.org/v3/documentation/network/protocols/adnl/overview). HTTP APIs are simpler and suitable for most applications, while ADNL offers advanced low-level network communication. Chainstack dedicated nodes provide ADNL support for advanced use cases requiring direct network layer interaction.

The TON HTTP API has two versions: V2, which provides real-time data, and V3, which offers indexed blockchain data. In this tutorial, we will use TonWeb, which operates on the V2 JSON-RPC endpoint.

### Jettons on TON

Jettons are smart contracts on TON that follow a standard interface, allowing wallets and applications to interact with them uniformly. Each Jetton consists of:

1. Master (or minter) contract: the contract that manages the Jetton metadata and logic.
2. Wallet contract: a contract for each user holding the Jetton, managing the user's balance and transfers.

### Prerequisites

1. Node.js and npm installed.
2. [TonWeb](https://github.com/toncenter/tonweb) installed.
3. RPC endpoint from Chainstack.

TonWeb is a JavaScript SDK that allows developers to interact with the TON blockchain via HTTP APIs. Install it using npm:

<CodeGroup>
  ```Text Bash theme={"system"}
  npm install tonweb
  ```
</CodeGroup>

## Interactions

### Fetching Jetton metadata

Fetching Jetton metadata allows you to retrieve essential information about a specific Jetton token. To achieve the same using HTTP requests, you can use the `/getTokenData` endpoint of TON API v2.

<CodeGroup>
  ```javascript Javascript (TonWeb) theme={"system"}
  const TonWeb = require('tonweb');

  // Initialize TonWeb with a provider
  const tonweb = new TonWeb(
      new TonWeb.HttpProvider('https://ton-mainnet.core.chainstack.com/.../api/v2/jsonRPC')
  );

  // Replace with the actual Jetton master contract address
  const jettonMasterAddress = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'; // e.g., mainnet USDT

  async function fetchJettonMetadata(jettonMasterAddress) {
      try {
          const jettonMinter = new TonWeb.token.jetton.JettonMinter(tonweb.provider, {address: jettonMasterAddress});
          const data = await jettonMinter.getJettonData();

          console.log('Jetton Metadata:');
          console.log('Total supply:', data.totalSupply.toString());
          console.log('URI to off-chain metadata:', data.jettonContentUri);
      } catch (error) {
          console.error('Error fetching jetton metadata:', error);
      }
  }

  fetchJettonMetadata(jettonMasterAddress);
  ```

  ```javascript Javascript (HTTP) theme={"system"}
  const fetch = require('node-fetch');

  const apiUrl = 'https://ton-mainnet.core.chainstack.com/.../api/v2/getTokenData';
  const jettonMasterAddress = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs';

  async function fetchJettonMetadata(jettonMasterAddress) {
      try {
          const params = new URLSearchParams({
              address: jettonMasterAddress
          });
          const url = `${apiUrl}?${params.toString()}`;
          const response = await fetch(url);
          const data = (await response.json()).result;
          console.log('Jetton Metadata:');
          console.log('Total supply:', data.total_supply);
          console.log('Mintable:', data.mintable);
          console.log('Admin address:', data.admin_address);
          console.log('URI:', data.jetton_content.data.uri);
          console.log('Decimals:', data.jetton_content.data.decimals);
      } catch (error) {
          console.error('Error fetching jetton metadata:', error);
      }
  }

  fetchJettonMetadata(jettonMasterAddress);
  ```
</CodeGroup>

### Fetching user Jetton balance

To display a user's Jetton balance, you need to query the blockchain for the amount of a specific Jetton that the user holds. The first example shows how to obtain a Jetton wallet for a specific user.

<CodeGroup>
  ```javascript Javascript theme={"system"}
  const TonWeb = require('tonweb');

  const tonweb = new TonWeb(
      new TonWeb.HttpProvider('https://ton-mainnet.core.chainstack.com/.../api/v2/jsonRPC')
  );

  // Replace with the actual Jetton master contract address and owner wallet address
  const jettonMasterAddress = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'; // e.g., mainnet USDT
  const ownerWalletAddress = 'UQ...';

  async function fetchJettonWalletAddress(jettonMasterAddress, ownerWalletAddress) {
      try {
          const jettonMinter = new TonWeb.token.jetton.JettonMinter(tonweb.provider, {
              address: jettonMasterAddress
          });

          const jettonWalletAddress = await jettonMinter.getJettonWalletAddress(
              new TonWeb.utils.Address(ownerWalletAddress)
          );

          const jettonWallet = new TonWeb.token.jetton.JettonWallet(tonweb.provider, {
              address: jettonWalletAddress
          });

          const jettonData = await jettonWallet.getData();

          // Verify that the Jetton Minter address matches
          if (jettonData.jettonMinterAddress.toString(false) !== jettonMinter.address.toString(false)) {
              throw new Error('Jetton minter address from jetton wallet does not match the expected minter address');
          }

          console.log('Jetton wallet address:', jettonWalletAddress.toString(true, true, true));
      } catch (error) {
          console.error('Error fetching jetton wallet address:', error);
      }
  }

  fetchJettonWalletAddress(jettonMasterAddress, ownerWalletAddress);
  ```
</CodeGroup>

When the Jetton wallet is known, we can fetch its balance:

<CodeGroup>
  ```javascript Javascript theme={"system"}
  const TonWeb = require('tonweb');

  const tonweb = new TonWeb(
      new TonWeb.HttpProvider('https://ton-mainnet.core.chainstack.com/.../api/v2/jsonRPC')
  );

  // Replace with the actual Jetton wallet address
  const walletAddress = 'EQ...';

  async function fetchJettonBalance(walletAddress) {
      try {
          const jettonWallet = new TonWeb.token.jetton.JettonWallet(tonweb.provider, { address: walletAddress });
          const data = await jettonWallet.getData();

          console.log('Jetton balance:', data.balance.toString());
          console.log('Jetton owner address:', data.ownerAddress.toString(true, true, true));
      } catch (error) {
          console.error('Error fetching jetton balance:', error);
      }
  }

  fetchJettonBalance(walletAddress);
  ```
</CodeGroup>

### Fetching user Jetton transfers

Fetching a user's Jetton transfer history allows you to display past transactions involving the Jetton token for that user.

<CodeGroup>
  ```javascript Javascript theme={"system"}
  const TonWeb = require('tonweb');

  // Initialize TonWeb with a provider
  const tonweb = new TonWeb(
      new TonWeb.HttpProvider('https://ton-mainnet.core.chainstack.com/.../api/v2/jsonRPC')
  );

  // Replace with the actual Jetton wallet address (user's Jetton wallet address)
  const jettonWalletAddress = 'EQ...';

  async function fetchJettonTransferHistory(jettonWalletAddress) {
      try {
          const limit = 10;
          const transactions = await tonweb.provider.getTransactions(jettonWalletAddress, limit);
          console.log(`Last ${limit} transactions for Jetton wallet ${jettonWalletAddress}:`);

          for (const tx of transactions) {
              // Basic transaction info
              console.log('Transaction ID:', tx.transaction_id.hash);
              console.log('LT:', tx.transaction_id.lt);
              console.log('Timestamp:', new Date(tx.utime * 1000).toISOString());

              // Check if the transaction is an inbound or outbound message
              if (tx.in_msg && tx.in_msg.msg_data) {
                  console.log('Incoming Message Data:', tx.in_msg.msg_data);
              }

              if (tx.out_msgs && tx.out_msgs.length > 0) {
                  tx.out_msgs.forEach((outMsg, index) => {
                      console.log(`Outgoing Message ${index + 1} Data:`, outMsg.msg_data);
                  });
              }

              console.log('----------------------------------------');
          }
      } catch (error) {
          console.error('Error fetching jetton transfer history:', error);
      }
  }

  fetchJettonTransferHistory(jettonWalletAddress);
  ```
</CodeGroup>

## Conlcusion

TonWeb is a JavaScript SDK that simplifies the process of interacting with Jetton contracts on the TON blockchain. In this tutorial, we explored how to peform basic fetching operations with Jettons.

### About the author

<CardGroup>
  <Card title="Anton Sauchyk">
    <img src="https://mintcdn.com/chainstack/UN3rP7zhB69idvnC/images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg?fit=max&auto=format&n=UN3rP7zhB69idvnC&q=85&s=1d13d3584862f1be3a84fa3f8e38fea7" alt="Anton Sauchyk" style={{width: '80px', height: '80px', borderRadius: '50%', objectFit: 'cover', display: 'block', margin: '0 auto'}} noZoom width="400" height="400" data-path="images/docs/profile_images/1817926677730664448/3nNn0T2p_400x400.jpg" />

    <Icon icon="code" iconType="solid" /> Developer Advocate @ Chainstack
    <br /><Icon icon="laptop" iconType="solid" /> Multiple years of software development and Web3 expertise

    <div style={{display: "flex", justifyContent: "center", gap: "12px"}}>
      <a href="https://github.com/smypmsa" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="github" iconType="brands" />
      </a>

      <a href="https://x.com/sensuniama" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="twitter" iconType="brands" />
      </a>

      <a href="https://www.linkedin.com/in/anton-sauchyk/" style={{textDecoration: "none", borderBottom: "none"}}>
        <Icon icon="linkedin" iconType="brands" />
      </a>
    </div>
  </Card>
</CardGroup>
