# How to send jettons with AppKit (https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/send-jettons/content.md)



Transfer a jetton (a TON fungible token) from the connected wallet to another address.

## How it works [#how-it-works]

Jettons are TON fungible tokens. Each holder has a jetton wallet contract derived from `(jettonMaster, ownerAddress)` — the owner is the user's GRAM wallet, and the jetton wallet is a separate contract owned by it. Transfer UI asks for the recipient's owner address, not their jetton wallet address. AppKit derives the sender's jetton wallet internally (that's the destination of the outer TON message), builds the inner jetton transfer payload that names the recipient's owner address, and wraps it in the outer message. The sender's jetton wallet then forwards the transfer to the recipient's jetton wallet on-chain. For example, USDT on TON is implemented as a jetton with the master address `EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs`.

Each jetton stores `name`, `symbol`, and `decimals` in its metadata. Amounts are decimal strings in the jetton's own units, so read the `decimals` field from chain metadata before building a transfer — different jettons can use different precision. USDT uses 6 decimals, so `'0.1'` represents 100,000 micro-USDT.

<Callout>
  To deploy and mint a new jetton on the mainnet without writing code, use the dedicated official tool: [TON MINTER](https://minter.ton.org).
</Callout>

## Before you begin [#before-you-begin]

A connected wallet, the React provider mounted, and the jetton's master address. See [Connect to a wallet](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/connect-to-a-wallet/content.md) and [Read balances](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/read-balances/content.md) to discover what the wallet holds.

<Callout type="caution" title="Funds at risk">
  Transferring without accounting for `decimals` can send drastically more or fewer tokens than intended. Verify the `decimals` value from chain metadata before calculating transfer amounts.
</Callout>

Before opening the wallet, make sure the sender's wallet has enough Gram to cover the [fees](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/blockchain-basics/primitives/fees/content.md).

## Component (recommended) [#component-recommended]

`<SendJettonButton />` accepts a `jetton` descriptor with `address`, `symbol`, and `decimals`.

```tsx
import { SendJettonButton } from '@ton/appkit-react';
import { getErrorMessage } from '@ton/appkit';

<SendJettonButton
  jetton={{ address: 'EQ...', symbol: 'USDT', decimals: 6 }}
  recipientAddress="EQ..."
  amount="1.0"
  comment="Invoice 42"
  onError={(e) => console.error(getErrorMessage(e))}
  onSuccess={({ boc }) => console.log('sent', boc)}
>
  {({ isLoading, onSubmit, disabled, text }) => (
    <button onClick={onSubmit} disabled={disabled}>
      {text}
    </button>
  )}
</SendJettonButton>
```

`amount` is a decimal string in jetton units (so `'1.0'` for one full token, regardless of `decimals`).

## Hook [#hook]

```tsx
import { useTransferJetton } from '@ton/appkit-react';

const transferJetton = useTransferJetton();

transferJetton.mutate({
  recipientAddress: 'EQ...',
  jettonAddress: 'EQ...',
  amount: '1.0',
  jettonDecimals: 6,
});
```

## Core action [#core-action]

```ts
import { transferJetton } from '@ton/appkit';

await transferJetton(appKit, {
  recipientAddress: 'EQ...',
  jettonAddress: 'EQ...',
  amount: '1.0',
  jettonDecimals: 6,
});
```

## Look up jetton metadata [#look-up-jetton-metadata]

When you need the symbol, decimals, or icon for a UI, call `getJettonInfo` and pass the result through `getFormattedJettonInfo`.

```ts
import { getJettonInfo, getFormattedJettonInfo } from '@ton/appkit';

const jetton = await getJettonInfo(appKit, { address: 'EQ...' });
const formatted = getFormattedJettonInfo(jetton);
// formatted: { name, symbol, decimals, balance, image, address }
```

## Parameters [#parameters]

| Field              | Type     | Required | Notes                                   |
| ------------------ | -------- | -------- | --------------------------------------- |
| `recipientAddress` | `string` | Yes      | TON address of the recipient.           |
| `jettonAddress`    | `string` | Yes      | Address of the jetton master contract.  |
| `amount`           | `string` | Yes      | Decimal jetton units, e.g. `'1.0'`.     |
| `jettonDecimals`   | `number` | Yes      | Decimal precision of the jetton.        |
| `comment`          | `string` | No       | Plain text memo on the jetton transfer. |

## Confirm settlement [#confirm-settlement]

Jetton transfers settle through the same trace API as Gram transfers, but typically produce multiple internal messages. Use `getTransactionStatus(appKit, { boc })` and inspect `onchainMessages`, `pendingMessages`, and `totalMessages` to track progress; treat only `status === 'completed'` as final.

Track the transaction with [Streaming](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/streaming/content.md) and verify the recipient's jetton balance in your backend before delivering value.

If the recipient has never held this jetton, the transfer deploys the recipient's jetton wallet contract on first receive, which costs additional gas. The default outer-GRAM allowance on a built `transferJetton` request is designed to cover this case.

## Common failures [#common-failures]

| Failure              | Meaning                                                                                                                      |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| User rejected        | The user closed or rejected the wallet request.                                                                              |
| Wrong decimals       | The amount was scaled with the wrong `decimals` value. The transfer may move drastically more or fewer tokens than intended. |
| Insufficient balance | The wallet cannot cover the jetton amount or the outer-GRAM fee allowance.                                                   |
| Invalid address      | The recipient or jetton master address is malformed or for the wrong network.                                                |

## Tips [#tips]

* Always pass the destination **owner address** as `recipientAddress`. AppKit derives the sender's jetton wallet contract and writes the destination owner address into the jetton transfer payload.
* Read decimals from chain metadata (`getJettonInfo`) before building a transfer. Different jettons can use different precision.
* Treat the `boc` from `sendTransaction` as "user signed". Verify settlement with `getTransactionStatus` before crediting an order.
* Refetch the user's jetton list and balances after settlement.
* Do not display jetton metadata without sanitizing — names and images are user-supplied.

## Code example [#code-example]

See a [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) of jetton transfers built with these mutations — [try it live](https://appkit-minter.vercel.app/).

## Related pages [#related-pages]

* [Read balances](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/read-balances/content.md)
* [Send Gram](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/send-gram/content.md)
* [Use UI widgets](https://docs-rbcpr9qys-ton-core-docs.vercel.app/llms/applications/appkit/howto/use-ui-widgets/content.md)
