Перейти к содержанию

Отправка/Запрос Mintless Jetton

Это пример содержит информацию о том, как запрашивать (claim) или отправлять Jetton Mintless с использованием TonapiClient.

Note

Для работы необходим API-ключ, который можно получить на tonconsole.com.


Отправить 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())

Запросить 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())