### Get Started with Tonconsole Development Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/README.md This snippet outlines the essential steps to set up and run the Tonconsole project locally for development. It covers installing project dependencies, configuring local environment variables, and starting the development server. ```Shell npm i cp .env.example .env.local npm run dev ``` -------------------------------- ### Install TON API Client and Core Libraries Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Commands to install the `@ton-api/client` for interacting with TON API and `@ton/core` for core TON functionalities. ```bash npm install @ton-api/client @ton/core ``` -------------------------------- ### Install TonConnect UI React Library Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Command to install the necessary library for integrating TonConnect UI into the React application. ```bash npm install @tonconnect/ui-react ``` -------------------------------- ### Launch Development Server for TON dApp (Bash) Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Command to start the local development server for the dApp, making it accessible via a web browser, typically at `http://localhost:3000`. ```bash npm run dev ``` -------------------------------- ### Install Vite Plugin for Node Polyfills Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Command to install `vite-plugin-node-polyfills` to ensure `@ton/core` works correctly in a browser environment by providing necessary polyfills. ```bash npm install vite-plugin-node-polyfills ``` -------------------------------- ### Install OpenAPI Generator CLI Globally Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/sdk.mdx This command installs the OpenAPI Generator command-line interface globally using npm, allowing it to be used from any directory. ```Bash npm install @openapitools/openapi-generator-cli -g ``` -------------------------------- ### Generate TonAPI SDK with OpenAPI Generator CLI Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/sdk.mdx This command demonstrates how to generate an SDK from the TonAPI OpenAPI specification. The example uses Ruby as the target language, but the `-g` parameter can be modified for other languages. It also notes the `--openapi-normalizer` option for handling multiple tags in Swagger files. ```Bash npx openapi-generator-cli generate -i https://raw.githubusercontent.com/tonkeeper/opentonapi/master/api/openapi.yml -g ruby -o /path/to/output ``` -------------------------------- ### Perform Gasless Jetton Transfer on TON with Go Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.go.mdx This Go example demonstrates how to initiate and complete a gasless jetton transfer on the TON network. It covers retrieving gasless configuration, estimating transaction fees, signing messages locally using a seed, and sending the signed transaction via TonAPI. The example specifically uses USDt as the jetton and highlights the role of a relay address for fee optimization. Remember to replace placeholder values for 'seed' and 'destination' before running. ```Go package main import ( "context" "crypto/ed25519" "encoding/hex" "fmt" "math/big" "time" "github.com/shopspring/decimal" "github.com/tonkeeper/tonapi-go" "github.com/tonkeeper/tongo" "github.com/tonkeeper/tongo/boc" "github.com/tonkeeper/tongo/contract/jetton" "github.com/tonkeeper/tongo/liteapi" "github.com/tonkeeper/tongo/tlb" "github.com/tonkeeper/tongo/ton" tongoWallet "github.com/tonkeeper/tongo/wallet" ) func printConfigAndReturnRelayAddress(tonapiCli *tonapi.Client) (ton.AccountID, error) { cfg, err := tonapiCli.GaslessConfig(context.Background()) if err != nil { return ton.AccountID{}, fmt.Errorf("failed to get gasless config: %w", err) } fmt.Printf("Available gas jettons:\n") for _, gasJetton := range cfg.GasJettons { fmt.Printf("Gas jetton master: %s\n", gasJetton.MasterID) } fmt.Printf("Relay address to send fees to: %v\n", cfg.RelayAddress) relayer := ton.MustParseAccountID(cfg.RelayAddress) return relayer, nil } func main() { // this is a simple example of how to send a gasless transfer. // you only need to specify your seed and a destination address. // the seed is not sent to the network, it is used to sign messages locally. seed := "..!!! REPLACE THIS WITH YOUR SEED !!! .." destination := ton.MustParseAccountID("... !!! REPLACE THIS WITH A CORRECT DESTINATION !!! ....") // we send 1 USDt to the destination. usdtMaster := ton.MustParseAccountID("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs") // USDt jetton master. jettonAmount := int64(1_000_000) // amount in the smallest jetton units. This is 1 USDt. // if you need to send lots of requests in parallel, // make sure you use a tonapi token. tonapiCli, err := tonapi.New() if err != nil { panic(err) } cli, err := liteapi.NewClient(liteapi.Mainnet()) if err != nil { panic(err) } // we use USDt in this example, // so we just print all supported gas jettons and get the relay address. // we have to send excess to the relay address in order to make a transfer cheaper. relay, err := printConfigAndReturnRelayAddress(tonapiCli) if err != nil { panic(err) } params := tonapi.GaslessEstimateParams{ MasterID: usdtMaster.ToRaw(), } j := jetton.New(usdtMaster, cli) walletPrivateKey, err := tongoWallet.SeedToPrivateKey(seed) if err != nil { panic(err) } networkID, err := cli.GetNetworkGlobalID(context.Background()) if err != nil { panic(err) } opts := tongoWallet.Options{ NetworkGlobalID: &networkID, } w5 := tongoWallet.NewWalletV5R1(walletPrivateKey.Public().(ed25519.PublicKey), opts) msgCh := make(chan tlb.Message, 1) // this is a trick with proxy. we don't want to send the original transaction to the network. // we pass the proxy to Wallet's New function and intercepts the outgoing message. proxy := &proxy{ msgCh: msgCh, cli: cli, } wallet, err := tongoWallet.New(walletPrivateKey, tongoWallet.V5R1, proxy, tongoWallet.WithNetworkGlobalID(networkID)) if err != nil { panic(err) } fmt.Printf("Wallet address: %v\n", wallet.GetAddress()) walletAddress := wallet.GetAddress() msg := jetton.TransferMessage{ Jetton: j, Sender: walletAddress, JettonAmount: big.NewInt(jettonAmount), Destination: destination, ResponseDestination: &relay, // excess, because some TONs will be sent back to the relay address, commission will be lowered. AttachedTon: 50_000_000, ForwardTonAmount: 1, } err = wallet.Send(context.Background(), msg) if err != nil { panic(err) } m := <-msgCh cell := boc.NewCell() if err := tlb.Marshal(cell, m); err != nil { panic(err) } rawMessages, err := tongoWallet.ExtractRawMessages(tongoWallet.V5R1, cell) if err != nil { panic(err) } if len(rawMessages) != 1 { panic("invalid rawMessages") } msgBoc, err := rawMessages[0].Message.ToBocString() if err != nil { panic(err) } // msgBoc is our transfer message. publicKey := walletPrivateKey.Public().(ed25519.PublicKey) estimateReq := tonapi.GaslessEstimateReq{ WalletAddress: walletAddress.ToRaw(), WalletPublicKey: hex.EncodeToString(publicKey), Messages: []tonapi.GaslessEstimateReqMessagesItem{ {Boc: msgBoc}, }, } // we send a single message containing a transfer from our wallet to a desired destination. // as a result of estimation, TonAPI returns a list of messages that we need to sign. // the first message is a fee transfer to the relay address, the second message is our original transfer. signRawParams, err := tonapiCli.GaslessEstimate(context.Background(), &estimateReq, params) if err != nil { panic(err) } // signRawParams is the same structure as signRawParams in tonconnect. var msgs []tongoWallet.Sendable for _, msg := range signRawParams.Messages { cells, err := boc.DeserializeBocHex(msg.Payload.Value) if err != nil { panic(err) } if len(cells) != 1 { panic("invalid cells") } ``` -------------------------------- ### Fetch GraphQL Data with Node.js using graphql-request Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/graphql.mdx This snippet provides an example of fetching data from the Tonkeeper GraphQL API in Node.js using the `graphql-request` library. It includes the installation command and a JavaScript example to retrieve the first account, emphasizing that `id` and `name` are placeholder fields. ```bash npm install graphql-request ``` ```js const { request } = require('graphql-request'); // Define the GraphQL query structure const query = "\n query {\n allAccounts(first: 1) {\n nodes {\n id\n name\n }\n }\n }\n"; // The endpoint where we will be sending our GraphQL request const endpoint = 'https://tonapi.io/v2/graphql'; request(endpoint, query) .then(response => console.log(response)) .catch(error => console.error(error)); ``` -------------------------------- ### Initialize React Project with Vite and TypeScript Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Commands to create a new React project using Vite with the TypeScript template and navigate into the project directory. ```bash npm create vite@latest tonapi-dapp -- --template react-ts cd tonapi-dapp ``` -------------------------------- ### Fetch GraphQL Data with Node.js using Axios Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/graphql.mdx This snippet demonstrates how to query the Tonkeeper GraphQL API from a Node.js environment using the `axios` library. It includes the necessary `npm install` command and a JavaScript example to fetch the first account, noting that `id` and `name` are placeholder fields. ```bash npm install axios ``` ```js const axios = require('axios'); // Define the GraphQL query structure const query = "\n query {\n allAccounts(first: 1) {\n nodes {\n id\n name\n }\n }\n }\n"; // The endpoint where we will be sending our GraphQL request const endpoint = 'https://tonapi.io/v2/graphql'; axios.post(endpoint, {query}) .then(response => { const { data } = response.data; console.log(data); }) .catch(error => console.error(error)); ``` -------------------------------- ### Create Production Build for Tonconsole Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/README.md This snippet provides the commands necessary to create a production-ready build of the Tonconsole project. It ensures a clean installation of dependencies and then compiles the project for deployment. ```Shell npm ci npm run build ``` -------------------------------- ### Set TON API Key in Environment File Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Instructions to add the `VITE_TONAPI_KEY` to the `.env` file, securing the API key for the application. ```plaintext VITE_TONAPI_KEY=your-api-key ``` -------------------------------- ### Invoice Creation API Response Example Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/invoices.mdx Example JSON response returned by Tonconsole after a successful invoice creation request, including invoice details and a payment link. ```json { "id": "string", "status": "pending", "amount": "string", "description": "string", "date_create": 1234567890, "date_expire": 1234567890, "payment_link": "string", "pay_to_address": "0:" } ``` -------------------------------- ### Executing Main Application Logic Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.js.mdx This line initiates the main application function, `main()`, and includes error handling to catch and log any exceptions that occur during its execution. ```JavaScript main().catch(console.error); ``` -------------------------------- ### Create TON API Client Instance Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Creates `src/tonapi.ts` to initialize and export a `TonApiClient` instance, configured with the base URL and an API key from environment variables. ```ts import { TonApiClient } from '@ton-api/client'; const ta = new TonApiClient({ baseUrl: 'https://tonapi.io', apiKey: import.meta.env.VITE_TONAPI_KEY || undefined, }); export default ta; ``` -------------------------------- ### SQL Schema for Get Plugin List Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/analytics/scheme.mdx Defines the SQL table schema for retrieving a list of plugins associated with a wallet account, including the plugin's account ID. ```SQL create table getmethods.get_plugin_list ( wallet_account_id text NOT NULL, plugin_account_id text NOT NULL, indexer_last_update_lt bigint ); ``` -------------------------------- ### Verifying TonConnect Signatures Off-chain in JavaScript Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/academy/sign-data.mdx This example demonstrates how to verify `text` and `binary` TonConnect signatures off-chain using the `tweetnacl` library in JavaScript. It emphasizes the importance of reconstructing the message identically and matching the signature address with the requested wallet. ```JavaScript import nacl from 'tweetnacl'; const isValid = await nacl.sign.detached.verify(hash, signature, publicKey); ``` -------------------------------- ### Execute Main Application Function Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.ts.mdx This line executes the 'main' asynchronous function and provides a global error handler to catch and log any unhandled exceptions that occur during its execution. ```TypeScript main().catch(console.error); ``` -------------------------------- ### Next.js Page Setup with Component Imports and Static Props Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/rest-api/extra-currency.mdx This snippet demonstrates how a Next.js page is configured, importing necessary components and constants for documentation and data loading, and exporting a static props function to pre-render the page. ```TypeScript import { Callout } from "nextra-theme-docs"; import { SwaggerLink, SchemaLoader } from "@/components"; import { SWAGGER_TAGS } from "@/constants"; import { loadStatic } from "@/utils"; export const getStaticProps = loadStatic; ``` -------------------------------- ### Example CSV Format for Jetton Airdrop File Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/jettons/airdrop.mdx This snippet provides an example of the required CSV format for the Airdrop file. It must include a header with 'recipient' and 'amount' columns, where 'amount' is in minimal indivisible units and 'recipient' is a TON wallet address. ```csv recipient,amount 0:d0ef5351eb05503c10a447543113b315e772d147c51aaa81aa308b5cae99e07e,2567953998 0:199b41bcda2472b057d72f80e2e8ba5e15dcb776e68c9eeea393dad668248068,3127429615 0:aa1e7af003374652e13f705b8c7010b3cf8708f5f98f43c999b8ec4e2c0e0cdf,562536354 ``` -------------------------------- ### Perform a Gasless Jetton Transfer on TON with TonAPI Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.js.mdx This JavaScript code snippet illustrates the end-to-end process of sending a gasless jetton transfer. It involves importing necessary TON libraries, configuring the TonClient, defining jetton parameters (like USDT master address and amount), fetching the sender's jetton wallet address via TonAPI, constructing the jetton transfer payload, estimating the gasless transaction using TonAPI's `/gasless/estimate` endpoint, and finally creating and signing the wallet transfer. The example uses `WalletContractV5R1` and requires a mnemonic seed for signing. It highlights how TonAPI returns a list of messages for signing, including a fee transfer to a relay address and the original jetton transfer. Note that the `printConfigAndReturnRelayAddress()` function is called but not defined in this snippet. ```javascript import { storeMessageRelaxed, WalletContractV5R1, TonClient } from '@ton/ton'; import { Address, beginCell, internal, toNano, SendMode, external, storeMessage, Cell } from '@ton/core'; import { mnemonicToPrivateKey } from '@ton/crypto'; // if you need to send lots of requests in parallel, // make sure you use a tonapi token. const tonApiBaseUrl = 'https://tonapi.io/v2'; // const apiKey = 'your_token_here'; const tc = new TonClient({ endpoint: 'https://toncenter.com/api/v2/jsonRPC', }); const JETTON_TRANSFER_OP_CODE = 0xf8a7ea5; // Amount for jetton transfer. Usually 0.05 TON is enough for most jetton transfers without forwardBody const BASE_JETTON_SEND_AMOUNT = toNano(0.05); const main = async () => { // this is a simple example of how to send a gasless transfer. // you only need to specify your seed and a destination address. // the seed is not sent to the network, it is used to sign messages locally. const seed = '..!!! REPLACE THIS WITH YOUR SEED !!! ..'; // wallet seed `word1 word2 word3 ... word24` const destination = Address.parse('..!!! REPLACE THIS WITH A CORRECT DESTINATION !!!..'); // replace with a correct recipient address const usdtMaster = Address.parse('EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'); // USDt jetton master. const jettonAmount = 1_000_000n; // amount in the smallest jetton units. This is 1 USDt. const keyPair = await mnemonicToPrivateKey(seed.split(' ')); const workChain = 0; const wallet = WalletContractV5R1.create({ workChain, publicKey: keyPair.publicKey }); const contract = tc.open(wallet); console.log('Wallet address:', wallet.address.toString()); const jettonWalletAddressResult = await fetch( `${tonApiBaseUrl}/blockchain/accounts/${usdtMaster}/methods/${'get_wallet_address'}?args=${wallet.address.toRawString()}` ).then(res => res.json()); const jettonWallet = Address.parse(jettonWalletAddressResult.decoded.jetton_wallet_address); // we use USDt in this example, // so we just print all supported gas jettons and get the relay address. // we have to send excess to the relay address in order to make a transfer cheaper. const relayerAddress = await printConfigAndReturnRelayAddress(); // Create payload for jetton transfer const tetherTransferPayload = beginCell() .storeUint(JETTON_TRANSFER_OP_CODE, 32) .storeUint(0, 64) .storeCoins(jettonAmount) // 1 USDT .storeAddress(destination) // address for receiver .storeAddress(relayerAddress) // address for excesses .storeBit(false) // null custom_payload .storeCoins(1n) // count of forward transfers in nanoton .storeMaybeRef(undefined) .endCell(); const messageToEstimate = beginCell() .storeWritable( storeMessageRelaxed( internal({ to: jettonWallet, bounce: true, value: BASE_JETTON_SEND_AMOUNT, body: tetherTransferPayload }) ) ) .endCell(); // we send a single message containing a transfer from our wallet to a desired destination. // as a result of estimation, TonAPI returns a list of messages that we need to sign. // the first message is a fee transfer to the relay address, the second message is our original transfer. const params = await fetch(`${tonApiBaseUrl}/gasless/estimate/${usdtMaster}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ wallet_address: wallet.address.toString(), wallet_public_key: keyPair.publicKey.toString('hex'), messages: [ { boc: messageToEstimate.toBoc().toString('hex') } ] }) }) .then(res => res.json()) .then(res => ({ ...res, messages: res.messages.map(message => ({ to: Address.parse(message.address), value: BigInt(message.amount), body: Cell.fromBase64(Buffer.from(message.payload, 'hex').toString('base64')) }) )})); console.log('Estimated transfer:', params); const seqno = await contract.getSeqno(); // params is the same structure as params in tonconnect const tetherTransferForSend = wallet.createTransfer({ seqno, authType: 'internal', timeout: Math.ceil(Date.now() / 1000) + 5 * 60, secretKey: keyPair.secretKey, sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, messages: params.messages.map(message => internal(message)) }); const extMessage = beginCell() .storeWritable( storeMessage( external({ to: contract.address, ``` -------------------------------- ### Example: SSE Message for New Block Notification Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/streaming-api.mdx An example of a 'message' event data structure received from the SSE `/v2/sse/blocks` endpoint, showing details of a new block including workchain, shard, sequence number, and hash values. ```text event: message id: 1702660194645470966 data: {"workchain":0,"shard":"8000000000000000","seqno":40695756,"root_hash":"4506cf117b96acf84717ec4b57d5d46011e94d5722681c3e4ecc8eb54932e7f8","file_hash":"97d993d43df9da463b349a6008baf3b7df229686489150a770a9fd1ffcb2d3f5"} ``` -------------------------------- ### Configure Vite for Node Polyfills and Base Path Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Updates `vite.config.ts` to include the `nodePolyfills` plugin and set a base path for the application, resolving browser compatibility issues for `@ton/core`. ```ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; export default defineConfig({ plugins: [nodePolyfills(), react()], base: '/tonapi-dapp-example/', }); ``` -------------------------------- ### API Authorization using Query String Token Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/streaming-api.mdx Example of an API key provided as a 'token' parameter in the query string of the URL. This method is an alternative for authorization. ```text https://tonapi.io/v2/sse/accounts/transactions?accounts=ALL&token=eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9 ``` -------------------------------- ### Webhook Payload Example (JSON) Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/invoices.mdx Example JSON structure of a webhook payload sent by Tonconsole, illustrating the data received when an invoice status changes. ```json { "id": "string", "status": "pending | paid | cancelled | expired", "amount": "string", "description": "string", "date_create": 1234567890, "date_expire": 1234567890, "date_change": 1234567890, "payment_link": "string", "pay_to_address": "0:", "paid_by_address": "0:", "overpayment": "string" } ``` -------------------------------- ### SQL Schema for Get Next Proof Information Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/analytics/scheme.mdx Defines the SQL table schema for retrieving information about the next proof, including its ID, last proof time, and maximum span. ```SQL create table getmethods.get_next_proof_info ( account_id text NOT NULL, next_proof bigint NOT NULL, last_proof_time bigint NOT NULL, max_span bigint NOT NULL, indexer_last_update_lt bigint NOT NULL ); ``` -------------------------------- ### Example Calculation of Recipient's Total Claim Fee Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/jettons/airdrop.mdx This example illustrates how the total amount a recipient pays for claiming Jettons is calculated, combining the fixed 'claim_fee' set by the Airdrop admin and the variable 'blockchain_fee' for transaction processing. ```APIDOC claim_fee = 0.15 TON blockchain_fee(forward_fee + gas_fee + ...) = ~0.0076 TON recipient_pays = claim_fee + blockchain_fee = 0.15 + ~0.0076 = ~0,1576 TON ``` -------------------------------- ### Implement a TON Blockchain Client Proxy in Go Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.go.mdx This Go code defines a `proxy` struct that acts as an intermediary for interacting with a `liteapi.Client`. It provides methods like `GetSeqno` to retrieve account sequence numbers, `SendMessage` to deserialize and forward messages, and `GetAccountState` to fetch account details, abstracting direct `liteapi` calls for cleaner wallet integration. ```Go type proxy struct { msgCh chan tlb.Message cli *liteapi.Client } func (p *proxy) GetSeqno(ctx context.Context, account tongo.AccountID) (uint32, error) { return p.cli.GetSeqno(ctx, account) } func (p *proxy) SendMessage(ctx context.Context, payload []byte) (uint32, error) { cells, err := boc.DeserializeBoc(payload) if err != nil { panic(err) } var msg tlb.Message if err := tlb.Unmarshal(cells[0], &msg); err != nil { panic(err) } p.msgCh <- msg return 0, nil } func (p *proxy) GetAccountState(ctx context.Context, accountID tongo.AccountID) (tlb.ShardAccount, error) { return p.cli.GetAccountState(ctx, accountID) } ``` -------------------------------- ### Configure TonConnectUIProvider in main.tsx Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/dapp/building.mdx Updates the main application entry file to wrap the App component with `TonConnectUIProvider`, enabling TonConnect functionality. Requires a correct `manifestUrl`. ```tsx import { TonConnectUIProvider } from '@tonconnect/ui-react'; import App from './App.tsx'; createRoot(document.getElementById('root')!).render( ); ``` -------------------------------- ### Perform Gasless Jetton Transfer on TON (Python) Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.py.mdx This comprehensive Python example demonstrates the end-to-end process of initiating and sending a gasless Jetton transfer on the TON blockchain. It includes importing necessary libraries, configuring API keys and wallet mnemonics, defining Jetton and destination addresses, building the transfer message, estimating gas prices using TonAPI, signing the transaction, and finally sending it. The `print_config_and_return_relay_address` helper function is also included to show how to fetch gasless configuration details. ```Python from pytoniq_core import Address, Cell from tonutils.client import TonapiClient from tonutils.jetton import JettonMaster, JettonWallet from tonutils.utils import to_nano from tonutils.wallet import WalletV5R1 from pytonapi import AsyncTonapi # API key to access Tonapi (obtained from https://tonconsole.com). API_KEY = "AE332...2BD5I" # Mnemonic phrase used to connect the wallet. MNEMONIC = "a b c ..." # Jetton master address. # For this example, USDt. JETTON_MASTER_ADDRESS = Address("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs") # Destination address. # Replace with a correct recipient address. DESTINATION_ADDRESS = Address("UQC-3ilVr-W0Uc3pLrGJElwSaFxvhXXfkiQA3EwdVBHNNbbp") # The number of decimal places. # For USDt, it is 6. JETTON_DECIMALS = 6 # Jetton amount for send. # For this example, 1 USDt. JETTON_AMOUNT = 1 # Amount for jetton transfer. # Usually 0.05 TON is enough for most jetton transfers without forward_payload. BASE_JETTON_SEND_AMOUNT = 0.05 async def main() -> None: tonapi, client = AsyncTonapi(API_KEY), TonapiClient(API_KEY) # We use USDt in this example, # so we just print all supported gas jettons and get the relay address. # We have to send excess to the relay address in order to make a transfer cheaper. relayer_address = await print_config_and_return_relay_address(tonapi) wallet, public_key, private_key, _ = WalletV5R1.from_mnemonic(client, MNEMONIC) print(f"Wallet address: {wallet.address.to_str()}") jetton_wallet_address = await JettonMaster.get_wallet_address( client=client, owner_address=wallet.address, jetton_master_address=JETTON_MASTER_ADDRESS, ) tether_transfer_body = JettonWallet.build_transfer_body( jetton_amount=to_nano(JETTON_AMOUNT, JETTON_DECIMALS), recipient_address=DESTINATION_ADDRESS, response_address=relayer_address, # Excess, because some TONs will be sent back to the relay address, commission will be lowered. ) message_to_estimate = wallet.create_internal_msg( dest=jetton_wallet_address, value=to_nano(BASE_JETTON_SEND_AMOUNT), body=tether_transfer_body, ) # We send a single message containing a transfer from our wallet to a desired destination. # As a result of estimation, TonAPI returns a list of messages that we need to sign. # The first message is a fee transfer to the relay address, the second message is our original transfer. sign_raw_params = await tonapi.gasless.estimate_gas_price( master_id=JETTON_MASTER_ADDRESS.to_str(), body={ "wallet_address": wallet.address.to_str(), "wallet_public_key": public_key.hex(), "messages": [ { "boc": message_to_estimate.serialize().to_boc().hex(), } ] } ) # Signs Raw Params is the same structure as signRawParams in to connect. # OK, at this point, we have everything we need to send a gasless transfer. # The message has to be signed internal. seqno = await WalletV5R1.get_seqno(client, wallet.address) tether_transfer_for_send = wallet.create_signed_internal_msg( messages=[ wallet.create_wallet_internal_message( destination=Address(message.address), value=int(message.amount), body=Cell.one_from_boc(message.payload), ) for message in sign_raw_params.messages ], seqno=seqno, valid_until=sign_raw_params.valid_until, ) ext_message = wallet.create_external_msg( dest=wallet.address, body=tether_transfer_for_send, ) # Send a gasless transfer. await tonapi.gasless.send( body={ "wallet_public_key": public_key.hex(), "boc": ext_message.serialize().to_boc().hex(), } ) print(f"A gasless transfer sent!") async def print_config_and_return_relay_address(tonapi: AsyncTonapi) -> Address: """Fetch the gasless configuration and return the relay address.""" gasless_config = await tonapi.gasless.get_config() print("Available gas jettons:") for jetton in gasless_config.gas_jettons: print(f"Gas jetton master: {Address(jetton.master_id).to_str()}") print(f"Relay address to send fees to: {Address(gasless_config.relay_address).to_str()}") return Address(gasless_config.relay_address) if __name__ == "__main__": import asyncio asyncio.run(main()) ``` -------------------------------- ### Example: Real-time Transaction Notification SSE Response Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/streaming-api.mdx An example of the Server-Sent Events (SSE) stream response for real-time transaction notifications, showing a heartbeat event and a message event with transaction details like account ID, logical time (lt), and transaction hash. ```text event: heartbeat event: message id: 1682407879253338019 data: {"account_id":"-1:5555555555555555555555555555555555555555555555555555555555555555","lt":37121532000003,"tx_hash":"076a457ace46c6bcea6ef0644d65a4b866d25a5fd52349f08a6ccfbf7cb99ddb"} ``` -------------------------------- ### Send Gasless TON Transfers with Go Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.go.mdx This Go code demonstrates how to construct and send a gasless transaction on the TON blockchain. It involves parsing recipient addresses and amounts, creating a wallet message body with a specific V5 message type, signing the message, and finally sending it via the `tonapi` service. Error handling is included for each step. ```Go dest := tongo.MustParseAccountID(msg.Address) amount := decimal.RequireFromString(msg.Amount) rawMessage := RawMessage{ Dest: dest, Amount: amount.BigInt().Int64(), Payload: cells[0], } msgs = append(msgs, rawMessage) } // OK, at this point, we have everything we need to send a gasless transfer. state, err := cli.GetAccountState(context.Background(), wallet.GetAddress()) if err != nil { panic(err) } nextMsgParams, err := w5.NextMessageParams(state) if err != nil { panic(err) } // the message has to be V5MsgTypeSignedInternal. conf := tongoWallet.MessageConfig{ Seqno: nextMsgParams.Seqno, ValidUntil: time.Now().UTC().Add(tongoWallet.DefaultMessageLifetime), V5MsgType: tongoWallet.V5MsgTypeSignedInternal, } body, err := wallet.CreateMessageBody(conf, msgs...) if err != nil { panic(err) } m, err = ton.CreateExternalMessage(wallet.GetAddress(), body, nextMsgParams.Init, tlb.VarUInteger16{}) if err != nil { panic(err) } cell = boc.NewCell() if err := tlb.Marshal(cell, m); err != nil { panic(err) } msgBoc, err = cell.ToBocBase64() if err != nil { panic(err) } sendReq := tonapi.GaslessSendReq{ WalletPublicKey: hex.EncodeToString(publicKey), Boc: msgBoc, } if err := tonapiCli.GaslessSend(context.Background(), &sendReq); err != nil { panic(err) } fmt.Printf("A gasless transfer sent\n") ``` -------------------------------- ### Perform Gasless Jetton Transfer on TON Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.ts.mdx This snippet illustrates the end-to-end process of sending a gasless jetton transfer. It initializes the TonApiClient, defines constants for operations and amounts, derives wallet keys from a mnemonic seed, and constructs the necessary messages for a jetton transfer. The TonAPI's gasless estimation service is used to determine the required messages for the relay, which are then signed locally and sent via the API. Note that the `printConfigAndReturnRelayAddress` function is assumed to be defined elsewhere. ```typescript import { WalletContractV5R1 } from '@ton/ton'; import { Address, beginCell, internal, toNano, SendMode, external, storeMessage, storeMessageRelaxed } from '@ton/core'; import { mnemonicToPrivateKey } from '@ton/crypto'; import { TonApiClient } from '@ton-api/client'; import { ContractAdapter } from '@ton-api/ton-adapter'; // if you need to send lots of requests in parallel, // make sure you use a tonapi token. const ta = new TonApiClient({ baseUrl: 'https://tonapi.io', // apiKey: 'YOUR_API_KEY', }); const provider = new ContractAdapter(ta); const OP_CODES = { TK_RELAYER_FEE: 0x878da6e3, JETTON_TRANSFER: 0xf8a7ea5 }; // Amount for jetton transfer. Usually 0.05 TON is enough for most jetton transfers without forwardBody const BASE_JETTON_SEND_AMOUNT = toNano(0.05); const main = async () => { // this is a simple example of how to send a gasless transfer. // you only need to specify your seed and a destination address. // the seed is not sent to the network, it is used to sign messages locally. const seed = '..!!! REPLACE THIS WITH YOUR SEED !!! ..'; // wallet seed `word1 word2 word3 ... word24` const destination = Address.parse('..!!! REPLACE THIS WITH A CORRECT DESTINATION !!!..'); // replace with a correct recipient address const usdtMaster = Address.parse('EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'); // USDt jetton master. const jettonAmount = 1_000_000n; // amount in the smallest jetton units. This is 1 USDt. const keyPair = await mnemonicToPrivateKey(seed.split(' ')); const workchain = 0; const wallet = WalletContractV5R1.create({ workchain, publicKey: keyPair.publicKey }); const contract = provider.open(wallet); console.log('Wallet address:', wallet.address.toString()); const jettonWalletAddressResult = await ta.blockchain.execGetMethodForBlockchainAccount( usdtMaster, 'get_wallet_address', { args: [wallet.address.toRawString()] } ); const jettonWallet = Address.parse(jettonWalletAddressResult.decoded.jettonWalletAddress); // we use USDt in this example, // so we just print all supported gas jettons and get the relay address. // we have to send excess to the relay address in order to make a transfer cheaper. const relayerAddress = await printConfigAndReturnRelayAddress(); // Create payload for jetton transfer const tetherTransferPayload = beginCell() .storeUint(OP_CODES.JETTON_TRANSFER, 32) .storeUint(0, 64) .storeCoins(jettonAmount) // 1 USDT .storeAddress(destination) // address for receiver .storeAddress(relayerAddress) // address for excesses .storeBit(false) // null custom_payload .storeCoins(1n) // count of forward transfers in nanoton .storeMaybeRef(undefined) .endCell(); const messageToEstimate = beginCell() .storeWritable( storeMessageRelaxed( internal({ to: jettonWallet, bounce: true, value: BASE_JETTON_SEND_AMOUNT, body: tetherTransferPayload }) ) ) .endCell(); // we send a single message containing a transfer from our wallet to a desired destination. // as a result of estimation, TonAPI returns a list of messages that we need to sign. // the first message is a fee transfer to the relay address, the second message is our original transfer. const params = await ta.gasless.gaslessEstimate(usdtMaster, { walletAddress: wallet.address, walletPublicKey: keyPair.publicKey.toString('hex'), messages: [{ boc: messageToEstimate }] }); //.catch(error => console.error(error)); console.log('Estimated transfer:', params); const seqno = await contract.getSeqno(); // params is the same structure as params in tonconnect const tetherTransferForSend = wallet.createTransfer({ seqno, authType: 'internal', timeout: Math.ceil(Date.now() / 1000) + 5 * 60, secretKey: keyPair.secretKey, sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS, messages: params.messages.map(message => internal({ to: message.address, value: BigInt(message.amount), body: message.payload }) ) }); const extMessage = beginCell() .storeWritable( storeMessage( external({ to: contract.address, init: seqno === 0 ? contract.init : undefined, body: tetherTransferForSend }) ) ) .endCell(); // Send a gasless transfer ta.gasless .gaslessSend({ walletPublicKey: keyPair.publicKey.toString('hex'), ``` -------------------------------- ### Next.js Static Props Configuration Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/rest-api/connect.mdx Configures the `getStaticProps` function for a Next.js page, delegating the data loading logic to a shared utility function `loadStatic`. This setup enables static site generation (SSG) for the page, pre-rendering content at build time. ```JavaScript export const getStaticProps = loadStatic; ``` -------------------------------- ### SQL Schema for Get Telemint Auction Configuration Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/analytics/scheme.mdx Defines the SQL table schema for retrieving configuration parameters of a Telemint auction, such as bid limits, duration, and beneficiary. ```SQL create table getmethods.get_telemint_auction_config ( account_id text NOT NULL, beneficiar_account_id text NOT NULL, initial_min_bid bigint NOT NULL, max_bid bigint NOT NULL, min_bid_step bigint NOT NULL, min_extend_time bigint NOT NULL, duration bigint NOT NULL, indexer_last_update_lt bigint NOT NULL ); ``` -------------------------------- ### SQL Schema for Get Basic Sale Data Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/analytics/scheme.mdx Defines the SQL table schema for basic sale data, including marketplace, NFT, owner, price, and fee details for a transaction. ```SQL create table getmethods.get_sale_data__basic ( account_id text NOT NULL, marketplace_account_id text NOT NULL, nft_account_id text NOT NULL, nft_owner_account_id text, royalty_account_id text, full_price numeric NOT NULL, marketplace_fee bigint NOT NULL, royalty_amount bigint NOT NULL, indexer_last_update_lt bigint NOT NULL ); ``` -------------------------------- ### Analyze Blockchain Operations by Type for Last Day (SQL) Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/analytics/examples.mdx This SQL query categorizes and counts blockchain operations based on their decoded operation, op_code, or defaults to 'SimpleTransfer' if neither is present. It aggregates data for the last 24 hours to provide a summary of transaction types and their frequencies. ```sql SELECT CASE WHEN m.decoded_operation != '' THEN m.decoded_operation WHEN m.op_code IS NOT NULL THEN 'unknown' ELSE 'SimpleTransfer' END op, count(1) FROM blockchain.messages m JOIN blockchain.transactions txs ON txs.in_msg = m.id WHERE utime > extract(EPOCH FROM now() - interval '1 day')::bigint GROUP BY op ``` -------------------------------- ### API Authorization using Bearer Token Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/streaming-api.mdx Example of an API key provided in the Authorization header with the Bearer scheme. This method is recommended for secure production usage. ```text Bearer eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9 ``` -------------------------------- ### Next.js Static Props and Component Imports for API Documentation Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/rest-api/inscriptions.mdx This TypeScript snippet illustrates the setup for a Next.js page, importing necessary components like `Callout`, `SwaggerLink`, and `SchemaLoader` for rendering API documentation, along with utilities for static site generation. It exports `getStaticProps` to pre-render the page. ```TypeScript import { Callout } from "nextra-theme-docs"; import { SwaggerLink, SchemaLoader } from "@/components"; import { SWAGGER_TAGS } from "@/constants"; import { loadStatic } from "@/utils"; export const getStaticProps = loadStatic; ``` -------------------------------- ### Fetching Gasless Configuration and Relay Address Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.js.mdx This asynchronous function retrieves the gasless configuration from the TON API's "/gasless/config" endpoint. It then logs the available jettons for gasless transfers and the relay address, finally returning the parsed relay address for further use. ```JavaScript async function printConfigAndReturnRelayAddress() { const cfg = await fetch(`${tonApiBaseUrl}/gasless/config`).then(res => res.json()); console.log('Available jettons for gasless transfer'); console.log(cfg.gas_jettons.map(gasJetton => gasJetton.master_id)); console.log(`Relay address to send fees to: ${cfg.relay_address}`); return Address.parse(cfg.relay_address); } ``` -------------------------------- ### Select Wallets Owning Specific Jettons and NFTs (SQL) Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonconsole/analytics/examples.mdx This SQL query identifies unique wallet accounts that hold both 'AMBRA' jettons and 'Whales Club' NFTs. It joins blockchain account data with NFT and jetton ownership records, filtering by specific collection and jetton account IDs to pinpoint relevant owners. ```sql SELECT human_readable FROM blockchain.accounts a JOIN ( SELECT DISTINCT nft.owner_account_id FROM getmethods.get_nft_data nft JOIN getmethods.get_wallet_data jetton ON jetton.owner_account_id = nft.owner_account_id WHERE nft.collection_account_id = '0:ef4453182ddc66bd8dd393e71f58c2ea0e75c5fc47253b6169e30c23df75a194' AND jetton.jetton_account_id = '0:9c2c05b9dfb2a7460fda48fae7409a32623399933a98a7a15599152f37572b49' ) t ON a.id = t.owner_account_id ``` -------------------------------- ### Retrieve Tonkeeper Gasless Configuration and Relay Address Source: https://github.com/tonkeeper/tonconsole-docs/blob/master/pages/tonapi/cookbook/gasless.ts.mdx This asynchronous function fetches the gasless configuration from the Tonkeeper API using 'ta.gasless.gaslessConfig()'. It then logs the available gas jettons and the relay address for sending fees, finally returning the relay address. ```TypeScript async function printConfigAndReturnRelayAddress(): Promise
{ const cfg = await ta.gasless.gaslessConfig(); console.log('Available jettons for gasless transfer'); console.log(cfg.gasJettons.map(gasJetton => gasJetton.masterId)); console.log(`Relay address to send fees to: ${cfg.relayAddress}`); return cfg.relayAddress; } ```