Send/Claim Mintless Jetton
This example contains information on how to claim or send mintless Jettons using TonapiClient.
Note
You need an API key from tonconsole.com.
Send Mintless Jetton
from typing import Any, Dict, Union
import aiohttp
from aiohttp import ClientResponseError
from pytoniq_core import Address, Cell, Slice, StateInit, begin_cell
from tonutils.client import TonapiClient
from tonutils.jetton import JettonWalletStandard
from tonutils.utils import to_amount
from tonutils.wallet import WalletV4R2
# API key for accessing the Tonapi (obtainable from https://tonconsole.com)
API_KEY = ""
# Mnemonic phrase
MNEMONIC = "word1 word2 word3 ..."
# The address of the Jetton Master contract
JETTON_MASTER_ADDRESS = "EQ..."
# The address of the recipient
DESTINATION_ADDRESS = "UQ..."
# Comment for transfer payload
COMMENT = "Hello from tonutils!"
async def main() -> None:
client = TonapiClient(api_key=API_KEY)
wallet, _, _, _ = WalletV4R2.from_mnemonic(client, MNEMONIC)
jetton_data = await get_jetton(client, wallet.address.to_str())
if jetton_data is None:
raise Exception("Jetton data not found. Are there jettons in this wallet?")
jetton_balance = int(jetton_data["balance"])
custom_payload_api_uri = jetton_data["jetton"]["custom_payload_api_uri"]
jetton_custom_payload = await get_payload(custom_payload_api_uri, wallet.address.to_str())
jetton_wallet_address = jetton_custom_payload["jetton_wallet"]
if not await is_claimed(client, jetton_wallet_address):
custom_payload = Cell.one_from_boc(jetton_custom_payload["custom_payload"])
state_init = StateInit.deserialize(Slice.one_from_boc(jetton_custom_payload["state_init"]))
else:
custom_payload = state_init = None
body = JettonWalletStandard.build_transfer_body(
recipient_address=Address(DESTINATION_ADDRESS),
response_address=wallet.address,
jetton_amount=jetton_balance,
custom_payload=custom_payload,
forward_payload=(
begin_cell()
.store_uint(0, 32)
.store_snake_string(COMMENT)
.end_cell()
),
forward_amount=1,
)
tx_hash = await wallet.transfer(
destination=jetton_wallet_address,
amount=0.1,
body=body,
state_init=state_init,
bounce=True,
)
print(f"Successfully transferred {to_amount(jetton_balance)} jettons!")
print(f"Transaction hash: {tx_hash}")
async def get_jetton(client: TonapiClient, addr: str) -> Union[Dict[str, Any], None]:
method = f"/v2/accounts/{addr}/jettons"
params = {"supported_extensions": "custom_payload"}
try:
result = await client._request("GET", path=method, params=params) # noqa
return next(
(b for b in result.get("balances", [])
if Address(b["jetton"]["address"]) == Address(JETTON_MASTER_ADDRESS)),
None
)
except Exception as e:
print(f"Error fetching jetton data: {e}")
return None
async def get_payload(api_uri: str, wallet_address: str) -> Dict[str, Any]:
async with aiohttp.ClientSession() as session:
async with session.get(f"{api_uri}/wallet/{wallet_address}") as response:
response.raise_for_status()
return await response.json()
async def is_claimed(client: TonapiClient, jetton_addr: str) -> bool:
try:
result = await client.run_get_method(jetton_addr, "is_claimed")
return bool(result[0])
except ClientResponseError as e:
if e.status == 404:
return False
raise
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Claim Mintless Jetton
from typing import Any, Dict, Union
import aiohttp
from aiohttp import ClientResponseError
from pytoniq_core import Address, Cell, Slice, StateInit
from tonutils.client import TonapiClient
from tonutils.jetton import JettonWalletStandard
from tonutils.utils import to_amount
from tonutils.wallet import WalletV4R2
# API key for accessing the Tonapi (obtainable from https://tonconsole.com)
API_KEY = ""
# Mnemonic phrase
MNEMONIC = "word1 word2 word3 ..."
# The address of the Jetton Master contract
JETTON_MASTER_ADDRESS = "EQ..."
async def main() -> None:
client = TonapiClient(api_key=API_KEY)
wallet, _, _, _ = WalletV4R2.from_mnemonic(client, MNEMONIC)
jetton_data = await get_jetton(client, wallet.address.to_str())
if jetton_data is None:
raise Exception("Jetton data not found. Are there jettons in this wallet?")
jetton_balance = int(jetton_data["balance"])
custom_payload_api_uri = jetton_data["jetton"]["custom_payload_api_uri"]
jetton_custom_payload = await get_payload(custom_payload_api_uri, wallet.address.to_str())
jetton_wallet_address = jetton_custom_payload["jetton_wallet"]
if not await is_claimed(client, jetton_wallet_address):
custom_payload = Cell.one_from_boc(jetton_custom_payload["custom_payload"])
state_init = StateInit.deserialize(Slice.one_from_boc(jetton_custom_payload["state_init"]))
else:
print("Jetton already claimed!")
return
body = JettonWalletStandard.build_transfer_body(
recipient_address=wallet.address,
response_address=wallet.address,
jetton_amount=jetton_balance,
custom_payload=custom_payload,
)
tx_hash = await wallet.transfer(
destination=jetton_wallet_address,
amount=0.1,
body=body,
state_init=state_init,
bounce=True,
)
print(f"Successfully claimed {to_amount(jetton_balance)} jettons!")
print(f"Transaction hash: {tx_hash}")
async def get_jetton(client: TonapiClient, addr: str) -> Union[Dict[str, Any], None]:
method = f"/v2/accounts/{addr}/jettons"
params = {"supported_extensions": "custom_payload"}
try:
result = await client._request("GET", path=method, params=params) # noqa
return next(
(b for b in result.get("balances", [])
if Address(b["jetton"]["address"]) == Address(JETTON_MASTER_ADDRESS)),
None
)
except Exception as e:
print(f"Error fetching jetton data: {e}")
return None
async def get_payload(api_uri: str, wallet_address: str) -> Dict[str, Any]:
async with aiohttp.ClientSession() as session:
async with session.get(f"{api_uri}/wallet/{wallet_address}") as response:
response.raise_for_status()
return await response.json()
async def is_claimed(client: TonapiClient, jetton_addr: str) -> bool:
try:
result = await client.run_get_method(jetton_addr, "is_claimed")
return bool(result[0])
except ClientResponseError as e:
if e.status == 404:
return False
raise
if __name__ == "__main__":
import asyncio
asyncio.run(main())