Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions standard/tokens/jettons/transfer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,201 @@ async function main() {
void main();
```

## Transfer via TON Connect

To send jettons from a dApp through [TON Connect](/ecosystem/ton-connect/overview), build the [TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer) transfer body and submit it to the sender's jetton wallet contract:

<CodeGroup>
```tsx title="React" icon="react"
import { useTonConnectUI } from '@tonconnect/ui-react';
import { beginCell, toNano, Address } from '@ton/ton';

// transfer#0f8a7ea5 query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress
// response_destination:MsgAddress custom_payload:(Maybe ^Cell)
// forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell)
// = InternalMsgBody;

const body = beginCell()
.storeUint(0x0f8a7ea5, 32) // jetton transfer op code
.storeUint(0, 64) // query_id:uint64
.storeCoins(toNano('0.001')) // amount — decimals vary per token (6 for USDT, 9 default)
.storeAddress(Address.parse('<DESTINATION_WALLET>')) // destination:MsgAddress
.storeAddress(Address.parse('<SENDER_WALLET>')) // response_destination:MsgAddress
.storeUint(0, 1) // custom_payload:(Maybe ^Cell)
.storeCoins(toNano('0.05')) // forward_ton_amount — if >0, sends notification
.storeBit(1) // forward_payload as reference
.storeRef(beginCell().endCell()) // empty payload reference
.endCell();

const transaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: '<SENDER_JETTON_WALLET>', // sender's jetton wallet address
amount: toNano('0.05').toString(), // for gas fees, excess is returned
payload: body.toBoc().toString('base64'),
},
],
};

export const TransferJetton = () => {
const [tonConnectUI] = useTonConnectUI();

return (
<button onClick={() => tonConnectUI.sendTransaction(transaction)}>
Send jetton
</button>
);
};
```

```ts title="TypeScript" icon="globe"
import TonConnectUI from '@tonconnect/ui';
import { beginCell, toNano, Address } from '@ton/ton';

const tonConnectUI = new TonConnectUI({ manifestUrl: '<MANIFEST_URL>' });

const body = beginCell()
.storeUint(0x0f8a7ea5, 32)
.storeUint(0, 64)
.storeCoins(toNano('0.001'))
.storeAddress(Address.parse('<DESTINATION_WALLET>'))
.storeAddress(Address.parse('<SENDER_WALLET>'))
.storeUint(0, 1)
.storeCoins(toNano('0.05'))
.storeBit(1)
.storeRef(beginCell().endCell())
.endCell();

const transaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: '<SENDER_JETTON_WALLET>',
amount: toNano('0.05').toString(),
payload: body.toBoc().toString('base64'),
},
],
};

const result = await tonConnectUI.sendTransaction(transaction);
```
Comment thread
coalus marked this conversation as resolved.
</CodeGroup>

Where:

- `<DESTINATION_WALLET>` — the recipient's TON wallet address.
- `<SENDER_WALLET>` — the sender's TON wallet address (receives excess fees).
- `<SENDER_JETTON_WALLET>` — the sender's jetton wallet contract address. To find it, call the `get_wallet_address` get method on the jetton master contract, passing the sender's wallet address. See the [low-level example](#transfer-using-a-wallet-contract) below for the full derivation.
- `<MANIFEST_URL>` — URL of the dApp manifest. See [manifest](/ecosystem/ton-connect/manifest).

<Aside>
`toNano` assumes 9 decimals. For tokens with different decimals (for example, 6 for USDT), compute the amount manually: `BigInt(value) * 10n ** BigInt(jettonDecimals)`.
</Aside>

### Transfer with a comment

To attach a text comment, serialize it in the `forward_payload` field with opcode `0` (text comment). This snippet builds only the body — send it the same way as a [regular transfer](#transfer-via-ton-connect) above:

```ts
import { beginCell, toNano, Address } from '@ton/ton';

const forwardPayload = beginCell()
.storeUint(0, 32) // 0 opcode = text comment
.storeStringTail('Hello, TON!')
.endCell();

const body = beginCell()
.storeUint(0x0f8a7ea5, 32)
.storeUint(0, 64)
.storeCoins(toNano('5')) // jetton amount (decimals vary)
.storeAddress(Address.parse('<DESTINATION_WALLET>'))
.storeAddress(Address.parse('<SENDER_WALLET>'))
.storeBit(0) // no custom payload
.storeCoins(toNano('0.02')) // forward amount
.storeBit(1) // forward_payload as reference
.storeRef(forwardPayload)
.endCell();
```

### Burn via TON Connect

The burn body follows [TEP-74 burn](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#2-burn) (`burn#595f07bc`):

<CodeGroup>
```tsx title="React" icon="react"
import { useTonConnectUI } from '@tonconnect/ui-react';
import { beginCell, toNano, Address } from '@ton/ton';

// burn#595f07bc query_id:uint64 amount:(VarUInteger 16)
// response_destination:MsgAddress custom_payload:(Maybe ^Cell)

const body = beginCell()
.storeUint(0x595f07bc, 32) // jetton burn op code
.storeUint(0, 64) // query_id
.storeCoins(toNano('0.001')) // burn amount (decimals vary)
.storeAddress(Address.parse('<OWNER_WALLET>')) // response_destination
.storeUint(0, 1) // no custom payload
.endCell();

const transaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: '<OWNER_JETTON_WALLET>', // owner's jetton wallet
amount: toNano('0.05').toString(),
payload: body.toBoc().toString('base64'),
},
],
};

export const BurnJetton = () => {
const [tonConnectUI] = useTonConnectUI();

return (
<button onClick={() => tonConnectUI.sendTransaction(transaction)}>
Burn jetton
</button>
);
};
```

```ts title="TypeScript" icon="globe"
import TonConnectUI from '@tonconnect/ui';
import { beginCell, toNano, Address } from '@ton/ton';

const tonConnectUI = new TonConnectUI({ manifestUrl: '<MANIFEST_URL>' });

const body = beginCell()
.storeUint(0x595f07bc, 32)
.storeUint(0, 64)
.storeCoins(toNano('0.001'))
.storeAddress(Address.parse('<OWNER_WALLET>'))
.storeUint(0, 1)
.endCell();

const transaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: '<OWNER_JETTON_WALLET>',
amount: toNano('0.05').toString(),
payload: body.toBoc().toString('base64'),
},
],
};

const result = await tonConnectUI.sendTransaction(transaction);
```
</CodeGroup>

Where:

- `<OWNER_WALLET>` — the wallet address of the jetton owner (receives excess fees).
- `<OWNER_JETTON_WALLET>` — the owner's jetton wallet contract address.

## Transfer using a wallet contract

For reference, here's a low-level example of the process, where message serialization is done manually.

```ts expandable
Expand Down
Loading