diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 000000000..2c98c1dd2 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,116 @@ +name: Deploy Docs + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + branches: + - main + paths: + - ".github/workflows/deploy-docs.yml" + - "next/**" + push: + branches: + - main + paths: + - ".github/workflows/deploy-docs.yml" + - "next/**" + workflow_dispatch: + +concurrency: + group: deploy-docs-${{ github.ref }} + cancel-in-progress: false + +permissions: { } + +jobs: + build: + name: Build docs + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Restore npm cache + id: npm-cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: &build-docs-npm-cache-paths | + ~/.npm + ${{ github.workspace }}/next/.next/cache + key: ${{ runner.os }}-${{ runner.arch }}-npm-docs-${{ hashFiles('next/package-lock.json') }}-${{ hashFiles('next/**.[jt]s', 'next/**.[jt]sx') }} + restore-keys: | + ${{ runner.os }}-${{ runner.arch }}-npm-docs-${{ hashFiles('next/package-lock.json') }}- + + - name: Install docs dependencies + working-directory: next + run: npm ci + + - name: Generated source + working-directory: next + run: npm run generated-source + +# - name: Check docs formatting +# working-directory: next +# run: npm run fmt:check +# +# - name: Lint Check docs +# working-directory: next +# run: npm run lint + + - name: Build docs + working-directory: next + env: + NEXT_TELEMETRY_DISABLED: '1' + run: npm run build + + - name: Generate redirect files + working-directory: next + run: npm run generate-redirects + + - name: Save npm cache + if: github.ref == 'refs/heads/main' && steps.npm-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: *build-docs-npm-cache-paths + key: ${{ steps.npm-cache.outputs.cache-primary-key }} + + - name: Upload GitHub Pages artifact + if: github.ref == 'refs/heads/main' && github.repository == 'the-ton-tech/ton-docs-private' + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + with: + path: next/out + + deploy: + name: Deploy docs + if: github.ref == 'refs/heads/main' && github.repository == 'the-ton-tech/ton-docs-private' + needs: [ build ] + runs-on: ubuntu-latest + + permissions: + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 diff --git a/.github/workflows/next-build.yml b/.github/workflows/next-build.yml new file mode 100644 index 000000000..30c0099bb --- /dev/null +++ b/.github/workflows/next-build.yml @@ -0,0 +1,80 @@ +name: Next.js build & lint + +on: + pull_request: + branches: ["**"] + paths: + - "next/**" + - ".github/workflows/next-build.yml" + push: + branches: ["main", "master"] + paths: + - "next/**" + - ".github/workflows/next-build.yml" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + name: Build Next.js docs + runs-on: ubuntu-latest + env: + NEXT_TELEMETRY_DISABLED: "1" + HUSKY: "0" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: "npm" + cache-dependency-path: next/package-lock.json + + - name: Install dependencies + working-directory: next + run: npm ci + + - name: Generate Fumadocs source + working-directory: next + run: npm run generated-source + + - name: Prettier check + working-directory: next + run: npm run fmt:check + continue-on-error: true + + - name: ESLint + working-directory: next + run: npm run lint + continue-on-error: true + + - name: Validate navigation + working-directory: next + run: npm run lint:navigation + + - name: Validate internal links + working-directory: next + run: npm run lint:links:internal + + - name: Validate links + working-directory: next + run: npm run lint:links:validate + + - name: Validate SEO continuity (redirects, chains, sitemap, canonical) + working-directory: next + run: npm run lint:seo + + - name: Next build + working-directory: next + run: npm run build diff --git a/.gitignore b/.gitignore index b04e0f746..f1c745ce6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ .emacs.d/ .zed/ +# Claude Code session state (worktrees, caches) +.claude/ + # Node.js node_modules/ @@ -24,3 +27,7 @@ __pycache__ # Generated folders /stats/ + +dist +export.zip +next/pnpm-lock.yaml diff --git a/.mintignore b/.mintignore new file mode 100644 index 000000000..af6bc0cf5 --- /dev/null +++ b/.mintignore @@ -0,0 +1 @@ +next/ diff --git a/contract-dev/introduction.mdx b/contract-dev/introduction.mdx new file mode 100644 index 000000000..cf8204941 --- /dev/null +++ b/contract-dev/introduction.mdx @@ -0,0 +1,14 @@ +--- +title: "Introduction to smart contract development" +sidebarTitle: "Introduction" +--- + +To start developing smart contracts on TON, use our Rust toolkit, [Acton](https://ton-blockchain.github.io/acton/docs/welcome). + +The recommended smart contract language is [Tolk](https://docs.ton.org/tolk/overview). + +In the sections below, you can also learn about the following: + +- IDEs and editor plugins: [VS Code](/contract-dev/ide/vscode) and [JetBrains IDEs](/contract-dev/ide/jetbrains) +- Techniques: [signing messages](/contract-dev/techniques/signing), [contract sharding](/contract-dev/techniques/contract-sharding), and more +- [Blueprint](/contract-dev/blueprint/overview) (for legacy developers) diff --git a/docs.json b/docs.json index 613dd31ce..ec6e5d157 100644 --- a/docs.json +++ b/docs.json @@ -66,22 +66,17 @@ } }, "navigation": { - "pages": [ - "start-here", - "get-support", - "from-ethereum", - "more-tutorials", - "old", + "tabs": [ { - "group": "Ecosystem", + "tab": "TON overview", "pages": [ { - "group": "AI", - "expanded": true, + "group": "Introduction", "pages": [ - "ecosystem/ai/overview", - "ecosystem/ai/mcp", - "ecosystem/ai/wallets" + "start-here", + "toolset", + "ecosystem/subsecond", + "get-support" ] }, { @@ -95,13 +90,198 @@ ] }, { - "group": "Explorers", + "group": "Infrastructure", + "pages": [ + { + "group": "Explorers", + "pages": [ + "ecosystem/explorers/overview", + "ecosystem/explorers/tonviewer" + ] + }, + "ecosystem/analytics", + { + "group": "Oracles", + "pages": [ + "ecosystem/oracles/overview", + "ecosystem/oracles/redstone", + "ecosystem/oracles/pyth" + ] + }, + "ecosystem/bridges", + "ecosystem/status" + ] + }, + { + "group": "AI in TON", + "pages": [ + "ecosystem/ai/overview", + "ecosystem/ai/mcp", + "ecosystem/ai/wallets" + ] + }, + { + "group": "Learn more", + "pages": [ + "foundations/glossary", + "from-ethereum", + "more-tutorials", + "old" + ] + }, + { + "group": "Contribute", + "pages": [ + "contribute/style-guide", + { + "group": "Components and snippets", + "pages": [ + "contribute/snippets/overview", + "contribute/snippets/aside", + "contribute/snippets/image", + "contribute/snippets/filetree" + ] + } + ] + } + ] + }, + { + "tab": "AppKit", + "icon": "box", + "pages": [ + "ecosystem/appkit/overview", + "ecosystem/appkit/changelog", + { + "group": "Get Started", + "icon": "rocket", + "expanded": true, + "pages": [ + "ecosystem/appkit/get-started", + { + "group": "Installation", + "pages": [ + "ecosystem/appkit/get-started/installation", + "ecosystem/appkit/get-started/installation/react-app", + "ecosystem/appkit/get-started/installation/vanilla-js" + ] + }, + "ecosystem/appkit/get-started/connectors", + "ecosystem/appkit/get-started/networks", + "ecosystem/appkit/get-started/basic-getter-hooks", + "ecosystem/appkit/get-started/sending-transactions", + "ecosystem/appkit/get-started/streaming", + "ecosystem/appkit/get-started/providers", + "ecosystem/appkit/get-started/using-ui-widgets" + ] + }, + { + "group": "How to", + "pages": [ + "ecosystem/appkit/howto", + "ecosystem/appkit/howto/appkit", + "ecosystem/appkit/howto/connect-to-a-wallet", + "ecosystem/appkit/howto/providers", + "ecosystem/appkit/howto/networks", + "ecosystem/appkit/howto/read-balances", + "ecosystem/appkit/howto/send-toncoin", + "ecosystem/appkit/howto/send-jettons", + "ecosystem/appkit/howto/nfts", + "ecosystem/appkit/howto/sign-data", + "ecosystem/appkit/howto/streaming", + "ecosystem/appkit/howto/swaps", + "ecosystem/appkit/howto/staking", + "ecosystem/appkit/howto/onramp", + "ecosystem/appkit/howto/gasless", + "ecosystem/appkit/howto/use-ui-widgets" + ] + }, + { + "group": "Reference", + "icon": "book", "pages": [ - "ecosystem/explorers/overview", - "ecosystem/explorers/tonviewer" + "ecosystem/appkit/reference", + "ecosystem/appkit/reference/appkit", + "ecosystem/appkit/reference/appkit-react" + ] + }, + "ecosystem/appkit/faq-troubleshooting" + ] + }, + { + "tab": "Tools", + "pages": [ + { + "group": "WalletKit", + "pages": [ + "ecosystem/walletkit/overview", + { + "group": "Web", + "pages": [ + "ecosystem/walletkit/web/init", + "ecosystem/walletkit/web/wallets", + "ecosystem/walletkit/web/connections", + "ecosystem/walletkit/web/events", + "ecosystem/walletkit/web/toncoin", + "ecosystem/walletkit/web/jettons", + "ecosystem/walletkit/web/nfts" + ] + }, + { + "group": "iOS", + "pages": [ + "ecosystem/walletkit/ios/installation", + "ecosystem/walletkit/ios/init", + "ecosystem/walletkit/ios/wallets", + "ecosystem/walletkit/ios/data", + "ecosystem/walletkit/ios/transactions", + "ecosystem/walletkit/ios/events", + "ecosystem/walletkit/ios/webview" + ] + }, + { + "group": "Android", + "pages": [ + "ecosystem/walletkit/android/installation", + "ecosystem/walletkit/android/init", + "ecosystem/walletkit/android/wallets", + "ecosystem/walletkit/android/data", + "ecosystem/walletkit/android/events", + "ecosystem/walletkit/android/transactions", + "ecosystem/walletkit/android/webview" + ] + }, + "ecosystem/walletkit/qa-guide", + "ecosystem/walletkit/native-web", + "ecosystem/walletkit/browser-extension" + ] + }, + { + "group": "TON Pay", + "pages": [ + "ecosystem/ton-pay/overview", + "ecosystem/ton-pay/quick-start", + "ecosystem/ton-pay/on-ramp", + { + "group": "Payment integration", + "pages": [ + "ecosystem/ton-pay/payment-integration/transfer", + "ecosystem/ton-pay/payment-integration/payments-react", + "ecosystem/ton-pay/payment-integration/payments-tonconnect", + "ecosystem/ton-pay/payment-integration/status-info" + ] + }, + { + "group": "UI integration", + "pages": [ + "ecosystem/ton-pay/ui-integration/button-react", + "ecosystem/ton-pay/ui-integration/button-js" + ] + }, + "ecosystem/ton-pay/webhooks", + "ecosystem/ton-pay/api-reference" ] }, - "ecosystem/sdks", { "group": "APIs", "pages": [ @@ -161,154 +341,374 @@ "ecosystem/api/price" ] }, - "ecosystem/subsecond", - "ecosystem/status", - "ecosystem/analytics", + "ecosystem/sdks" + ] + }, + { + "tab": "Blockchain basics", + "pages": [ + "foundations/core-concepts", { - "group": "Oracles", + "group": "Payment processing", "pages": [ - "ecosystem/oracles/overview", - "ecosystem/oracles/redstone", - "ecosystem/oracles/pyth" + "payments/overview", + "payments/toncoin", + "payments/jettons" ] }, - "ecosystem/bridges", { "group": "TON Connect", "pages": [ "ecosystem/ton-connect/overview", - "ecosystem/ton-connect/wallet-connect", - "ecosystem/ton-connect/dapp", - "ecosystem/ton-connect/wallet", - "ecosystem/ton-connect/manifest", - "ecosystem/ton-connect/message-lookup" + "ecosystem/ton-connect/get-started", + { + "group": "How to", + "pages": [ + "ecosystem/ton-connect/how-to/connect", + "ecosystem/ton-connect/how-to/disconnect", + "ecosystem/ton-connect/how-to/send-transaction", + "ecosystem/ton-connect/how-to/sign-data", + "ecosystem/ton-connect/how-to/sign-message-gasless", + "ecosystem/ton-connect/how-to/embedded-request", + "ecosystem/ton-connect/how-to/filter-wallets", + "ecosystem/ton-connect/how-to/walletconnect-support" + ] + }, + "ecosystem/ton-connect/core-concepts", + { + "group": "API reference", + "pages": [ + "ecosystem/ton-connect/api-reference/ui-react", + "ecosystem/ton-connect/api-reference/ui", + "ecosystem/ton-connect/api-reference/sdk", + "ecosystem/ton-connect/api-reference/protocol" + ] + }, + "ecosystem/ton-connect/troubleshooting", + "ecosystem/ton-connect/faq" ] }, { - "group": "AppKit", + "group": "Standard contracts", "pages": [ - "ecosystem/appkit/overview", - "ecosystem/appkit/init", - "ecosystem/appkit/toncoin", - "ecosystem/appkit/jettons", - "ecosystem/appkit/nfts", - "ecosystem/appkit/swap", - "ecosystem/appkit/stake" - ] - }, - { - "group": "WalletKit", - "pages": [ - "ecosystem/walletkit/overview", + "standard/overview", { - "group": "Web", + "group": "Wallets", "pages": [ - "ecosystem/walletkit/web/init", - "ecosystem/walletkit/web/wallets", - "ecosystem/walletkit/web/connections", - "ecosystem/walletkit/web/events", - "ecosystem/walletkit/web/toncoin", - "ecosystem/walletkit/web/jettons", - "ecosystem/walletkit/web/nfts" + "standard/wallets/how-it-works", + "standard/wallets/mnemonics", + "standard/wallets/comparison", + "standard/wallets/interact", + { + "group": "Wallet V5", + "tag": "latest", + "pages": [ + "standard/wallets/v5", + "standard/wallets/v5-api" + ] + }, + "standard/wallets/v4", + { + "group": "Highload wallets", + "pages": [ + "standard/wallets/highload/overview", + { + "group": "Highload wallet v3", + "pages": [ + "standard/wallets/highload/v3/create", + "standard/wallets/highload/v3/send-single-transfer", + "standard/wallets/highload/v3/send-batch-transfers", + "standard/wallets/highload/v3/verify-is-processed", + "standard/wallets/highload/v3/specification" + ] + }, + { + "group": "Highload wallet v2", + "pages": [ + "standard/wallets/highload/v2/specification" + ] + } + ] + }, + "standard/wallets/lockup", + { + "group": "Preprocessed wallet", + "tag": "v2", + "pages": [ + "standard/wallets/preprocessed-v2/interact", + "standard/wallets/preprocessed-v2/specification" + ] + }, + "standard/wallets/restricted", + "standard/wallets/performance", + "standard/wallets/history" ] }, { - "group": "iOS", + "group": "Tokens", "pages": [ - "ecosystem/walletkit/ios/installation", - "ecosystem/walletkit/ios/init", - "ecosystem/walletkit/ios/wallets", - "ecosystem/walletkit/ios/data", - "ecosystem/walletkit/ios/transactions", - "ecosystem/walletkit/ios/events", - "ecosystem/walletkit/ios/webview" + "standard/tokens/overview", + "standard/tokens/metadata", + { + "group": "Jettons", + "pages": [ + "standard/tokens/jettons/overview", + "standard/tokens/jettons/how-it-works", + "standard/tokens/jettons/comparison", + "standard/tokens/jettons/mint", + "standard/tokens/jettons/transfer", + "standard/tokens/jettons/burn", + "standard/tokens/jettons/find", + "standard/tokens/jettons/wallet-data", + "standard/tokens/jettons/supply-data", + "standard/tokens/jettons/mintless/overview", + "standard/tokens/jettons/mintless/deploy", + "standard/tokens/jettons/api" + ] + }, + { + "group": "NFTs", + "pages": [ + "standard/tokens/nft/overview", + "standard/tokens/nft/how-it-works", + "standard/tokens/nft/sbt", + "standard/tokens/nft/comparison", + "standard/tokens/nft/deploy", + "standard/tokens/nft/transfer", + "standard/tokens/nft/metadata", + "standard/tokens/nft/verify", + "standard/tokens/nft/api", + "standard/tokens/nft/nft-reference" + ] + }, + "standard/tokens/airdrop" ] }, + "standard/vesting" + ] + }, + { + "group": "Smart contract development", + "pages": [ + "contract-dev/introduction", + "contract-dev/acton", { - "group": "Android", + "group": "IDEs and editor plugins", "pages": [ - "ecosystem/walletkit/android/installation", - "ecosystem/walletkit/android/init", - "ecosystem/walletkit/android/wallets", - "ecosystem/walletkit/android/data", - "ecosystem/walletkit/android/events", - "ecosystem/walletkit/android/transactions", - "ecosystem/walletkit/android/webview" + "contract-dev/ide/vscode", + "contract-dev/ide/jetbrains" + ] + }, + { + "group": "Techniques", + "pages": [ + "contract-dev/techniques/signing", + "contract-dev/techniques/contract-sharding", + "contract-dev/techniques/security", + "contract-dev/techniques/gas", + "contract-dev/techniques/on-chain-jetton-processing", + "contract-dev/techniques/using-on-chain-libraries", + "contract-dev/techniques/random", + "contract-dev/techniques/upgrades", + "contract-dev/techniques/vanity", + "contract-dev/techniques/zero-knowledge" ] }, { - "group": "Manual implementation", + "group": "Blueprint", + "tag": "legacy", "pages": [ - "ecosystem/walletkit/qa-guide", - "ecosystem/walletkit/native-web", - "ecosystem/walletkit/browser-extension" + "contract-dev/blueprint/overview", + "contract-dev/blueprint/first-smart-contract", + "contract-dev/blueprint/develop", + { + "group": "Testing", + "pages": [ + "contract-dev/testing/overview", + "contract-dev/testing/reference" + ] + }, + "contract-dev/blueprint/debug", + "contract-dev/blueprint/deploy", + "contract-dev/blueprint/coverage", + "contract-dev/blueprint/benchmarks", + "contract-dev/blueprint/config", + "contract-dev/blueprint/cli", + "contract-dev/blueprint/api" ] } ] }, { - "group": "TON Pay", + "group": "Tolk language", + "tag": "recommended", "pages": [ - "ecosystem/ton-pay/overview", - "ecosystem/ton-pay/quick-start", - "ecosystem/ton-pay/on-ramp", + "tolk/overview", + "tolk/basic-syntax", + "tolk/idioms-conventions", + "tolk/examples", { - "group": "Payment integration", + "group": "Type system", "pages": [ - "ecosystem/ton-pay/payment-integration/transfer", - "ecosystem/ton-pay/payment-integration/payments-react", - "ecosystem/ton-pay/payment-integration/payments-tonconnect", - "ecosystem/ton-pay/payment-integration/status-info" + "tolk/types/list-of-types", + "tolk/types/numbers", + "tolk/types/booleans", + "tolk/types/address", + "tolk/types/cells", + "tolk/types/strings", + "tolk/types/structures", + "tolk/types/aliases", + "tolk/types/generics", + "tolk/types/enums", + "tolk/types/nullable", + "tolk/types/unions", + "tolk/types/tensors", + "tolk/types/tuples", + "tolk/types/maps", + "tolk/types/callables", + "tolk/types/unknown", + "tolk/types/void-never", + "tolk/types/type-checks-and-casts", + "tolk/types/overall-tvm-stack", + "tolk/types/overall-serialization" ] }, { - "group": "UI integration", + "group": "Syntax details", "pages": [ - "ecosystem/ton-pay/ui-integration/button-react", - "ecosystem/ton-pay/ui-integration/button-js" + "tolk/syntax/variables", + "tolk/syntax/conditions-loops", + "tolk/syntax/exceptions", + "tolk/syntax/functions-methods", + "tolk/syntax/structures-fields", + "tolk/syntax/pattern-matching", + "tolk/syntax/mutability", + "tolk/syntax/operators", + "tolk/syntax/imports" ] }, - "ecosystem/ton-pay/webhooks", - "ecosystem/ton-pay/api-reference" + { + "group": "Language features", + "pages": [ + "tolk/features/message-handling", + "tolk/features/contract-storage", + "tolk/features/contract-getters", + "tolk/features/contract-abi", + "tolk/features/message-sending", + "tolk/features/auto-serialization", + "tolk/features/lazy-loading", + "tolk/features/jetton-payload", + "tolk/features/standard-library", + "tolk/features/asm-functions", + "tolk/features/compiler-optimizations" + ] + }, + { + "group": "Migrating from FunC", + "pages": [ + "tolk/from-func/tolk-vs-func", + "tolk/from-func/stdlib-comparison", + "tolk/from-func/converter" + ] + }, + "tolk/changelog" ] }, { - "group": "TMA: Telegram Mini Apps", + "group": "Other languages", "pages": [ - "ecosystem/tma/overview", - "ecosystem/tma/create-mini-app", { - "group": "Telegram UI", + "group": "TL-B", + "pages": [ + "languages/tl-b/overview", + "languages/tl-b/syntax-and-semantics", + "languages/tl-b/simple-examples", + "languages/tl-b/complex-and-non-trivial-examples", + "languages/tl-b/tep-examples", + "languages/tl-b/tooling" + ] + }, + { + "group": "Fift", "pages": [ - "ecosystem/tma/telegram-ui/overview", - "ecosystem/tma/telegram-ui/getting-started", - "ecosystem/tma/telegram-ui/platform-and-palette", + "languages/fift/overview", + "languages/fift/fift-and-tvm-assembly", + "languages/fift/deep-dive", + "languages/fift/multisig", + "languages/fift/whitepaper" + ] + }, + { + "group": "FunC", + "pages": [ + "languages/func/overview", + "languages/func/cookbook", { - "group": "Reference", + "group": "Language", + "expanded": true, "pages": [ - "ecosystem/tma/telegram-ui/reference/avatar" + "languages/func/comments", + "languages/func/types", + "languages/func/literals", + "languages/func/operators", + "languages/func/expressions", + "languages/func/statements", + { + "group": "Program declarations", + "expanded": true, + "pages": [ + "languages/func/declarations-overview", + "languages/func/functions", + "languages/func/special-functions", + "languages/func/asm-functions", + "languages/func/global-variables", + "languages/func/compiler-directives" + ] + }, + "languages/func/built-ins", + "languages/func/dictionaries" ] - } + }, + { + "group": "Libraries", + "expanded": true, + "pages": [ + "languages/func/stdlib", + "languages/func/libraries" + ] + }, + "languages/func/changelog", + "languages/func/known-issues" ] }, + "languages/tact" + ] + }, + { + "group": "TVM: TON Virtual Machine", + "pages": [ + "tvm/overview", { - "group": "Analytics", + "group": "Tools", "pages": [ - "ecosystem/tma/analytics/analytics", - "ecosystem/tma/analytics/supported-events", - "ecosystem/tma/analytics/preparation", - "ecosystem/tma/analytics/install-via-script", - "ecosystem/tma/analytics/install-via-npm", - "ecosystem/tma/analytics/api-endpoints", - "ecosystem/tma/analytics/managing-integration", - "ecosystem/tma/analytics/faq" + "tvm/tools/txtracer", + "tvm/tools/retracer", + "tvm/tools/tvm-explorer", + "tvm/tools/ton-decompiler" ] - } + }, + "tvm/instructions", + "tvm/builders-and-slices", + "tvm/continuations", + "tvm/registers", + "tvm/gas", + "tvm/initialization", + "tvm/exit-codes", + "tvm/get-method" ] }, { - "group": "Blockchain nodes", - "expanded": true, + "group": "Nodes", "pages": [ "ecosystem/nodes/overview", { @@ -351,426 +751,98 @@ "ecosystem/nodes/rust/probes", "ecosystem/nodes/rust/monitoring" ] + }, + { + "group": "Staking", + "pages": [ + "ecosystem/staking/overview", + "ecosystem/staking/stake-calculation", + "ecosystem/staking/liquid-staking", + "ecosystem/staking/single-nominator", + "ecosystem/staking/nominator-pools" + ] } ] }, { - "group": "Staking", - "pages": [ - "ecosystem/staking/overview", - "ecosystem/staking/stake-calculation", - "ecosystem/staking/liquid-staking", - "ecosystem/staking/single-nominator", - "ecosystem/staking/nominator-pools" - ] - } - ] - }, - { - "group": "Payment processing", - "pages": [ - "payments/overview", - "payments/toncoin", - "payments/jettons" - ] - }, - { - "group": "Standard contracts", - "pages": [ - { - "group": "Wallets", + "group": "Primitives", "pages": [ - "standard/wallets/how-it-works", - "standard/wallets/mnemonics", - "standard/wallets/comparison", - "standard/wallets/interact", { - "group": "Wallet V5", - "tag": "latest", + "group": "Serialization", "pages": [ - "standard/wallets/v5", - "standard/wallets/v5-api" + "foundations/serialization/cells", + "foundations/serialization/library", + "foundations/serialization/merkle", + "foundations/serialization/merkle-update", + "foundations/serialization/pruned", + "foundations/serialization/boc" ] }, - "standard/wallets/v4", { - "group": "Highload wallets", + "group": "Addresses", "pages": [ - "standard/wallets/highload/overview", - { - "group": "Highload wallet v3", - "pages": [ - "standard/wallets/highload/v3/create", - "standard/wallets/highload/v3/send-single-transfer", - "standard/wallets/highload/v3/send-batch-transfers", - "standard/wallets/highload/v3/verify-is-processed", - "standard/wallets/highload/v3/specification" - ] - }, - { - "group": "Highload wallet v2", - "pages": [ - "standard/wallets/highload/v2/specification" - ] - } + "foundations/addresses/overview", + "foundations/addresses/formats", + "foundations/addresses/serialize", + "foundations/addresses/derive" ] }, - "standard/wallets/lockup", { - "group": "Preprocessed wallet", - "tag": "v2", + "group": "Messages & Transactions", "pages": [ - "standard/wallets/preprocessed-v2/interact", - "standard/wallets/preprocessed-v2/specification" + "foundations/messages/overview", + "foundations/messages/internal", + "foundations/messages/external-in", + "foundations/messages/external-out", + "foundations/messages/deploy", + "foundations/messages/modes", + "foundations/messages/ordinary-tx" ] }, - "standard/wallets/restricted", - "standard/wallets/performance", - "standard/wallets/history" - ] - }, - { - "group": "Tokens", - "expanded": true, - "pages": [ - "standard/tokens/overview", - "standard/tokens/metadata", { - "group": "Jettons", + "group": "Actions", "pages": [ - "standard/tokens/jettons/overview", - "standard/tokens/jettons/how-it-works", - "standard/tokens/jettons/comparison", - "standard/tokens/jettons/mint", - "standard/tokens/jettons/transfer", - "standard/tokens/jettons/burn", - "standard/tokens/jettons/find", - "standard/tokens/jettons/wallet-data", - "standard/tokens/jettons/supply-data", - "standard/tokens/jettons/mintless/overview", - "standard/tokens/jettons/mintless/deploy", - "standard/tokens/jettons/api" + "foundations/actions/overview", + "foundations/actions/send", + "foundations/actions/reserve", + "foundations/actions/set-code", + "foundations/actions/change-library" ] }, { - "group": "NFT", + "group": "Consensus", "pages": [ - "standard/tokens/nft/overview", - "standard/tokens/nft/how-it-works", - "standard/tokens/nft/sbt", - "standard/tokens/nft/comparison", - "standard/tokens/nft/deploy", - "standard/tokens/nft/transfer", - "standard/tokens/nft/metadata", - "standard/tokens/nft/verify", - "standard/tokens/nft/api", - "standard/tokens/nft/nft-reference", - "standard/tokens/nft/nft-2.0" + "foundations/consensus/catchain-visualizer" ] }, - "standard/tokens/airdrop" - ] - }, - "standard/vesting" - ] - }, - { - "group": "Contract development", - "pages": [ - "contract-dev/acton", - { - "group": "IDEs and editor plugins", - "pages": [ - "contract-dev/ide/vscode", - "contract-dev/ide/jetbrains" - ] - }, - { - "group": "Techniques", - "expanded": true, - "pages": [ - "contract-dev/techniques/signing", - "contract-dev/techniques/contract-sharding", - "contract-dev/techniques/security", - "contract-dev/techniques/gas", - "contract-dev/techniques/on-chain-jetton-processing", - "contract-dev/techniques/using-on-chain-libraries", - "contract-dev/techniques/random", - "contract-dev/techniques/upgrades", - "contract-dev/techniques/vanity", - "contract-dev/techniques/zero-knowledge" - ] - }, - { - "group": "Blueprint", - "tag": "legacy", - "pages": [ - "contract-dev/blueprint/overview", - "contract-dev/blueprint/first-smart-contract", - "contract-dev/blueprint/develop", + "foundations/status", + "foundations/phases", + "foundations/fees", + "foundations/traces", + "foundations/shards", + "foundations/limits", + "foundations/config", { - "group": "Testing", + "group": "Web3 services", "pages": [ - "contract-dev/testing/overview", - "contract-dev/testing/reference" + "foundations/web3/overview", + "foundations/web3/ton-dns", + "foundations/web3/ton-storage", + "foundations/web3/ton-proxy", + "foundations/web3/ton-sites" ] }, - "contract-dev/blueprint/debug", - "contract-dev/blueprint/deploy", - "contract-dev/blueprint/coverage", - "contract-dev/blueprint/benchmarks", - "contract-dev/blueprint/config", - "contract-dev/blueprint/cli", - "contract-dev/blueprint/api" - ] - } - ] - }, - { - "group": "Tolk language", - "tag": "recommended", - "pages": [ - "tolk/overview", - "tolk/basic-syntax", - "tolk/idioms-conventions", - "tolk/examples", - { - "group": "Type system", - "pages": [ - "tolk/types/list-of-types", - "tolk/types/numbers", - "tolk/types/booleans", - "tolk/types/address", - "tolk/types/cells", - "tolk/types/strings", - "tolk/types/structures", - "tolk/types/aliases", - "tolk/types/generics", - "tolk/types/enums", - "tolk/types/nullable", - "tolk/types/unions", - "tolk/types/tensors", - "tolk/types/tuples", - "tolk/types/maps", - "tolk/types/callables", - "tolk/types/unknown", - "tolk/types/void-never", - "tolk/types/type-checks-and-casts", - "tolk/types/overall-tvm-stack", - "tolk/types/overall-serialization" - ] - }, - { - "group": "Syntax details", - "pages": [ - "tolk/syntax/variables", - "tolk/syntax/conditions-loops", - "tolk/syntax/exceptions", - "tolk/syntax/functions-methods", - "tolk/syntax/structures-fields", - "tolk/syntax/pattern-matching", - "tolk/syntax/mutability", - "tolk/syntax/operators", - "tolk/syntax/imports" - ] - }, - { - "group": "Language features", - "pages": [ - "tolk/features/message-handling", - "tolk/features/contract-storage", - "tolk/features/contract-getters", - "tolk/features/contract-abi", - "tolk/features/message-sending", - "tolk/features/auto-serialization", - "tolk/features/lazy-loading", - "tolk/features/jetton-payload", - "tolk/features/standard-library", - "tolk/features/asm-functions", - "tolk/features/compiler-optimizations" - ] - }, - { - "group": "Migrating from FunC", - "pages": [ - "tolk/from-func/tolk-vs-func", - "tolk/from-func/stdlib-comparison", - "tolk/from-func/converter" - ] - }, - "tolk/changelog" - ] - }, - { - "group": "Other languages", - "pages": [ - { - "group": "TL-B", - "pages": [ - "languages/tl-b/overview", - "languages/tl-b/syntax-and-semantics", - "languages/tl-b/simple-examples", - "languages/tl-b/complex-and-non-trivial-examples", - "languages/tl-b/tep-examples", - "languages/tl-b/tooling" - ] - }, - { - "group": "Fift", - "pages": [ - "languages/fift/overview", - "languages/fift/fift-and-tvm-assembly", - "languages/fift/deep-dive", - "languages/fift/multisig", - "languages/fift/whitepaper" - ] - }, - { - "group": "FunC", - "pages": [ - "languages/func/overview", - "languages/func/cookbook", { - "group": "Language", - "expanded": true, + "group": "Merkle proofs", "pages": [ - "languages/func/comments", - "languages/func/types", - "languages/func/literals", - "languages/func/operators", - "languages/func/expressions", - "languages/func/statements", - { - "group": "Program declarations", - "expanded": true, - "pages": [ - "languages/func/declarations-overview", - "languages/func/functions", - "languages/func/special-functions", - "languages/func/asm-functions", - "languages/func/global-variables", - "languages/func/compiler-directives" - ] - }, - "languages/func/built-ins", - "languages/func/dictionaries" + "foundations/proofs/overview", + "foundations/proofs/verifying-liteserver-proofs" ] }, - { - "group": "Libraries", - "expanded": true, - "pages": [ - "languages/func/stdlib", - "languages/func/libraries" - ] - }, - "languages/func/changelog", - "languages/func/known-issues" - ] - }, - "languages/tact" - ] - }, - { - "group": "TVM: TON Virtual Machine", - "pages": [ - "tvm/overview", - { - "group": "Tools", - "pages": [ - "tvm/tools/txtracer", - "tvm/tools/retracer", - "tvm/tools/tvm-explorer", - "tvm/tools/ton-decompiler" - ] - }, - "tvm/instructions", - "tvm/builders-and-slices", - "tvm/continuations", - "tvm/registers", - "tvm/gas", - "tvm/initialization", - "tvm/exit-codes", - "tvm/get-method" - ] - }, - { - "group": "Blockchain foundations", - "pages": [ - { - "group": "Serialization", - "pages": [ - "foundations/serialization/cells", - "foundations/serialization/library", - "foundations/serialization/merkle", - "foundations/serialization/merkle-update", - "foundations/serialization/pruned", - "foundations/serialization/boc" - ] - }, - { - "group": "Addresses", - "pages": [ - "foundations/addresses/overview", - "foundations/addresses/formats", - "foundations/addresses/serialize", - "foundations/addresses/derive" - ] - }, - { - "group": "Messages & Transactions", - "pages": [ - "foundations/messages/overview", - "foundations/messages/internal", - "foundations/messages/external-in", - "foundations/messages/external-out", - "foundations/messages/deploy", - "foundations/messages/modes", - "foundations/messages/ordinary-tx" + "foundations/system", + "foundations/precompiled" ] }, - { - "group": "Actions", - "pages": [ - "foundations/actions/overview", - "foundations/actions/send", - "foundations/actions/reserve", - "foundations/actions/set-code", - "foundations/actions/change-library" - ] - }, - { - "group": "Consensus", - "pages": [ - "foundations/consensus/catchain-visualizer" - ] - }, - "foundations/status", - "foundations/phases", - "foundations/fees", - "foundations/traces", - "foundations/shards", - "foundations/limits", - "foundations/config", - { - "group": "Web3 services", - "pages": [ - "foundations/web3/overview", - "foundations/web3/ton-dns", - "foundations/web3/ton-storage", - "foundations/web3/ton-proxy", - "foundations/web3/ton-sites" - ] - }, - { - "group": "Merkle proofs", - "pages": [ - "foundations/proofs/overview", - "foundations/proofs/verifying-liteserver-proofs" - ] - }, - "foundations/system", - "foundations/precompiled", { "group": "Whitepapers", "pages": [ @@ -780,24 +852,13 @@ "foundations/whitepapers/ton", "foundations/whitepapers/catchain" ] - }, - "foundations/glossary" + } ] }, { - "group": "Contribute", - "pages": [ - "contribute/style-guide", - { - "group": "Components and snippets", - "pages": [ - "contribute/snippets/overview", - "contribute/snippets/aside", - "contribute/snippets/image", - "contribute/snippets/filetree" - ] - } - ] + "tab": "Smart contracts", + "icon": "arrow-up-right", + "href": "https://ton-blockchain.github.io/acton/docs/welcome" } ] }, @@ -1291,11 +1352,6 @@ "destination": "/standard/tokens/nft/overview", "permanent": true }, - { - "source": "/v3/documentation/dapps/assets/nft-2.0", - "destination": "/standard/tokens/nft/nft-2.0", - "permanent": true - }, { "source": "/v3/documentation/dapps/assets/usdt", "destination": "https://old-docs.ton.org/v3/documentation/dapps/assets/usdt", @@ -1988,57 +2044,57 @@ }, { "source": "/guidelines/tma", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/v3/guidelines/dapps/tma/overview", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/guidelines/tma-guidelines", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/v3/guidelines/dapps/tma/guidelines/testing-apps", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/v3/guidelines/dapps/tma/guidelines/publishing", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/v3/guidelines/dapps/tma/guidelines/monetization", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/v3/guidelines/dapps/tma/guidelines/tips-and-tricks", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/guidelines/tma-tutorials-and-examples", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/v3/guidelines/dapps/tma/tutorials/step-by-step-guide", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/v3/guidelines/dapps/tma/tutorials/app-examples", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/v3/guidelines/dapps/tma/tutorials/design-guidelines", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { @@ -2568,27 +2624,27 @@ }, { "source": "/develop/dapps/telegram-apps/testing-apps", - "destination": "/v3/guidelines/dapps/tma/guidelines/testing-apps", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/develop/dapps/telegram-apps/publishing", - "destination": "/v3/guidelines/dapps/tma/guidelines/publishing", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/develop/dapps/telegram-apps/monetization", - "destination": "/v3/guidelines/dapps/tma/guidelines/monetization", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/develop/dapps/telegram-apps/tips-and-tricks", - "destination": "/v3/guidelines/dapps/tma/guidelines/tips-and-tricks", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/develop/dapps/telegram-apps/design-guidelines", - "destination": "/v3/guidelines/dapps/tma/tutorials/design-guidelines", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { @@ -3158,17 +3214,17 @@ }, { "source": "/develop/dapps/telegram-apps/README", - "destination": "/v3/guidelines/dapps/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/develop/dapps/telegram-apps/app-examples", - "destination": "/v3/guidelines/dapps/tma/tutorials/app-examples", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/develop/dapps/telegram-apps/step-by-step-guide", - "destination": "/v3/guidelines/dapps/tma/tutorials/step-by-step-guide", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { @@ -3558,17 +3614,17 @@ }, { "source": "/ecosystem/tma/mate/telegram-apps-mate", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/ecosystem/tma/mate/getting-started", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { "source": "/ecosystem/tma/mate/hosting", - "destination": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview", "permanent": true }, { @@ -3635,6 +3691,181 @@ "source": "/contract-dev/ide/overview", "destination": "/contract-dev/ide/vscode", "permanent": true + }, + { + "source": "/ecosystem/tma/overview", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/create-mini-app", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/telegram-ui/overview", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/telegram-ui/getting-started", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/telegram-ui/platform-and-palette", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/telegram-ui/reference/avatar", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/analytics/analytics", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/analytics/supported-events", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/analytics/preparation", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/analytics/install-via-script", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/analytics/install-via-npm", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/analytics/api-endpoints", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/analytics/managing-integration", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/tma/analytics/faq", + "destination": "/ecosystem/appkit/overview" + }, + { + "source": "/ecosystem/appkit/init", + "destination": "/ecosystem/appkit/get-started" + }, + { + "source": "/ecosystem/appkit/toncoin", + "destination": "/ecosystem/appkit/howto/send-toncoin" + }, + { + "source": "/ecosystem/appkit/jettons", + "destination": "/ecosystem/appkit/howto/send-jettons" + }, + { + "source": "/ecosystem/appkit/nfts", + "destination": "/ecosystem/appkit/howto/nfts" + }, + { + "source": "/ecosystem/appkit/swap", + "destination": "/ecosystem/appkit/howto/swaps" + }, + { + "source": "/ecosystem/appkit/stake", + "destination": "/ecosystem/appkit/howto/staking" + }, + { + "source": "/ecosystem/ton-connect/wallet-connect", + "destination": "/ecosystem/ton-connect/how-to/walletconnect-support" + }, + { + "source": "/ecosystem/ton-connect/dapp", + "destination": "/ecosystem/ton-connect/overview" + }, + { + "source": "/ecosystem/ton-connect/wallet", + "destination": "/ecosystem/ton-connect/overview" + }, + { + "source": "/ecosystem/ton-connect/manifest", + "destination": "/ecosystem/ton-connect/overview" + }, + { + "source": "/ecosystem/ton-connect/message-lookup", + "destination": "/ecosystem/ton-connect/overview" + }, + { + "source": "/standard/wallets/gasless", + "destination": "/ecosystem/appkit/howto/gasless", + "permanent": true + }, + { + "source": "/standard/tokens/nft/nft-2.0", + "destination": "/standard/tokens/nft/overview", + "permanent": true + }, + { + "source": "/foundations/tlb/overview", + "destination": "/languages/tl-b/overview", + "permanent": true + }, + { + "source": "/foundations/tlb/syntax-and-semantics", + "destination": "/languages/tl-b/syntax-and-semantics", + "permanent": true + }, + { + "source": "/foundations/tlb/simple-examples", + "destination": "/languages/tl-b/simple-examples", + "permanent": true + }, + { + "source": "/foundations/tlb/complex-and-non-trivial-examples", + "destination": "/languages/tl-b/complex-and-non-trivial-examples", + "permanent": true + }, + { + "source": "/foundations/tlb/tep-examples", + "destination": "/languages/tl-b/tep-examples", + "permanent": true + }, + { + "source": "/foundations/tlb/tooling", + "destination": "/languages/tl-b/tooling", + "permanent": true + }, + { + "source": "/foundations/consensus/catchain-overview", + "destination": "/foundations/whitepapers/catchain", + "permanent": true + }, + { + "source": "/foundations/whitepapers/comments", + "destination": "/foundations/whitepapers/overview", + "permanent": true + }, + { + "source": "/languages/fift/types", + "destination": "/languages/fift/overview", + "permanent": true + }, + { + "source": "/languages/fift/basic-values", + "destination": "/languages/fift/overview", + "permanent": true + }, + { + "source": "/languages/fift/variables", + "destination": "/languages/fift/overview", + "permanent": true + }, + { + "source": "/languages/fift/control", + "destination": "/languages/fift/overview", + "permanent": true + }, + { + "source": "/languages/fift/fift-assembler", + "destination": "/languages/fift/fift-and-tvm-assembly", + "permanent": true } ] } diff --git a/ecosystem/api/toncenter/smc-index.json b/ecosystem/api/toncenter/smc-index.json index 97fdbf38c..f4f971e03 100644 --- a/ecosystem/api/toncenter/smc-index.json +++ b/ecosystem/api/toncenter/smc-index.json @@ -62,7 +62,8 @@ "schema": { "type": "string", "description": "The nominator address.", - "title": "Nominator" + "title": "Nominator", + "example": "UQDTKO3a_hF6r_i_9RcLe5euCgMsfCkrrmBfS_ZdfUgoNiei" }, "description": "The nominator address." }, @@ -80,7 +81,8 @@ } ], "description": "The pool address in which nominator stakes coins. If not specified, returns nominator from all his pools.", - "title": "Pool" + "title": "Pool", + "example": "Ef8OsrSEsBU_e9xPvgbNMjo700s1gqJSwvqnzM5tWv5CCGsX" }, "description": "The pool address in which nominator stakes coins. If not specified, returns nominator from all his pools." } @@ -134,7 +136,8 @@ "schema": { "type": "string", "description": "The pool address. Can be sent in hex, base64 or base64url form.", - "title": "Pool" + "title": "Pool", + "example": "Ef8OsrSEsBU_e9xPvgbNMjo700s1gqJSwvqnzM5tWv5CCGsX" }, "description": "The pool address. Can be sent in hex, base64 or base64url form." } @@ -184,7 +187,8 @@ "schema": { "type": "string", "description": "The nominator address.", - "title": "Nominator" + "title": "Nominator", + "example": "UQDTKO3a_hF6r_i_9RcLe5euCgMsfCkrrmBfS_ZdfUgoNiei" }, "description": "The nominator address." }, @@ -195,7 +199,8 @@ "schema": { "type": "string", "description": "Pool address to get bookings in.", - "title": "Pool" + "title": "Pool", + "example": "Ef8OsrSEsBU_e9xPvgbNMjo700s1gqJSwvqnzM5tWv5CCGsX" }, "description": "Pool address to get bookings in." }, @@ -304,7 +309,8 @@ "schema": { "type": "string", "description": "The nominator address.", - "title": "Nominator" + "title": "Nominator", + "example": "UQDTKO3a_hF6r_i_9RcLe5euCgMsfCkrrmBfS_ZdfUgoNiei" }, "description": "The nominator address." }, @@ -315,7 +321,8 @@ "schema": { "type": "string", "description": "Pool address to get earnings in.", - "title": "Pool" + "title": "Pool", + "example": "Ef8OsrSEsBU_e9xPvgbNMjo700s1gqJSwvqnzM5tWv5CCGsX" }, "description": "Pool address to get earnings in." }, @@ -420,7 +427,8 @@ "schema": { "type": "string", "description": "Pool address to get bookings in.", - "title": "Pool" + "title": "Pool", + "example": "Ef8OsrSEsBU_e9xPvgbNMjo700s1gqJSwvqnzM5tWv5CCGsX" }, "description": "Pool address to get bookings in." }, diff --git a/ecosystem/api/toncenter/v2.json b/ecosystem/api/toncenter/v2.json index 231e6d24a..ee5636613 100644 --- a/ecosystem/api/toncenter/v2.json +++ b/ecosystem/api/toncenter/v2.json @@ -841,7 +841,15 @@ "operationId": "getMasterchainBlockSignatures_get", "parameters": [ { - "$ref": "#/components/parameters/seqno" + "name": "seqno", + "in": "query", + "description": "Masterchain block sequence number (block height). Used to query state at a specific point in time. If omitted, returns the current state.", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 68213743 } ], "responses": { @@ -1006,7 +1014,15 @@ "$ref": "#/components/parameters/shard" }, { - "$ref": "#/components/parameters/seqnoOptional" + "name": "seqno", + "in": "query", + "description": "Block sequence number to look up. Provide this, `lt`, or `unixtime` to identify the block. If omitted, returns the current block.", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 73082262 }, { "name": "lt", @@ -1076,8 +1092,15 @@ "operationId": "getShards_get", "parameters": [ { - "$ref": "#/components/parameters/seqno", - "description": "Seqno of masterchain block" + "name": "seqno", + "in": "query", + "description": "Seqno of masterchain block", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 68213743 } ], "responses": { @@ -1234,6 +1257,11 @@ "application/json": { "schema": { "$ref": "#/components/schemas/RunGetMethodRequest" + }, + "example": { + "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", + "method": "get_jetton_data", + "stack": [] } } }, @@ -1295,6 +1323,11 @@ "application/json": { "schema": { "$ref": "#/components/schemas/RunGetMethodStdRequest" + }, + "example": { + "address": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", + "method": "get_jetton_data", + "stack": [] } } }, @@ -1472,6 +1505,11 @@ "application/json": { "schema": { "$ref": "#/components/schemas/EstimateFeeRequest" + }, + "example": { + "address": "EQAPgRDXZBQAWp8KfetNFZOLHNjbIt8_Fg7VtIM3c1q7YtHM", + "body": "te6cckEBBAEAkQABoXNpZ25///8R/////wAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAECCg7DyG0CAgMAAABgQgAHwIhrsgoALU+FPvWmisnFjmxtkW+fiwdq2kGbua1dsQAAAAAAAAAAAAAAAAAASigcDQ==", + "ignore_chksig": true } } }, @@ -1722,7 +1760,8 @@ "schema": { "type": "integer", "format": "int32" - } + }, + "example": 0 }, { "name": "seqno", @@ -1838,14 +1877,17 @@ { "name": "libraries", "in": "query", - "description": "Hashes of libraries", - "required": false, + "description": "Hashes of libraries to fetch. Provide one or more library cell hashes in hex format.", + "required": true, "schema": { "type": "array", "items": { - "type": "#/components/schemas/TonHash" + "$ref": "#/components/schemas/TonHash" } - } + }, + "example": [ + "8f452d7a4dfd74066b682365177259ed05734435be76b5fd4bd5d8af2b7c3d68" + ] } ], "responses": { @@ -1898,6 +1940,12 @@ "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcRequest" + }, + "example": { + "jsonrpc": "2.0", + "id": "1", + "method": "getMasterchainInfo", + "params": {} } } }, @@ -1987,7 +2035,8 @@ "schema": { "$ref": "#/components/schemas/TonAddr" }, - "description": "The account address to query." + "description": "The account address to query.", + "example": "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs" }, "hash": { "name": "hash", @@ -1995,7 +2044,8 @@ "required": true, "schema": { "$ref": "#/components/schemas/TonHash" - } + }, + "example": "b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" }, "hashOptional": { "name": "hash", @@ -2013,7 +2063,8 @@ "schema": { "type": "integer", "format": "int32" - } + }, + "example": 0 }, "shard": { "name": "shard", @@ -2023,7 +2074,8 @@ "schema": { "type": "string", "x-usrv-cpp-type": "std::int64_t" - } + }, + "example": "8000000000000000" }, "seqno": { "name": "seqno", @@ -2033,7 +2085,8 @@ "schema": { "type": "integer", "format": "int32" - } + }, + "example": 73082262 }, "seqnoOptional": { "name": "seqno", @@ -2147,7 +2200,8 @@ "schema": { "$ref": "#/components/schemas/TonAddr" }, - "description": "Source account address." + "description": "Source account address.", + "example": "EQDOz0WkOipe6e8v0ExU_VNozZUfRFMTWjuuBkRSndpy9fp_" }, "destinationAddress": { "name": "destination", @@ -2156,7 +2210,8 @@ "schema": { "$ref": "#/components/schemas/TonAddr" }, - "description": "Destination account address." + "description": "Destination account address.", + "example": "EQDOj8JxAaq_1ZUE7o4O6pX7PxIsRu9uqq8lLAdBDtB_L-OH" }, "createdLt": { "name": "created_lt", @@ -2166,7 +2221,8 @@ "schema": { "type": "string", "x-usrv-cpp-type": "std::int64_t" - } + }, + "example": "78473359000006" } }, "responses": { diff --git a/ecosystem/api/toncenter/v3.yaml b/ecosystem/api/toncenter/v3.yaml index f3a719430..2121309eb 100644 --- a/ecosystem/api/toncenter/v3.yaml +++ b/ecosystem/api/toncenter/v3.yaml @@ -26,6 +26,8 @@ paths: type: array items: type: string + example: + - EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs - name: include_boc in: query description: 'Include code and data BOCs. Default: true' @@ -264,6 +266,8 @@ paths: type: array items: type: string + example: + - EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs responses: '200': description: OK @@ -292,6 +296,7 @@ paths: required: true schema: type: string + example: EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs - name: use_v2 in: query description: Use method from api/v2. Not recommended @@ -323,16 +328,20 @@ paths: - name: hash in: query description: Transaction hash. + required: true schema: type: string + example: 6XDJwadlnDFW+HLKm/OoDTpiwOAyoyNMLHCcWYc0omk= - name: direction in: query description: Direction of message. + required: true schema: type: string enum: - in - out + example: in responses: '200': description: OK @@ -473,6 +482,9 @@ paths: type: array items: type: string + example: + - '0xf8a7ea5' + - '0x178d4519' - name: bodies in: query description: List of message bodies to decode (base64 or hex) @@ -508,6 +520,10 @@ paths: application/json: schema: $ref: '#/components/schemas/DecodeRequest' + example: + opcodes: + - '0xf8a7ea5' + - '0x178d4519' responses: '200': description: OK @@ -540,6 +556,7 @@ paths: description: Domain name to search for. DNS records with this exact domain name will be returned. schema: type: string + example: foundation.ton - name: limit in: query description: Limit number of queried rows. Use with *offset* to batch read. @@ -585,6 +602,10 @@ paths: application/json: schema: $ref: '#/components/schemas/V2EstimateFeeRequest' + example: + address: EQAPgRDXZBQAWp8KfetNFZOLHNjbIt8_Fg7VtIM3c1q7YtHM + body: te6cckEBBAEAkQABoXNpZ25///8R/////wAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAECCg7DyG0CAgMAAABgQgAHwIhrsgoALU+FPvWmisnFjmxtkW+fiwdq2kGbua1dsQAAAAAAAAAAAAAAAAAASigcDQ== + ignore_chksig: true responses: '200': description: OK @@ -948,6 +969,7 @@ paths: schema: type: integer format: int32 + example: 68213743 responses: '200': description: OK @@ -977,6 +999,7 @@ paths: schema: type: integer format: int32 + example: 68213743 - name: limit in: query description: Limit number of queried rows. Use with *offset* to batch read. @@ -1197,6 +1220,8 @@ paths: type: array items: type: string + example: + - EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs responses: '200': description: OK @@ -1230,11 +1255,14 @@ paths: - name: multisig_address in: query description: 'Address of corresponding multisig. Max: 1024.' + required: true explode: true schema: type: array items: type: string + example: + - EQA4qyJqpTzdWEIIgW-OUkg3Jhrl4vrGkqYkzNCxR-fk70Cd - name: parse_actions in: query description: Parser order actions @@ -1292,11 +1320,14 @@ paths: - name: address in: query description: 'Multisig contract address in any form. Max: 1024.' + required: true explode: true schema: type: array items: type: string + example: + - EQA4qyJqpTzdWEIIgW-OUkg3Jhrl4vrGkqYkzNCxR-fk70Cd - name: wallet_address in: query description: 'Address of signer or proposer wallet in any form. Max: 1024.' @@ -1605,8 +1636,10 @@ paths: - name: account in: query description: List of account addresses to get actions. Can be sent in hex, base64 or base64url form. + required: true schema: type: string + example: EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs - name: ext_msg_hash in: query description: Find actions by trace external hash @@ -1654,8 +1687,10 @@ paths: - name: account in: query description: List of account addresses to get transactions. Can be sent in hex, base64 or base64url form. + required: true schema: type: string + example: EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs - name: ext_msg_hash in: query description: Find trace by external hash @@ -1689,11 +1724,14 @@ paths: - name: account in: query description: List of account addresses to get transactions. Can be sent in hex, base64 or base64url form. + required: true explode: true schema: type: array items: type: string + example: + - EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs - name: trace_id in: query description: Find transactions by trace id. @@ -1746,6 +1784,10 @@ paths: application/json: schema: $ref: '#/components/schemas/V2RunGetMethodRequest' + example: + address: EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs + method: get_jetton_data + stack: [] responses: '200': description: OK @@ -2067,6 +2109,7 @@ paths: schema: type: integer format: int32 + example: 68213743 - name: limit in: query description: Limit number of queried rows. Use with *offset* to batch read. @@ -2118,8 +2161,10 @@ paths: - name: msg_hash in: query description: Message hash. Acceptable in hex, base64 and base64url forms. + required: true schema: type: string + example: 2jgA4GAQZXcA7ZlrdQ7IA+EhY8KHg8ccLRqKpzA/T2A= - name: body_hash in: query description: Hash of message body. @@ -2180,11 +2225,14 @@ paths: - name: contract_address in: query description: 'Vesting contract address in any form. Max: 1000.' + required: true explode: true schema: type: array items: type: string + example: + - EQA5OSku0NViTdvki1NQwlU-SBmCI2CYFOLer9Cx__KZX95d - name: wallet_address in: query description: 'Wallet address to filter by owner or sender. Max: 1000.' @@ -2245,6 +2293,7 @@ paths: required: true schema: type: string + example: UQDve6CLVbaaXQTd54gI-XK8iR63SsaSgcoRZ9byuSFdahyg - name: use_v2 in: query description: Use method from api/v2. Not recommended @@ -2282,6 +2331,8 @@ paths: type: array items: type: string + example: + - EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs responses: '200': description: OK diff --git a/ecosystem/appkit/changelog.mdx b/ecosystem/appkit/changelog.mdx new file mode 100644 index 000000000..2238fa3d7 --- /dev/null +++ b/ecosystem/appkit/changelog.mdx @@ -0,0 +1,80 @@ +--- +title: "Changelog" +sidebarTitle: "Changelog" +--- + +AppKit ships as two paired packages — `@ton/appkit` and `@ton/appkit-react` — that version together. Each release below lists the matching pair, and pulls in notable changes from the underlying `@ton/walletkit` when they affect AppKit consumers. + +AppKit is early-stage. Pin a known-good version in `package.json` and review this page before upgrading. + +## `@ton/appkit` 0.0.4 — 2026-03-25 + +Paired with `@ton/appkit-react` 0.0.5 · `@ton/walletkit` 0.0.11. + +**Added** + +- `getMasterchainInfo` on `ApiClient` (implemented in `ApiClientToncenter` and `ApiClientTonApi`). +- `getBlockNumber` core action in `@ton/appkit`. +- `useBlockNumber` React hook in `@ton/appkit-react`. +- `ApiClientTonApi` gained or improved: `jettonsByOwnerAddress`, `nftItemsByAddress`, `nftItemsByOwner`, `runGetMethod`, `getAccountTransactions`, `getTransactionsByHash`, `getTrace`, `getPendingTrace`, `getEvents`, `getMasterchainInfo`. +- `connectionEventFromUrl` for handling a TON Connect connect event in the same place the URL is received (via `@ton/walletkit`). +- Log levels configurable from environment (via `@ton/walletkit`). + +**Changed (breaking)** + +- `SwapQuote` and `SwapQuoteParams`: `amount`, `fromAmount`, `toAmount`, and `minReceived` changed type from `TokenAmount` to `string`. These fields now carry already-formatted, human-readable values; `TokenAmount` remains the raw nano representation. + +**Changed** + +- Switched to the public `@tonconnect/bridge-sdk` package (via `@ton/walletkit`). + +## `@ton/appkit` 0.0.3 — 2026-03-06 + +Paired with `@ton/appkit-react` 0.0.4 · `@ton/walletkit` 0.0.10. + +**Added** + +- Support for the Tetra network and `ApiClientTonApi` implementation. +- `getDefaultNetwork`, `setDefaultNetwork`, `watchDefaultNetwork` core actions. +- `useDefaultNetwork` and `useNetworks` React hooks. +- Internal event-bus subscription for `defaultNetwork` updates. +- `TonConnectConnector` now subscribes to `NETWORKS_EVENTS.DEFAULT_CHANGED` for automatic network switching when the wallet reports a network change. + +**Changed (breaking)** + +- React hook rename: `useNFTsByAddress` → `useNftsByAddress`. +- `ApiClient.sendBoc` return value now uses a `0x`-prefixed hex string. + +**Fixed** + +- Infinite re-render in `useNetworks`. + +**Internal** + +- API clients refactored over a shared abstract `BaseApiClient`. + +## `@ton/appkit` 0.0.2 — 2026-02-26 + +Paired with `@ton/appkit-react` 0.0.3 · `@ton/walletkit` 0.0.9. + +**Added** + +- `getTransactionStatus` accepts either a BOC or a hash. + +**Changed (breaking)** + +- `ApiClient.sendBoc` adds the `0x` prefix to the returned hash. + +## `@ton/appkit` 0.0.1 + +Paired with `@ton/appkit-react` 0.0.1. + +- Initial alpha release. + +## Upgrade notes + +The breaking changes worth a code-level review: + +- **`SwapQuote` amounts as strings (0.0.4).** Anything that treated `SwapQuote.amount`, `fromAmount`, `toAmount`, or `minReceived` as nano `TokenAmount` values needs to handle them as already-formatted decimal strings. Raw nano values now live only in `TokenAmount`-typed fields. +- **`useNFTsByAddress` → `useNftsByAddress` (0.0.3).** Casing-only rename; behavior is unchanged. +- **`ApiClient.sendBoc` returns `0x`-prefixed hex (0.0.2 and 0.0.3).** Callers that compared the return value as a bare hash string need to strip or expect the prefix. diff --git a/ecosystem/appkit/faq-troubleshooting.mdx b/ecosystem/appkit/faq-troubleshooting.mdx new file mode 100644 index 000000000..58bd2d4ca --- /dev/null +++ b/ecosystem/appkit/faq-troubleshooting.mdx @@ -0,0 +1,77 @@ +--- +title: "FAQ & Troubleshooting" +sidebarTitle: "FAQ & Troubleshooting" +--- + +Use this page to answer common AppKit questions and triage the most common failure classes: connector issues, wallet rejection, network mismatch, provider gaps, and settlement confusion. + +## FAQ + +### What is AppKit? + +AppKit is a TypeScript toolkit for TON wallet connection, transaction requests, asset reads, provider integrations, React hooks, and UI components. + +### Is AppKit a wallet? + +No. Wallets hold keys and sign requests. AppKit prepares requests, tracks application state, and routes actions through connectors and providers. + +### Does AppKit store private keys? + +No. Private keys stay in the user's wallet. AppKit stores runtime configuration, local state, and provider registrations. + +### Can AppKit be used without React? + +Yes. The core package `@ton/appkit` works from plain TypeScript. React-specific hooks and components live in `@ton/appkit-react`. + +### How is transaction success confirmed? + +A wallet transaction response only proves the user signed and the wallet submitted the request. Confirm success with `getTransactionStatus`, streaming updates, or backend verification before changing product state. + +### Why does a wallet reject a request? + +Common causes include user rejection, expired requests, wrong network, missing wallet selection, malformed payloads, and insufficient balance for value plus fees. + +## Troubleshoot by symptom + +### The connect button opens, but no wallet connects + +Check the connector configuration first: + +- confirm the TON Connect manifest URL is public and reachable +- confirm at least one connector is registered on the `AppKit` instance +- confirm the React tree is wrapped with `AppKitProvider` + +If the UI is custom, confirm the code passes a valid `connectorId` to `connect`. + +### Reads work, but watch hooks never update + +Streaming hooks need a streaming provider. Confirm that: + +- a streaming provider is registered for the active network +- the network passed to the watcher matches the configured network +- `hasStreamingProvider(appKit, network)` returns `true` + +### The wallet signs, but the app never sees settlement + +Separate wallet acceptance from on-chain settlement: + +- treat the wallet response as a submission result, not finality +- check `getTransactionStatus` with the returned `boc` +- if streaming is used, remember that streaming finality uses `pending`, `confirmed`, `finalized`, and `invalidated` + +### Jetton or NFT data looks wrong + +Treat off-chain metadata as display data: + +- read decimals from jetton metadata before building transfers +- sanitize jetton and NFT names, images, and descriptions +- route by contract addresses, not by display metadata + +### A transaction fails on the wrong network + +Check both the selected wallet network and the AppKit default network. Block unsafe actions until the expected network and wallet-reported network match. + +## Related pages + +- [Get started](/ecosystem/appkit/get-started) +- [How to](/ecosystem/appkit/howto) diff --git a/ecosystem/appkit/get-started.mdx b/ecosystem/appkit/get-started.mdx new file mode 100644 index 000000000..8575ecd77 --- /dev/null +++ b/ecosystem/appkit/get-started.mdx @@ -0,0 +1,26 @@ +--- +title: "Get started with AppKit" +sidebarTitle: "Get started" +--- + +Follow this section end-to-end to take a fresh project from zero to a working AppKit app. Each page is one concrete action. + +The first four pages get the baseline running — install the packages, register a connector, read wallet and chain state, and send a transaction. From that point the app is functional. The remaining pages are improvements that layer on live updates, DeFi flows, and ready-made UI. + +## Step by step + +| Step | What you do | +| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| [Prepare your project](/ecosystem/appkit/get-started/installation) | Scaffold a project if you have none, install AppKit, and add the Buffer polyfill. | +| [Add wallet connectors](/ecosystem/appkit/get-started/connectors) | Create the AppKit instance and drop in ``. | +| [Add networks](/ecosystem/appkit/get-started/networks) | Configure mainnet alongside testnet and pick an API client. | +| [Use basic getter hooks](/ecosystem/appkit/get-started/basic-getter-hooks) | Read wallet, address, balance, jetton, NFT, network, and transaction state. | +| [Send transactions](/ecosystem/appkit/get-started/sending-transactions) | Send Toncoin, jettons, NFTs, or prepared transactions. | +| [Stream live updates](/ecosystem/appkit/get-started/streaming) | Subscribe to balance, jetton, and transaction changes in real time. | +| [Add DeFi providers](/ecosystem/appkit/get-started/providers) | Plug in swap and staking providers when the app needs DeFi flows. | +| [Use UI widgets](/ecosystem/appkit/get-started/using-ui-widgets) | Drop in ``, ``, and other prebuilt flows. | + +## What to do next + +- See task-level walkthroughs in [How to](/ecosystem/appkit/howto). +- Look up the full surface in [AppKit reference](/ecosystem/appkit/reference). diff --git a/ecosystem/appkit/get-started/basic-getter-hooks.mdx b/ecosystem/appkit/get-started/basic-getter-hooks.mdx new file mode 100644 index 000000000..b2971734e --- /dev/null +++ b/ecosystem/appkit/get-started/basic-getter-hooks.mdx @@ -0,0 +1,64 @@ +--- +title: "Use basic getter hooks" +sidebarTitle: "Use basic getter hooks" +--- + +Read the connected wallet's state from React using AppKit's getter hooks. They re-render when the underlying state changes and do not themselves start wallet actions — transactions and signatures use mutation hooks such as `useTransferTon`, `useSignText`, and `useSendTransaction`. + +## Before you begin + +You need the React providers in place. See [Installation → Wrap the application](/ecosystem/appkit/get-started/installation/react-app#wrap-the-application). + +## Three return shapes + +Getter hooks come in three return shapes. Which shape a hook uses depends on where the data lives. + +**Tuple with setter** — for state AppKit stores reactively and that the app can change. The first element is the value (or `null` if not yet set), the second is the setter. + +```tsx +const [wallet, setWalletId] = useSelectedWallet(); +const [network, setDefaultNetwork] = useDefaultNetwork(); +``` + +The hooks that return tuples: `useSelectedWallet`, `useDefaultNetwork`. + +**Direct value** — for store-derived state that is read-only from React. Returns the value itself, no wrapper. + +```tsx +const address = useAddress(); // string | undefined +const network = useNetwork(); // Network | undefined +const networks = useNetworks(); // Network[] +const connectors = useConnectors(); // Connector[] +const wallets = useConnectedWallets(); // WalletInterface[] +const connector = useConnectorById('tonconnect'); // Connector | undefined +const appKit = useAppKit(); // AppKit +``` + +These hooks use `useSyncExternalStore` under the hood and re-render when the underlying state changes. + +**TanStack Query result** — for chain data fetched through an API client. Returns `{ data, isLoading, isError, error, refetch }` and accepts a `query` field for `refetchInterval`, `enabled`, `staleTime`, and the rest of the TanStack Query options. + +```tsx +const { data: balance, isLoading } = useBalance(); +const { data: jettons } = useJettons({ query: { refetchInterval: 20000 } }); +``` + +The hooks that return query results: `useBalance`, `useJettons`, `useNfts`, `useTransactionStatus`, `useJettonInfo`, `useJettonBalanceByAddress`, `useJettonWalletAddress`, `useBlockNumber`, `useNft`, and the `*ByAddress` variants. + +## Provider data is not chain finality + +A getter hook reflects a snapshot of provider data, not the chain itself. The user's wallet may report state that differs from what the configured `apiClient` returns — usually because the indexer is a few blocks behind the wallet, sometimes because the two read different networks. For live updates without manual refetch, mount the matching `useWatch*` hook (covered on [Stream live updates](/ecosystem/appkit/get-started/streaming)). + +## Tips + +Treat hook data as the latest read, not as a settlement check. Verify chain effects with `getTransactionStatus` or a backend before crediting value, and re-check after any reconnect. + +## Code example + +See a [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) that uses `useBalance`, `useJettons`, and `useNfts` to render wallet state — [try it live](https://appkit-minter.vercel.app/). + +## What to do next + +- Move on to [Send transactions](/ecosystem/appkit/get-started/sending-transactions) — at that point you'll have a working app. +- For a full walkthrough of reading TON, jetton, and NFT data, see [Read balances](/ecosystem/appkit/howto/read-balances). +- Look up the full hook surface in the [`@ton/appkit-react` reference](/ecosystem/appkit/reference/appkit-react). diff --git a/ecosystem/appkit/get-started/connectors.mdx b/ecosystem/appkit/get-started/connectors.mdx new file mode 100644 index 000000000..a5b80ca9c --- /dev/null +++ b/ecosystem/appkit/get-started/connectors.mdx @@ -0,0 +1,52 @@ +--- +title: "Add wallet connectors" +sidebarTitle: "Add wallet connectors" +--- + +Wire up a connector so AppKit can talk to a wallet. A connector is the transport that carries requests to the wallet and responses back — it does not sign, and is not itself a wallet. Signing always happens inside the user's wallet application. AppKit cares about the connector's `id`, `type`, `metadata`, and the methods it exposes (`connectWallet`, `disconnectWallet`, `getConnectedWallets`, `destroy`). The protocol underneath is hidden. + +## Before you begin + +Install AppKit first. See [Prepare your project](/ecosystem/appkit/get-started/installation). + +## Create the AppKit instance + +The `AppKit` constructor takes a `connectors` array of connector factories such as `createTonConnectConnector`. A single `AppKit` instance can hold more than one connector, and the user picks at connect time. The example below registers a single TON Connect connector — enough to start wiring up the connect button. + +```ts +// appkit.ts +import { AppKit, createTonConnectConnector } from '@ton/appkit'; + +export const appKit = new AppKit({ + connectors: [ + createTonConnectConnector({ + tonConnectOptions: { + manifestUrl: 'https://example.com/tonconnect-manifest.json', + }, + }), + ], +}); +``` + +## Render a connect button + +Drop `` somewhere in the tree to open the wallet picker. The button assumes the React providers are already mounted — see [Installation → Wrap the application](/ecosystem/appkit/get-started/installation/react-app#wrap-the-application). + +For most apps, this drop-in component is the right starting point. `` from `@ton/appkit-react` owns the connect modal, the session lifecycle (open, restore on reload, expire), and the connected-address display. When the UI needs a tailored wallet list, use `useConnectors` and `useConnect` directly. That pattern is documented in [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet). + +```tsx +import { TonConnectButton } from '@ton/appkit-react'; + +export function Header() { + return ; +} +``` + +## Tips + +Do not assume every connector supports every flow. Check capability before sending a request and treat user rejection as a normal outcome on every action. Connector errors and chain errors are different failure classes and need different recovery paths. + +## What to do next + +- Continue to [Add networks](/ecosystem/appkit/get-started/networks) to configure mainnet alongside testnet and pick an API client. +- For the custom-selector pattern, vanilla JS connect, and error handling, see [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet). diff --git a/ecosystem/appkit/get-started/installation.mdx b/ecosystem/appkit/get-started/installation.mdx new file mode 100644 index 000000000..73fb44ec2 --- /dev/null +++ b/ecosystem/appkit/get-started/installation.mdx @@ -0,0 +1,83 @@ +--- +title: "Prepare your project" +sidebarTitle: "Prepare your project" +--- + +Before adding AppKit, you need a JavaScript project with a module bundler. If you already have one, skip the scaffold step. The Buffer polyfill is required either way — AppKit's dependencies (`@ton/core` and `@ton/crypto`) use Node.js' `Buffer` global, which browsers don't expose and bundlers (Vite, esbuild, Rollup, Parcel) don't polyfill automatically. Without it, the first call into `@ton/core` — usually address parsing — throws `ReferenceError: Buffer is not defined`. + +The polyfill must run before any AppKit import. Load it from a dedicated module in the HTML entry, ahead of the application script — relying on import order inside the entry file alone is fragile. + +The steps below use Vite. The same pattern applies to any module-based bundler (Webpack, Parcel, Rollup): a dedicated polyfill module referenced from the HTML entry, before the application script. + + + + Create a new Vite project with the React + TypeScript template. + + ```shell + pnpm create vite my-app --template react-ts + cd my-app + pnpm install + ``` + + Swap `pnpm` for `npm create` or `yarn create` if you don't use pnpm. + + + + ```shell + npm i buffer + ``` + + + + Add a file that imports `buffer` and exposes it on `window`. Use `.ts` for TypeScript projects, `.js` otherwise. + + ```ts + // src/buffer.ts + import { Buffer } from 'buffer'; + + window.Buffer = Buffer; + ``` + + + + In `index.html`, reference the polyfill from a ` + + + ``` + + + +## Next steps + + + + Install `@ton/appkit-react` with TanStack Query and TON Connect UI for React. + + + + Install `@ton/appkit` with TON Connect UI. + + diff --git a/ecosystem/appkit/get-started/installation/react-app.mdx b/ecosystem/appkit/get-started/installation/react-app.mdx new file mode 100644 index 000000000..2db993c52 --- /dev/null +++ b/ecosystem/appkit/get-started/installation/react-app.mdx @@ -0,0 +1,128 @@ +--- +title: "Install AppKit in a React app" +sidebarTitle: "React App" +--- + +Install AppKit's React packages, create an `AppKit` instance, and mount the providers that AppKit hooks rely on. + +## Install packages + +```bash +npm i @ton/appkit @ton/appkit-react @tanstack/react-query @tonconnect/ui-react @ton/core +``` + +| Package | Use | +| ----------------------- | ----------------------------------------------------------------------------------------------- | +| `@ton/appkit` | Core runtime: actions, connectors, types. | +| `@ton/appkit-react` | React layer: hooks, components, default stylesheet. | +| `@tanstack/react-query` | Cache backing every query and mutation hook. | +| `@tonconnect/ui-react` | TON Connect transport. `@ton/appkit-react` re-exports `` from this package. | +| `@ton/core` | TON address and cell primitives. | + + + Complete the [Preparation](/ecosystem/appkit/get-started/installation) step first. The `Buffer` polyfill it sets up is required for AppKit to run in the browser. + + +## Create the AppKit instance + +Create the `AppKit` instance in a dedicated module. An empty configuration is enough to start — connectors, networks, and DeFi providers are added on the following pages. + +```ts +// src/appkit.ts +import { AppKit } from "@ton/appkit-react" + +export const appKit = new AppKit({}); +``` + +## Wrap the application + +AppKit hooks read from two React contexts: the `AppKit` instance and a TanStack Query cache. Mount both at the application root. + + + + `AppKitProvider` exposes the `AppKit` instance to descendant hooks and components, removing the need to thread it through props. + + ```tsx + import { AppKitProvider } from '@ton/appkit-react' + import { appKit } from './appkit' + + function App() { + return ( + + {/** ... */} + + ) + } + ``` + + + + `QueryClientProvider` owns the cache used by every query and mutation hook. Create one `QueryClient` per application and mount it inside `AppKitProvider`. + + ```tsx + import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + import { AppKitProvider } from '@ton/appkit-react' + import { appKit } from './appkit' + + const queryClient = new QueryClient() + + function App() { + return ( + + + {/** ... */} + + + ) + } + ``` + + + +## Import styles + +To use the bundled components from `@ton/appkit-react` (``, ``, ``), import the default stylesheet once at the application root. Without it, the components render unstyled. + +```ts +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { AppKitProvider } from '@ton/appkit-react' +import { appKit } from './appkit' + +import '@ton/appkit-react/styles.css' + +const queryClient = new QueryClient() + +function App() { + return ( + + + {/** ... */} + + + ) +} +``` + +## Next steps + + + + Register wallet connectors on the `AppKit` instance and render the connect button. + + + + Read wallet, balance, and transaction state from React. + + diff --git a/ecosystem/appkit/get-started/installation/vanilla-js.mdx b/ecosystem/appkit/get-started/installation/vanilla-js.mdx new file mode 100644 index 000000000..0cee000fd --- /dev/null +++ b/ecosystem/appkit/get-started/installation/vanilla-js.mdx @@ -0,0 +1,75 @@ +--- +title: "Install AppKit in a vanilla JS app" +sidebarTitle: "Vanilla JS" +--- + +Install AppKit's core package, create an `AppKit` instance, and call actions such as `connect`, `transferTon`, `sendTransaction`, and the `watch*` subscribers directly on it. + +## Install packages + +```bash +npm i @ton/appkit @tonconnect/ui @ton/core +``` + +| Package | Use | +| ---------------- | ------------------------------------------------------- | +| `@ton/appkit` | Core runtime: actions, connectors, types. | +| `@tonconnect/ui` | TON Connect transport used by the TonConnect connector. | +| `@ton/core` | TON address and cell primitives. | + + + Complete the [Preparation](/ecosystem/appkit/get-started/installation) step first. The `Buffer` polyfill it sets up is required for AppKit to run in the browser. + + +## Create the AppKit instance + +Create the `AppKit` instance in a dedicated module. An empty configuration is enough to start — connectors, networks, and DeFi providers are added on the following pages. + +```ts +// src/appkit.ts +import { AppKit } from '@ton/appkit'; + +export const appKit = new AppKit({}); +``` + +Import the instance wherever a core action is called: + +```ts +import { connect, getBalance, transferTon } from '@ton/appkit'; +import { appKit } from './appkit'; + +await connect(appKit, { connectorId: 'tonconnect' }); +const balance = await getBalance(appKit); +await transferTon(appKit, { recipientAddress, amount: '0.1' }); +``` + +## TanStack Query (optional) + +The `@ton/appkit/queries` entry point exports framework-agnostic `queryOptions` and mutation helpers compatible with any TanStack adapter — `@tanstack/vue-query`, `@tanstack/svelte-query`, `@tanstack/solid-query`, and others. The same entry point backs `@ton/appkit-react` through `@tanstack/react-query`. + +```ts +import { useQuery } from '@tanstack/react-query'; +import { getBalanceQueryOptions } from '@ton/appkit/queries'; + +const { data } = useQuery(getBalanceQueryOptions(appKit, { address: '...' })); +``` + +## Next steps + + + + Register wallet connectors on the `AppKit` instance and open the connect flow. + + + + Call `transferTon`, `transferJetton`, and `sendTransaction` from the AppKit core. + + diff --git a/ecosystem/appkit/get-started/networks.mdx b/ecosystem/appkit/get-started/networks.mdx new file mode 100644 index 000000000..4f28cefdc --- /dev/null +++ b/ecosystem/appkit/get-started/networks.mdx @@ -0,0 +1,54 @@ +--- +title: "Add networks" +sidebarTitle: "Add networks" +--- + +Declare every chain the app can read. A network in AppKit is the chain context AppKit uses to read data and to compare wallet state against the app's expected chain — each network is identified by a `Network` value with a `chainId` and carries an `apiClient` that reads chain state for that chain. + +A single `AppKit` instance can hold more than one network. The user picks at connect time, and a flow can move between networks without rebuilding the instance. + +## Before you begin + +Install AppKit first. See [Prepare your project](/ecosystem/appkit/get-started/installation). + +## Basic configuration + +By default, AppKit constructs a mainnet-only `ApiClientToncenter` with no API key when `networks` is omitted. That is enough to start exploring AppKit, but production traffic will hit TON Center's public rate limits — apps that ship to users should supply their own key. + +Pass an `apiClient` entry on the network with a `key` to lift the limits. AppKit has preset URLs for mainnet (`https://toncenter.com`) and testnet (`https://testnet.toncenter.com`), so `url` can be omitted for those. For other networks, specify `url` explicitly. + +```ts +const appKit = new AppKit({ + networks: { + [Network.mainnet().chainId]: { + apiClient: { + key: 'your-key', + }, + }, + }, +}); +``` + +## Use TonAPI instead + +Besides TON Center, AppKit ships `ApiClientTonApi` for TonAPI and compatible deployments. The inline `{ url, key }` shape is TON Center-only. To use TonAPI, pass a constructed instance into `apiClient`. + +```ts +import { AppKit, ApiClientTonApi, Network } from '@ton/appkit'; + +const appKit = new AppKit({ + networks: { + [Network.mainnet().chainId]: { + apiClient: new ApiClientTonApi({ + network: Network.mainnet(), + apiKey: 'your-key', + }), + }, + }, +}); +``` + +## What to do next + +- Continue to [Use basic getter hooks](/ecosystem/appkit/get-started/basic-getter-hooks) to read the connected wallet's state from the configured networks. +- For multiple networks, custom chains, the default network, and mismatch handling, see [Networks](/ecosystem/appkit/howto/networks). diff --git a/ecosystem/appkit/get-started/providers.mdx b/ecosystem/appkit/get-started/providers.mdx new file mode 100644 index 000000000..1511297fd --- /dev/null +++ b/ecosystem/appkit/get-started/providers.mdx @@ -0,0 +1,63 @@ +--- +title: "Add DeFi providers" +sidebarTitle: "Add DeFi providers" +--- + +Plug in a swap or staking provider when the app needs DeFi flows. Each provider sits on the AppKit instance and supplies data: prices, quotes, and the transactions that the user will sign. The wallet still signs; the provider only prepares. + +A provider is not a wallet and not a connector. AppKit treats whatever it returns as external input — useful for showing prices, balances, and quotes, but never as proof of settlement. + +## Before you begin + +You need AppKit packages installed and an `AppKit` instance created. See [Prepare your project](/ecosystem/appkit/get-started/installation) and [Add wallet connectors](/ecosystem/appkit/get-started/connectors). + +## Add a swap provider + +Swap providers ship as separate entrypoints under `@ton/appkit/swap/*` so the bundler only includes the protocols you actually use. Construct an instance and pass it in the `providers` array on the `AppKit` constructor; AppKit registers it with the swap manager and the swap hooks (`useSwapQuote`, `useBuildSwapTransaction`) start returning quotes from that protocol. + +```ts +// appkit.ts +import { AppKit } from '@ton/appkit'; +import { DeDustSwapProvider } from '@ton/appkit/swap/dedust'; + +export const appKit = new AppKit({ + // networks, connectors... + providers: [ + new DeDustSwapProvider(), + ], +}); +``` + +Omniston requires its own SDK package — install it before registering the provider: + +```bash +npm i @ston-fi/omniston-sdk +``` + +## Add a staking provider + +Use `createTonstakersProvider({})` from `@ton/appkit/staking/tonstakers` the same way: + +```ts +import { AppKit } from '@ton/appkit'; +import { createTonstakersProvider } from '@ton/appkit/staking/tonstakers'; + +export const appKit = new AppKit({ + // networks, connectors... + providers: [ + createTonstakersProvider({}), + ], +}); +``` + +The staking hooks (`useStakingQuote`, `useBuildStakeTransaction`) start returning data from the registered provider. + +## Tips + +Provider responses are external data. Validate critical outcomes server side before changing product state — wallet acceptance is not settlement, and a swap quote is not a price guarantee. + +## What to do next + +- Continue to [Use UI widgets](/ecosystem/appkit/get-started/using-ui-widgets) to render full swap and staking UIs with one component each. +- For task-level walkthroughs, see [Swaps](/ecosystem/appkit/howto/swaps) and [Staking](/ecosystem/appkit/howto/staking). +- See [Providers](/ecosystem/appkit/howto/providers) for the full provider model — including the streaming and API client roles not covered here. diff --git a/ecosystem/appkit/get-started/sending-transactions.mdx b/ecosystem/appkit/get-started/sending-transactions.mdx new file mode 100644 index 000000000..f82bdacf1 --- /dev/null +++ b/ecosystem/appkit/get-started/sending-transactions.mdx @@ -0,0 +1,57 @@ +--- +title: "Send transactions" +sidebarTitle: "Send transactions" +--- + +Assemble a `TransactionRequest`, ask the connected wallet to sign and broadcast it, and wait for the chain to settle the result. AppKit ships three layers over this flow: UI components that wrap the full mutation lifecycle, mutation hooks that expose the same lifecycle without UI, and core actions that just make the wallet call. + +Reaching this page means you have a working app: install, connect, read, send. The pages after this one — streaming, DeFi providers, and UI widgets — layer on improvements rather than baseline functionality. + +The lifecycle has clear ownership boundaries. _Your code_ assembles the request — recipient, amount, payload, validity window. _The wallet_ reviews the request, presents it to the user, and signs it. _The chain_ receives the signed message, orders it, and either includes or rejects it. _The streaming provider_ observes the result and reports back. `onSuccess` firing on a component or hook means the wallet accepted the request, not that the chain accepted the transaction; settlement is observed separately on [Streaming](/ecosystem/appkit/get-started/streaming). + +## Send Toncoin in one component + +`` owns the whole lifecycle for a Toncoin transfer. It builds the request from `recipientAddress`, `amount` (decimal TON), and an optional `comment`; calls the wallet; and surfaces pending, error, and success state through a render-prop child. When you need to send something other than a plain Toncoin transfer — an NFT, a multi-message bundle, a swap-built transaction — use `` with a `request` callback that returns a `TransactionRequest` you assembled yourself. + +```tsx +import { SendTonButton } from '@ton/appkit-react'; + + + {({ isLoading, onSubmit, disabled, text }) => ( + + )} + +``` + +The component exposes `{ isLoading, onSubmit, disabled, text }` to the render-prop child. + +## Pick the right call + +The three layers trade off control against work done for you. Components own the full lifecycle and ship a default render-prop UI hook, so you write the least code; mutation hooks give you the lifecycle without UI, so you build the button yourself; core actions give you neither — just a function that drives the wallet — and are the right pick from vanilla code or when you need to compose multiple transactions in one user action. Pick by how much of the lifecycle you want AppKit to own. + +| Goal | Component | Hook | Core action | +| ----------------------------------- | ---------------------- | -------------------- | ----------------- | +| Send Toncoin | `` | `useTransferTon` | `transferTon` | +| Send a jetton | `` | `useTransferJetton` | `transferJetton` | +| Send an NFT | `` + builder | `useTransferNft` | `transferNft` | +| Send a prepared transaction request | `` | `useSendTransaction` | `sendTransaction` | + +## Tips + +Wallet acceptance is not settlement. Always pair a send with [Stream live updates](/ecosystem/appkit/get-started/streaming) or `getTransactionStatus`, and verify the final outcome on a backend before delivering paid goods. + +## Code example + +See a [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) of TON, jetton, and NFT transfers built with these mutations — [try it live](https://appkit-minter.vercel.app/). + +## What to do next + +You now have the baseline working app. From here, layer on improvements: + +- [Stream live updates](/ecosystem/appkit/get-started/streaming) to react to chain changes without polling. +- [Add DeFi providers](/ecosystem/appkit/get-started/providers) when the app needs swaps or staking. +- [Use UI widgets](/ecosystem/appkit/get-started/using-ui-widgets) to drop in ready-made flows. + +For task-level walkthroughs, see [Send Toncoin](/ecosystem/appkit/howto/send-toncoin), [Send jettons](/ecosystem/appkit/howto/send-jettons), and [NFTs](/ecosystem/appkit/howto/nfts). diff --git a/ecosystem/appkit/get-started/streaming.mdx b/ecosystem/appkit/get-started/streaming.mdx new file mode 100644 index 000000000..75d796fa5 --- /dev/null +++ b/ecosystem/appkit/get-started/streaming.mdx @@ -0,0 +1,47 @@ +--- +title: "Stream live updates" +sidebarTitle: "Stream live updates" +--- + +Subscribe to balance, jetton, and transaction updates pushed by a streaming provider so the UI reflects chain changes without polling. Use it whenever you would otherwise refetch on a timer. + +Treat the stream as a fast cache, not as proof of settlement. The underlying transport is a long-lived connection; it can disconnect and reconnect while the chain advances, and after reconnect updates resume from the provider's current view, which may skip in-flight transitions. + +## Before you begin + +A streaming provider has to be registered on the AppKit instance before any watcher will fire. The default `ApiClient` does not stream — you register a streaming provider separately on `appKit.streamingManager`. See [Providers → How they are registered](/ecosystem/appkit/howto/providers#how-they-are-registered) for the registration pattern, and use `hasStreamingProvider(appKit, network)` from `@ton/appkit` as the runtime check before subscribing. + +## Mount global watchers + +Mount the `useWatch*` hooks once at a high level — the router, the app shell, or a layout component — so the underlying cache stays current for every consumer below. Calling a hook with no arguments simply keeps the cache warm. Pass an `onChange` callback for one-shot side effects (toast, log, navigate). A [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) calls `useWatchBalance()`, `useWatchJettons()`, and `useWatchTransactions({ onChange })` from its router for exactly this reason. [Try it live](https://appkit-minter.vercel.app/). + +Call the `useWatch*` hooks once high in the tree. They keep the cache fresh; pass `onChange` when you want to react to each update. + +```tsx +import { useWatchBalance, useWatchTransactions } from '@ton/appkit-react'; + +export function GlobalWatchers() { + useWatchBalance(); + useWatchTransactions({ + onChange: (update) => { + if (update.status === 'finalized') { + console.log('settled', update.traceHash); + } + }, + }); + return null; +} +``` + +`update.status` is `'pending' | 'confirmed' | 'finalized' | 'invalidated'`. Treat anything before `'finalized'` as not yet settled. + +Each status names a different stage and carries a different meaning. `pending` means the streaming provider has seen the request but not its inclusion in a block. `confirmed` means a block contains the transaction but it is not irreversible. `finalized` means the chain considers the transaction permanent. `invalidated` means the provider has rolled the transaction back or rejected it; treat it the same as a failed send. UI hints are appropriate at every stage; product-state changes (delivering value, settling an order) are appropriate only at `finalized`. + +## Tips + +A stream is a hint, not proof. Verify the final state on a backend before delivering value or settling an order, and re-check after every reconnect — the stream may have missed transitions while disconnected. + +## What to do next + +- Continue to [Add DeFi providers](/ecosystem/appkit/get-started/providers) when the app needs swap or staking flows. +- For task-level patterns that combine sends with settlement tracking, see [Streaming](/ecosystem/appkit/howto/streaming) in How to. diff --git a/ecosystem/appkit/get-started/using-ui-widgets.mdx b/ecosystem/appkit/get-started/using-ui-widgets.mdx new file mode 100644 index 000000000..835f29bf2 --- /dev/null +++ b/ecosystem/appkit/get-started/using-ui-widgets.mdx @@ -0,0 +1,63 @@ +--- +title: "Use UI widgets" +sidebarTitle: "Use UI widgets" +--- + +Drop in ready-made React components when an AppKit flow needs to ship fast. This page shows the three component shapes and when to pick each one. + +## Before you begin + +Install the React packages, mount `AppKitProvider`, and import the default stylesheet. See [Install AppKit in a React app](/ecosystem/appkit/get-started/installation/react-app). + +## Pick a widget level + +AppKit UI widgets come in three levels: + +| Level | Use when | +| ---------------------- | ------------------------------------------------------------------------------------ | +| Default component | The app needs a ready-made flow with the package stylesheet. | +| Render-prop component | The app needs AppKit to own the state machine, but the layout must be custom. | +| Hooks and core actions | The flow no longer matches one widget, or state must be shared across a larger page. | + +Default components such as `` and `` cover the full user flow. Render-prop components keep the same behavior while letting the app render custom markup. Hooks and core actions are the lowest level and fit flows that need fully custom product logic. + +## Start with a ready-made component + +`` is the smallest example. It opens the TON Connect modal, restores the session, and renders the selected wallet address. + +```tsx +import { TonConnectButton } from '@ton/appkit-react'; + +export function Header() { + return ; +} +``` + +## Move to a custom send flow + +`` keeps the AppKit transaction flow while exposing render props for custom UI. + +```tsx +import { SendTonButton } from '@ton/appkit-react'; + + + {({ isLoading, onSubmit, disabled, text }) => ( + + )} +; +``` + +## Tips + +Pick the widget level by control needs. Use a ready-made component for the smallest integration, render props for custom layout, and hooks or core actions when the flow needs more control than one widget can provide. + +## Code example + +See a [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) that integrates `` and `` — [try it live](https://appkit-minter.vercel.app/). + +## What to do next + +- For a component-by-component walkthrough (Send, NFT, Swap, Staking, transaction progress), see [Use UI widgets](/ecosystem/appkit/howto/use-ui-widgets). +- For task-level recipes, switch to [How to](/ecosystem/appkit/howto). diff --git a/ecosystem/appkit/howto.mdx b/ecosystem/appkit/howto.mdx new file mode 100644 index 000000000..ebf18e1d0 --- /dev/null +++ b/ecosystem/appkit/howto.mdx @@ -0,0 +1,42 @@ +--- +title: "How to" +sidebarTitle: "Overview" +--- + +How-to guides explain how AppKit features work, then show the code path for using them in an app. + +Start with [Get started](/ecosystem/appkit/get-started) for installation and the smallest setup. Use these guides when you need both the model behind a feature and the practical code that drives it. + +## Core setup + +| Guide | What it covers | +| ------------------------------------------------------------------ | -------------------------------------------------------------------- | +| [AppKit](/ecosystem/appkit/howto/appkit) | Runtime state, configuration, lifecycle, and React provider setup. | +| [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet) | Wallet and connector model, connect UI, selected wallet, and errors. | +| [Providers](/ecosystem/appkit/howto/providers) | API clients, streaming providers, swap providers, and staking setup. | +| [Networks](/ecosystem/appkit/howto/networks) | Mainnet, testnet, default network, API clients, and mismatch checks. | + +## Wallet data and assets + +| Guide | What it covers | +| ------------------------------------------------------ | -------------------------------------------------------------------- | +| [Read balances](/ecosystem/appkit/howto/read-balances) | Toncoin and jetton reads for the selected wallet or any address. | +| [Send Toncoin](/ecosystem/appkit/howto/send-toncoin) | Toncoin transfer flow, amount handling, and settlement checks. | +| [Send jettons](/ecosystem/appkit/howto/send-jettons) | Jetton metadata, decimals, transfer requests, and settlement checks. | +| [NFTs](/ecosystem/appkit/howto/nfts) | NFT reads, rendering, transfer requests, and ownership verification. | +| [Sign data](/ecosystem/appkit/howto/sign-data) | Text, binary, and cell signatures with verifier-side checks. | +| [Streaming](/ecosystem/appkit/howto/streaming) | Live balance, jetton, and transaction updates. | + +## Product flows + +| Guide | What it covers | +| -------------------------------------------------------- | ----------------------------------------------------------------- | +| [Swaps](/ecosystem/appkit/howto/swaps) | Quote freshness, provider routing, build flow, and settlement. | +| [Staking](/ecosystem/appkit/howto/staking) | Stake and unstake quotes, provider metadata, balances, and sends. | +| [Onramp](/ecosystem/appkit/howto/onramp) | Fiat redirects, crypto deposits, provider status, and completion. | +| [Gasless](/ecosystem/appkit/howto/gasless) | Jetton-paid fees, relayer estimates, wallet signing, and sends. | +| [Use UI widgets](/ecosystem/appkit/howto/use-ui-widgets) | Default UI, render props, hooks, and vertical flow components. | + +## Conventions + +Every guide assumes an `AppKit` instance and the React provider are already mounted unless it is showing setup. Code samples use public package imports and keep product-state decisions separate from wallet approval. diff --git a/ecosystem/appkit/howto/appkit.mdx b/ecosystem/appkit/howto/appkit.mdx new file mode 100644 index 000000000..b9316b83b --- /dev/null +++ b/ecosystem/appkit/howto/appkit.mdx @@ -0,0 +1,74 @@ +--- +title: "AppKit" +sidebarTitle: "AppKit" +--- + +TON Connect's **AppKit** is an open-source SDK that integrates web2 and web3 applications with TON. It enables wallet connection, authorization, balance tracking, asset transfers, and data signing. + +The framework-independent core lives in `@ton/appkit`. React bindings live in `@ton/appkit-react` and wrap the same actions with hooks. + +AppKit prepares requests, tracks application state, and connects your interface to wallets and providers. Wallets still sign transactions and data. Your backend still verifies orders, signatures, and business rules. + +## Features + +- **TON Connect**: integrates with the standard wallet infrastructure for TON. +- **Wallet management**: access connected wallets from registered connectors. +- **Asset support**: work with Toncoin, jettons (USDT and others), and NFTs. +- **React**: hooks and ready-made UI components for common flows. +- **DeFi**: swaps, staking, on-ramp, and gasless transfers through registered providers. + +## What it owns + +A single `AppKit` instance holds configured **networks** (mainnet, testnet, and the API client used to read each), registered **connectors** that route signing requests to wallets, **connected wallets** with one wallet marked as selected, and **managers** for swap, staking, and streaming flows. Anything that reads or changes wallet state should go through this instance. + +## Configuration notes + +Create the `AppKit` instance once with an `AppKitConfig`. All fields are optional; pass only the configuration your app needs. See [Get started → Add wallet connectors](/ecosystem/appkit/get-started/connectors) for the constructor walkthrough. + + + If `networks` is omitted entirely, AppKit constructs a **mainnet-only** `ApiClientToncenter` with no API key. Fine for quick prototyping; expect rate limits on real workloads. Testnet apps must declare `networks` explicitly. + + +## Lifecycle + +```text +new AppKit(config) → connect → connector:connected → wallets:updated → (auto-select) wallets:selection-changed +``` + +## Action shapes + +AppKit exposes its capabilities as standalone functions instead of instance methods. Every action takes the `AppKit` instance as its first argument, which keeps the API easy to test and tree-shake. + +```ts +import { getSelectedWallet, transferTon } from '@ton/appkit'; + +const wallet = getSelectedWallet(appKit); +if (wallet) { + await transferTon(appKit, { recipientAddress: 'EQ...', amount: '1.25' }); +} +``` + +Actions use three patterns. **Queries** read state synchronously or with a one-shot async call (`getSelectedWallet`, `getBalance`). **Watchers** subscribe to changes and return an unsubscribe function (`watchSelectedWallet`, `watchBalance`). **Mutations** trigger side effects and return a promise (`connect`, `transferTon`, `sendTransaction`). + +Read actions that target the selected wallet (`getBalance`, `getJettons`, `getNfts`, …) return **`null`** when no wallet is selected. Mutation and signing actions (`transferTon`, `sendTransaction`, `signText`, …) **throw** `Error('Wallet not connected')` in the same case. Check for a selected wallet — or be ready for the throw — before calling a mutation. + +## React integration + +`@ton/appkit-react` provides hooks for the main AppKit actions and watchers. Mount `AppKitProvider` once at the root inside a TanStack `QueryClientProvider`, then call hooks anywhere below it. See [Installation → Wrap the application](/ecosystem/appkit/get-started/installation/react-app#wrap-the-application) for the full mount. + +Most async data hooks return TanStack Query results such as `{ data, isLoading, error, refetch }`, and mutation hooks return `{ mutate, mutateAsync, isPending, error }`. Watch hooks such as `useWatchBalance` return `void` and keep the related query cache fresh while mounted. Wallet state hooks read AppKit's local store directly: `useSelectedWallet()` returns `[wallet, setWalletId]`, while `useConnectedWallets()` and `useConnectors()` return arrays. + +## Tips + +- Create one AppKit instance for one app runtime. Do not share an instance across windows or processes. +- Pass the instance to actions directly, or provide it to React with `AppKitProvider`. Do not call actions before the instance is constructed. +- Connector `id`s are singletons. Registering a second connector with the same `id` calls `destroy()` on the old one before installing the new. +- Swap and staking provider `providerId`s are singletons too, but only the registry entry is replaced — the previous provider is not torn down. Hold the reference if you need to dispose of it yourself. +- Do not call signing or transfer mutations without first checking that a wallet is selected. + +## Related pages + +- [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet) +- [Providers](/ecosystem/appkit/howto/providers) +- [Send Toncoin](/ecosystem/appkit/howto/send-toncoin) +- [Sign data](/ecosystem/appkit/howto/sign-data) diff --git a/ecosystem/appkit/howto/connect-to-a-wallet.mdx b/ecosystem/appkit/howto/connect-to-a-wallet.mdx new file mode 100644 index 000000000..dac25d708 --- /dev/null +++ b/ecosystem/appkit/howto/connect-to-a-wallet.mdx @@ -0,0 +1,176 @@ +--- +title: "Connect to a wallet" +sidebarTitle: "Connect to a wallet" +--- + +Open a wallet session, restore it across reloads, and disconnect cleanly. + +## How it works + +A wallet is the signing application that owns the keys. A connector is the transport AppKit uses to open the wallet flow, restore sessions, list connected wallets, and route signing requests. A connector is not a wallet and does not sign — signing always happens inside the user's wallet application. + +AppKit can track multiple connected wallets, but one wallet is selected for actions such as transfers and data signing. A selected wallet can change after disconnects, network changes, or session restore, so read it from AppKit state instead of storing it as long-lived application state. The same wallet on a different network has a different wallet id, and AppKit treats those as separate wallet objects. + +## Before you begin + +You need an `AppKit` instance with at least one connector and the React providers mounted. See [Add wallet connectors](/ecosystem/appkit/get-started/connectors) for the instance and [Installation → Wrap the application](/ecosystem/appkit/get-started/installation/react-app#wrap-the-application) for the provider setup. + +## Drop-in button + +`` from `@ton/appkit-react` opens the TON Connect modal, manages the session, and renders the connected address. This is the recommended path for most apps. + +```tsx +import { TonConnectButton } from '@ton/appkit-react'; + +export function Header() { + return ; +} +``` + +## Custom connector picker + +When the app needs a tailored UI, render the connector list directly. `useConnectors()` returns an array of registered connectors, and `useConnect()` accepts a `connectorId`. + +```tsx +import { useConnect, useConnectors, useDisconnect, useSelectedWallet } from '@ton/appkit-react'; + +export function ConnectPanel() { + const connectors = useConnectors(); + const [wallet] = useSelectedWallet(); + const connect = useConnect(); + const disconnect = useDisconnect(); + + if (wallet) { + return ( + + ); + } + + return connectors.map((connector) => ( + + )); +} +``` + +If the `connectorId` passed to `connect` is not registered, the action throws `Connector with id "" not found`. Confirm registration with `getConnectors` (or `useConnectors` in React) before calling `connect`. + +## Read the connected wallet + +`useSelectedWallet` returns the wallet used by the next action. The wallet exposes `getAddress()` and `getNetwork()`. + +```tsx +import { useSelectedWallet } from '@ton/appkit-react'; + +const [wallet] = useSelectedWallet(); +const address = wallet?.getAddress(); +const network = wallet?.getNetwork(); +``` + +For just the address string, `useAddress` is the convenience hook: + +```tsx +import { useAddress } from '@ton/appkit-react'; + +const address = useAddress(); // string | undefined +``` + +## Register a connector after construction + +If connector configuration is not available at construction time, register it with `addConnector`. Use this for user-scoped manifest URLs, feature flags, or dynamic wallet sets. `addConnector` accepts a `Connector` instance or a factory and returns an unregister function. + +```ts +import { addConnector, createTonConnectConnector } from '@ton/appkit'; +import { appKit } from './appkit'; + +const unregister = addConnector( + appKit, + createTonConnectConnector({ + tonConnectOptions: { manifestUrl: 'https://example.com/tonconnect-manifest.json' }, + }), +); + +// Later, to remove and destroy the connector: +unregister(); +``` + +Connector ids are singletons. Registering a second connector with the same id calls `destroy()` on the previous one before installing the new. + +## Connect from vanilla JS + +```ts +import { connect, disconnect, getConnectors } from '@ton/appkit'; +import { appKit } from './appkit'; + +const [connector] = getConnectors(appKit); +await connect(appKit, { connectorId: connector.id }); + +// Later: +await disconnect(appKit, { connectorId: connector.id }); +``` + +`connector.connectWallet()` and `connector.disconnectWallet()` are the lower-level equivalents when direct connector access is preferred. + +## Wallet and Connector interfaces + +Every connected wallet exposes the same minimum surface: a reference to the connector that produced it, four identity readers, and two wallet-approved operations that are routed through the connector. + +```ts +interface WalletInterface { + /** Connector that created this wallet */ + readonly connectorId: string; + + // Identity + getAddress(): UserFriendlyAddress; + getPublicKey(): Hex; + getNetwork(): Network; + getWalletId(): string; + + // Actions requiring wallet signature + sendTransaction(request: TransactionRequest): Promise; + signData(payload: SignDataRequest): Promise; +} +``` + +Connectors implement a small interface that AppKit treats as opaque transport — the protocol underneath is hidden. + +```ts +interface Connector { + readonly id: string; + readonly type: string; + readonly metadata: ConnectorMetadata; + destroy(): void; + connectWallet(network?: Network): Promise; + disconnectWallet(): Promise; + getConnectedWallets(): WalletInterface[]; +} +``` + +The bundled `createTonConnectConnector` covers most apps. It wraps `@tonconnect/ui`, restores prior sessions when the connector is first read, and syncs TON Connect with AppKit's default network. + +## Errors and recovery + +Wrap every action in try/catch and convert errors with `getErrorMessage` from `@ton/appkit`. + +| Error | Recovery | +| ----------------- | -------------------------------------------------- | +| User rejected | Keep state unchanged and allow retry. | +| Missing connector | Check the AppKit configuration. | +| Expired session | Disconnect and request a new connection. | +| Wrong network | Prompt the user to switch network in their wallet. | + +## Tips + +- Do not call mutations or signing actions while `getSelectedWallet` is `null` — the action will throw. +- Do not assume every connector supports every flow. Catch rejections from the wallet and surface a recovery option. +- Do not store `selectedWalletId` as long-lived application state. The selected wallet can change after disconnects, network changes, or session restores. +- In application code, use public actions or React hooks instead of calling connector methods directly, so AppKit's event flow stays consistent. Connector methods are still the public surface for custom connector implementations. + +## Related pages + +- [AppKit](/ecosystem/appkit/howto/appkit) +- [`@ton/appkit-react` reference](/ecosystem/appkit/reference/appkit-react) +- [Use UI widgets](/ecosystem/appkit/howto/use-ui-widgets) diff --git a/ecosystem/appkit/howto/gasless.mdx b/ecosystem/appkit/howto/gasless.mdx new file mode 100644 index 000000000..cde1a4937 --- /dev/null +++ b/ecosystem/appkit/howto/gasless.mdx @@ -0,0 +1,55 @@ +--- +title: "Gasless" +sidebarTitle: "Gasless" +--- + +Gasless in AppKit lets the selected wallet sign a transaction while a relayer broadcasts it and charges fees in an accepted jetton instead of TON. + +## Relayer model + +Gasless does not remove the wallet step. The selected wallet still reviews and signs the message set, and the relayer only takes over after the wallet signature exists. + +The `GaslessManager` coordinates a gasless provider. The provider describes which gas jettons it accepts, how it estimates fees, and which message shapes it is willing to relay. + +## Fee and signature flow + +The flow begins by reading relayer configuration, then estimating the fee for the chosen gas jetton. The estimate gives the app two things to present together: the fee charged by the relayer and the message set the wallet must sign. + +After signing, the app sends the signed payload to the relayer. The relayer broadcasts the transaction on behalf of the wallet and charges its fee according to its own rules. + +```ts +import { estimateGasless, getGaslessConfig, sendGaslessTransaction } from '@ton/appkit'; + +const config = await getGaslessConfig(appKit, { network }); +const gasJetton = config.gasJettons[0]; + +const estimate = await estimateGasless(appKit, { + network, + gasJetton, + messages, +}); + +const response = await sendGaslessTransaction(appKit, { + estimate, +}); +``` + +Show the chosen gas jetton and estimated fee before opening the wallet. The wallet signs the message set; the relayer handles broadcast after that signature exists. + +## Failure boundaries + +Gasless introduces a relayer failure class in addition to wallet and chain failures. The wallet can reject signing, the relayer can reject the signed request, and the chain can still reject or fail the broadcast transaction. + +Those outcomes need different recovery paths. A relayer rejection may require a different gas jetton or payload, while a chain failure requires normal transaction-status handling. + +## Tips + +- Show the fee in the chosen gas jetton before opening the wallet. +- Treat relayer rejection and chain rejection as different outcomes. +- Verify the final on-chain result after the relayer accepts the signed request. + +## Related pages + +- [Providers](/ecosystem/appkit/howto/providers) +- [Send Toncoin](/ecosystem/appkit/howto/send-toncoin) +- [How to](/ecosystem/appkit/howto) diff --git a/ecosystem/appkit/howto/networks.mdx b/ecosystem/appkit/howto/networks.mdx new file mode 100644 index 000000000..1264727a7 --- /dev/null +++ b/ecosystem/appkit/howto/networks.mdx @@ -0,0 +1,92 @@ +--- +title: "Networks" +sidebarTitle: "Networks" +--- + +A network in AppKit is the chain context AppKit uses to read data, submit requests, and compare wallet state against the app's expected chain. + +## Configured networks + +AppKit can be configured with more than one network at the same time. Each network is identified by a `Network` value with a `chainId`, and each configured chain has an API client behind it. + +The API client is the app's source for chain reads: balances, transactions, block height, asset metadata, and request submission. AppKit includes Toncenter and TonAPI clients, and a pre-built client can be supplied when an app needs its own transport or authentication policy. + +At least one network must be configured. If a flow tries to read from a chain that the AppKit instance does not know about, the app has no reliable source of truth for that chain. + +## Default network + +Some flows are explicitly scoped to a network. Others need a fallback when the caller does not pass one. The default network is that fallback. + +Changing the default network changes the chain AppKit uses for reads and actions that do not specify a network. Connectors observe this change so wallet-facing flows can compare the selected wallet's network with the app's current expectation. + +## Configure networks + +Each network entry takes an `apiClient` — either the inline `{ url, key }` shape that targets TON Center, or a constructed `ApiClient` instance (such as `ApiClientTonApi`) for any other backend. The inline shape has preset URLs for mainnet and testnet, so `url` can be omitted for those. + +### Configure multiple networks + +Add more entries to `networks` to declare additional chains, including custom networks beyond mainnet and testnet. The example below configures mainnet and testnet through TON Center, plus a custom chain through a TonAPI client instance with its own endpoint URL. + +```ts +const customNetwork = Network.custom('custom_chain_id'); + +const appKit = new AppKit({ + networks: { + [Network.mainnet().chainId]: { + apiClient: { + key: 'your-mainnet-key', + }, + }, + [Network.testnet().chainId]: { + apiClient: { + url: 'https://testnet.toncenter.com', + key: 'your-testnet-key', + }, + }, + [customNetwork.chainId]: { + apiClient: new ApiClientTonApi({ + network: customNetwork, + endpoint: 'https://your-custom-endpoint.example', + apiKey: 'your-custom-key', + }), + }, + }, +}); +``` + +### Set and read the default network + +Set the default network with `setDefaultNetwork`: + +```ts +import { setDefaultNetwork } from '@ton/appkit'; + +setDefaultNetwork(appKit, { network: Network.testnet() }); +``` + +Use `getDefaultNetwork` or the React `useDefaultNetwork` hook when UI needs to show the active chain. `useDefaultNetwork` returns a `[network, setNetwork]` tuple — the value is `Network | undefined`, and the setter updates the default network reactively. + +```tsx +import { useDefaultNetwork } from '@ton/appkit-react'; + +const [network] = useDefaultNetwork(); +return {network?.chainId}; +``` + +## Network mismatch + +A network mismatch happens when the app expects one chain and the selected wallet or provider reports another. This is not a display issue — it changes where a transaction request would be signed and where a result would settle. + +Block unsafe actions when the expected network and the reported network do not match. Surface the expected network and let the wallet or app flow resolve the mismatch before sending a transaction request. + +## Tips + +- Develop on testnet first. +- Never derive product state from a network the AppKit instance is not configured to read. +- Block unsafe actions when the selected wallet network does not match the expected network. + +## Related pages + +- [Providers](/ecosystem/appkit/howto/providers) +- [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet) +- [How to](/ecosystem/appkit/howto) diff --git a/ecosystem/appkit/howto/nfts.mdx b/ecosystem/appkit/howto/nfts.mdx new file mode 100644 index 000000000..36eac515d --- /dev/null +++ b/ecosystem/appkit/howto/nfts.mdx @@ -0,0 +1,236 @@ +--- +title: "NFTs" +sidebarTitle: "NFTs" +--- + +Read, render, and transfer NFTs owned by the connected wallet. + + + **Verify NFT authenticity.** Scammers create fake NFTs that mimic popular collections. An NFT item contract can claim any collection address, so reading the `collection` field from the item alone is not sufficient. To verify that an NFT item genuinely belongs to a collection, query the collection contract with the item's index and check that the returned address matches the item's address. See [how to verify an NFT item](/standard/tokens/nft/verify) for the full procedure. + + +## How it works + +NFTs are unique digital assets on TON, similar to ERC-721 tokens on Ethereum. Unlike jettons, which have a balance, a wallet either owns a specific NFT item or it does not. NFTs consist of a collection contract and individual NFT item contracts; each item carries an `ownerAddress` that reflects current ownership. + +AppKit reads NFT ownership and metadata through the configured API client. NFT names, images, attributes, and collection fields are display data from contracts and indexers, so render them defensively. + +Transfers are wallet-approved transaction requests. The wallet accepting the request starts the chain flow, while ownership verification confirms that the NFT moved. + +## Before you begin + +A connected wallet and the React provider mounted. See [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet). + +## Read NFTs + +```tsx +import { useNfts } from '@ton/appkit-react'; + +export function MyNfts() { + const { data, isLoading, isError, refetch } = useNfts({ + query: { refetchInterval: 10000 }, + }); + + if (isError) return ; + if (isLoading) return Loading…; + + const nfts = data?.nfts ?? []; + return

{nfts.length} NFT(s)

; +} +``` + +For a different address, use `useNftsByAddress({ address })`. + +## Render with `` + +`@ton/appkit-react` ships an `NftItem` component that renders the NFT card with image, name, and collection. + +```tsx +import { NftItem, useNfts } from '@ton/appkit-react'; + +const { data } = useNfts(); +const nfts = data?.nfts ?? []; + +return ( +
+ {nfts.map((nft) => ( + console.log(nft)} /> + ))} +
+); +``` + +## Transfer an NFT + + + **Assets at risk.** Transferring an NFT is irreversible — once sent, only the new owner can transfer it back. Verify both the NFT address and the recipient address before initiating a transfer. A common failure mode is **stale ownership**: the NFT changes owners between the read and the transfer attempt. Reread `ownerAddress` before opening the wallet. If `isOnSale` is true, also check `realOwnerAddress`. + + +Before opening the wallet, make sure the sender's wallet has enough Toncoin to cover the [fees](/foundations/fees). + +An NFT transfer carries the destination owner address, the NFT contract address, and an optional small Toncoin amount that is forwarded to the recipient message. For NFTs the recommended pattern is to build the request offline with `createTransferNftTransaction`, then hand it to ``. This keeps the transaction shape testable. + +```tsx +import { Send, useAppKit } from '@ton/appkit-react'; +import { createTransferNftTransaction, getErrorMessage } from '@ton/appkit'; +import { useCallback } from 'react'; + +export function TransferNft({ nftAddress, recipientAddress }: { nftAddress: string; recipientAddress: string }) { + const appKit = useAppKit(); + + const request = useCallback( + () => createTransferNftTransaction(appKit, { nftAddress, recipientAddress }), + [appKit, nftAddress, recipientAddress], + ); + + return ( + console.log('done')} + onError={(e) => console.error(getErrorMessage(e))} + > + {({ isLoading, onSubmit, disabled, text }) => ( + + )} + + ); +} +``` + +The shorter route is the `useTransferNft` hook or the `transferNft` core action — both build the request internally and call the wallet directly. + +```ts +import { transferNft } from '@ton/appkit'; + +await transferNft(appKit, { + nftAddress: 'EQ...', + recipientAddress: 'EQ...', +}); +``` + +## Read a single NFT + +```ts +import { getNft } from '@ton/appkit'; + +const nft = await getNft(appKit, { address: 'EQ...' }); +``` + +## Continuous ownership monitoring + +A discrete ownership check is fine for assembling an outgoing transfer, but UI state should not be derived from it — ownership can change between read and render. There is no `watchNfts` streaming action; to keep an NFT view in sync, mount a query hook with a refetch interval, or run a polling loop from vanilla code. + +```tsx +import { useNfts } from '@ton/appkit-react'; + +const { data, isLoading, error, refetch } = useNfts({ + limit: 100, + query: { refetchInterval: 10_000 }, +}); +``` + +From vanilla JS: + +```ts +import { type AppKit, type NFT, getNfts } from '@ton/appkit'; + +/** Start polling. Returns a stop function. */ +export function startNftOwnershipMonitoring( + appKit: AppKit, + onNftsUpdate: (nfts: NFT[]) => void, + intervalMs: number = 10_000, +): () => void { + let isRunning = true; + + const poll = async () => { + while (isRunning) { + const result = await getNfts(appKit, { limit: 100 }); + onNftsUpdate(result?.nfts ?? []); + await new Promise((resolve) => setTimeout(resolve, intervalMs)); + } + }; + + poll(); + return () => { isRunning = false; }; +} +``` + +Pick an interval based on UX needs — shorter intervals provide fresher data but increase API usage. For large wallets, call `getNfts` with increasing `offset` to paginate. + +## `NFT` type + +NFT-related queries produce objects that conform to the following interface: + +```ts title="TypeScript" +/** + * Non-fungible token (NFT) on the TON blockchain. + */ +export interface NFT { + /** Contract address of the NFT item */ + address: string; + /** Index of the item within its collection */ + index?: string; + /** Display information about the NFT (name, description, images, etc.) */ + info?: TokenInfo; + /** Custom attributes/traits of the NFT (e.g., rarity, properties) */ + attributes?: NFTAttribute[]; + /** Information about the collection this item belongs to */ + collection?: NFTCollection; + /** Address of the auction contract, if the NFT is being auctioned */ + auctionContractAddress?: string; + /** Hash of the NFT smart contract code */ + codeHash?: string; + /** Hash of the NFT's on-chain data */ + dataHash?: string; + /** Whether the NFT contract has been initialized */ + isInited?: boolean; + /** Whether the NFT is soulbound (non-transferable) */ + isSoulbound?: boolean; + /** Whether the NFT is currently listed for sale */ + isOnSale?: boolean; + /** Current owner address of the NFT */ + ownerAddress?: string; + /** Real owner address when NFT is on sale (sale contract becomes temporary owner) */ + realOwnerAddress?: string; + /** Address of the sale contract, if the NFT is listed for sale */ + saleContractAddress?: string; + /** Off-chain metadata of the NFT (key-value pairs) */ + extra?: { [key: string]: unknown }; +} +``` + +NFT display data (`info`, `attributes`, `extra`) is off-chain and untrusted. The contract address is the authoritative routing key. When `isOnSale` is `true`, `ownerAddress` points to the sale contract and `realOwnerAddress` points to the seller. Render the seller, but route on-chain interactions through `ownerAddress`. + +## Confirm settlement + +The wallet accepting the request does not prove the NFT moved. Track the transaction with [Streaming](/ecosystem/appkit/howto/streaming) and verify ownership before crediting value. + +## Common failures + +| Failure | Meaning | +| --------------- | ------------------------------------------------------------------------------------------------------------------------ | +| User rejected | The user closed or rejected the wallet request. | +| Stale ownership | The NFT changed owners between the read and the transfer. Reread `ownerAddress` before sending. | +| NFT on sale | `isOnSale === true` — the NFT is held by a sale contract. Cancel the listing or transfer through the sale contract flow. | +| Soulbound NFT | `isSoulbound === true` — the item cannot be transferred. | +| Invalid address | The recipient or NFT contract address is malformed or for the wrong network. | + +## Tips + +- Verify the current `ownerAddress` before allowing a user-initiated transfer. NFTs can change owners between reads and transfers. +- Display **verified** fields first: contract address, owner address, real owner address, and collection address. Treat off-chain display data (`info`, `attributes`, `extra`) as **display data only**. Sanitize names and images, and never route on display metadata. +- Use `getTransactionStatus` to confirm settlement before updating NFT state in your app. The wallet response only proves the user signed. +- Refetch the NFT list after settlement so the owner fields are current. +- Do not concatenate NFT addresses for analytics keys. Use the contract address verbatim and `index` as a string when present. + +## Code example + +See a [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) of an NFT list rendered with `useNfts` — [try it live](https://appkit-minter.vercel.app/). + +## Related pages + +- [Use UI widgets](/ecosystem/appkit/howto/use-ui-widgets) +- [AppKit reference](/ecosystem/appkit/reference) +- [Send Toncoin](/ecosystem/appkit/howto/send-toncoin) diff --git a/ecosystem/appkit/howto/onramp.mdx b/ecosystem/appkit/howto/onramp.mdx new file mode 100644 index 000000000..ace5b2ff3 --- /dev/null +++ b/ecosystem/appkit/howto/onramp.mdx @@ -0,0 +1,68 @@ +--- +title: "Onramp" +sidebarTitle: "Onramp" +--- + +Onramp in AppKit covers external flows that bring value into a TON wallet through fiat payment providers or crypto bridge aggregators. + +## Two external flows + +| Flow | What starts in AppKit | What finishes outside AppKit | +| ------------- | ----------------------------------------------------- | -------------------------------------------------------------------------- | +| Fiat onramp | A provider quote and hosted payment URL. | Fiat payment, KYC, provider settlement, and delivery to the TON address. | +| Crypto onramp | A bridge quote and source-chain deposit instructions. | Source-chain deposit, bridge processing, status updates, and TON delivery. | + +Both flows use the same manager and provider idea as other AppKit integrations. The manager gives the app one surface to ask for quotes; the provider owns payment pages, bridge routes, fees, limits, and completion status. + +## Fiat onramp + +A fiat onramp sends the user to a hosted provider page. AppKit can collect quotes and produce the URL for Mercuryo, MoonPay, TON Pay, or another registered provider, but it does not process card payments or perform KYC. + +The redirect is the beginning of an external process. The selected wallet address may be the destination, but wallet state alone is not the completion source for the provider flow. + +```ts +import { buildOnrampUrl, getOnrampQuote } from '@ton/appkit'; + +const quote = await getOnrampQuote(appKit, { + fiatCurrency: 'USD', + cryptoCurrency: 'TON', + fiatAmount: '100', + destinationAddress: wallet.getAddress().toString(), +}); + +const url = await buildOnrampUrl(appKit, { quote }); +window.location.assign(url); +``` + +## Crypto onramp + +A crypto onramp starts with bridge intent: a source chain, a source asset, and a TON destination. AppKit asks the provider for a quote, creates a deposit, and exposes the chain-specific instructions needed to fund it. + +Deposit addresses, memos, source-chain confirmations, bridge timing, and refunds are provider concerns. AppKit surfaces status so the app can track progress without pretending to move funds itself. + +```ts +import { createCryptoOnrampDeposit, getCryptoOnrampQuote, getCryptoOnrampStatus } from '@ton/appkit'; + +const quote = await getCryptoOnrampQuote(appKit, { + fromChain: 'ethereum', + fromToken: 'USDT', + toToken: 'TON', + amount: '50', + destinationAddress: wallet.getAddress().toString(), +}); + +const deposit = await createCryptoOnrampDeposit(appKit, { quote }); +const status = await getCryptoOnrampStatus(appKit, { depositId: deposit.id }); +``` + +## Tips + +- Treat a hosted URL or deposit instruction as the start of a long-running provider process. +- Verify completion through provider status, not wallet state alone. +- Surface provider fees, limits, and timing as provider data rather than guaranteed outcomes. + +## Related pages + +- [Providers](/ecosystem/appkit/howto/providers) +- [Networks](/ecosystem/appkit/howto/networks) +- [How to](/ecosystem/appkit/howto) diff --git a/ecosystem/appkit/howto/providers.mdx b/ecosystem/appkit/howto/providers.mdx new file mode 100644 index 000000000..dee7da641 --- /dev/null +++ b/ecosystem/appkit/howto/providers.mdx @@ -0,0 +1,107 @@ +--- +title: "Providers" +sidebarTitle: "Providers" +--- + +import { Aside } from '/snippets/aside.jsx'; + +Providers connect AppKit to external data sources and DeFi services. AppKit uses four provider-related components: the **API client** that reads chain state, the **streaming provider** that delivers live updates, the **swap provider** that quotes and builds DEX transactions, and the **staking provider** that quotes and builds staking transactions. Each component is managed by the matching manager on the `AppKit` instance. + +A provider is not a wallet and not a connector. The wallet still signs every transaction; the connector still owns the session. A provider is the source of _data_ about the chain or about a DeFi protocol, and AppKit treats whatever it returns as external input — useful for showing prices, balances, and quotes, but never as proof of settlement. + +| Type | Use | Manager | +| ------------------ | -------------------------------------------------------------------- | ------------------------- | +| API client | Reads blockchain data such as balances, jettons, and NFTs. | `AppKit.networkManager` | +| Streaming provider | Pushes live balance, transaction, and jetton updates over WebSocket. | `AppKit.streamingManager` | +| Swap provider | Returns swap quotes and builds swap transactions. | `AppKit.swapManager` | +| Staking provider | Returns staking quotes, balance, and builds stake transactions. | `AppKit.stakingManager` | + + + +## Swaps + +A swap provider integrates a DEX or aggregator. It returns a **quote** for a route (input asset, output asset, amount, fees, and execution assumptions), then builds a `TransactionRequest` when the user accepts the quote. AppKit does not price markets itself; `SwapManager` coordinates the registered providers. Bundled options include Omniston and DeDust. See [Swaps](/ecosystem/appkit/howto/swaps) for the full quote-then-build flow. + +## Staking + +A staking provider quotes a stake or unstake intent for a specific pool or protocol, then builds the matching `TransactionRequest`. + +Liquid-staking positions may appear in the wallet as derivative balances, such as tsTON. AppKit exposes pool metadata and APY as provider-supplied **display data**, not a guarantee of future yield or redemption timing. + +Unstake mechanics — instant, delayed, or round-end — are provider-specific. The bundled staking provider is Tonstakers. See [Staking](/ecosystem/appkit/howto/staking) for the full flow. + +## How they are registered + +Provider setup uses three registration paths. **API clients** are bound to a network through the `networks` field of `AppKitConfig`. **Streaming providers** are also network-specific and are registered directly on `appKit.streamingManager`. **Swap** and **staking** providers are network-aware DeFi providers: pass them in `providers` at construction or register them later with `registerProvider`. Each DeFi provider declares its `type` so AppKit can route it to the correct manager. + +Omniston requires its own SDK package — install it before registering the provider: + +```bash +npm i @ston-fi/omniston-sdk +``` + +```ts +import { AppKit, Network, registerProvider } from '@ton/appkit'; +import { OmnistonSwapProvider } from '@ton/appkit/swap/omniston'; +import { createTonstakersProvider } from '@ton/appkit/staking/tonstakers'; + +const appKit = new AppKit({ + networks: { + [Network.mainnet().chainId]: { + apiClient: { url: 'https://toncenter.com', key: 'your-mainnet-api-key' }, + }, + [Network.testnet().chainId]: { + apiClient: { url: 'https://testnet.toncenter.com', key: 'your-testnet-api-key' }, + }, + }, + providers: [new OmnistonSwapProvider()], +}); + +registerProvider(appKit, createTonstakersProvider()); +``` + +When a network entry does not provide a custom API client, AppKit creates a TonCenter client for that network. TonAPI and TonCenter streaming providers are available through WalletKit and can be registered on `appKit.streamingManager`. Omniston and DeDust are available as swap providers, and Tonstakers is available as a staking provider. Custom DeFi backends can use the same `registerProvider` path by implementing the swap or staking interface. + +`registerProvider` returns `void`. To replace a provider, call `registerProvider` again with the same `providerId` — the registry entry is overwritten. If the previous provider owns resources you need to clean up, hold the reference and dispose of it yourself before re-registering. + +## Two-step transactions + +Swap and staking actions follow a quote-then-build pattern. `getSwapQuote` and `getStakingQuote` return provider offers, including fees, settlement amounts, and routing data. `buildSwapTransaction` and `buildStakeTransaction` turn an accepted quote into a `TransactionRequest`. Building the request does not broadcast it; the request still has to be submitted with `sendTransaction` and approved in the connected wallet. + +```ts +import { AppKit, getSwapQuote, buildSwapTransaction, sendTransaction } from '@ton/appkit'; + +const quote = await getSwapQuote(appKit, { + amount: '1000000000', + from: { address: 'EQ-USDT-MASTER', decimals: 6 }, + to: { address: 'ton', decimals: 9 }, + network, +}); +const request = await buildSwapTransaction(appKit, { + quote, + userAddress: wallet.getAddress().toString(), +}); +await sendTransaction(appKit, request); +``` + +This separation matters because quotes can expire. The settlement amount shown when the quote is fetched is not guaranteed when the transaction lands on chain. Refetch the quote before building the transaction, and verify the on-chain result before updating product state. + +## Custom providers + +Apps that integrate their own DEX or staking backend implement the same provider interface as the bundled providers, then register the provider with `registerProvider`. Provider ids are singletons; registering another provider with the same `providerId` replaces the previous entry. + +## Tips + +- Provider responses are external data. Validate critical outcomes, including settlement amounts, addresses, and signatures, before changing product state. +- Quotes can expire. Build the transaction soon after fetching the quote, or refetch before showing the user a final price. +- Do not assume a provider is registered. Call `getStakingProviders(appKit)` or `hasStreamingProvider(appKit, network)` and degrade gracefully when the list is empty or the provider is absent. +- Treat provider ids as singletons. Registering a second provider with the same id replaces the previous one. + +## Related pages + +- [AppKit](/ecosystem/appkit/howto/appkit) +- [Swaps](/ecosystem/appkit/howto/swaps) +- [Staking](/ecosystem/appkit/howto/staking) +- [Streaming](/ecosystem/appkit/howto/streaming) diff --git a/ecosystem/appkit/howto/read-balances.mdx b/ecosystem/appkit/howto/read-balances.mdx new file mode 100644 index 000000000..a4fc8307d --- /dev/null +++ b/ecosystem/appkit/howto/read-balances.mdx @@ -0,0 +1,100 @@ +--- +title: "Read balances" +sidebarTitle: "Read balances" +--- + +Read Toncoin and jetton balances for the connected wallet or any address. + +## How it works + +Balance reads come from the configured API client for the active network. Selected-wallet hooks are convenient for wallet UI, while address-explicit hooks are better for receipts, profiles, admin views, and backend-confirmed destinations. + +Live balance changes are streaming signals. They are useful for refreshing UI, but product decisions should still rely on a point-in-time read from the network the app is configured to trust. + +## Before you begin + +A connected wallet (or a known address) and the React provider mounted. See [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet). + +## Pick the right hook + +| Goal | Hook | +| --------------------------------------- | --------------------- | +| Toncoin balance for the selected wallet | `useBalance` | +| Toncoin balance for an explicit address | `useBalanceByAddress` | +| Jetton balances for the selected wallet | `useJettons` | +| Jetton balances for an explicit address | `useJettonsByAddress` | + +All four are query hooks. They return `{ data, isLoading, error, refetch }` and accept a `query` field to forward TanStack Query options such as `refetchInterval`. + +## Read the connected wallet + +```tsx +import { useBalance, useJettons } from '@ton/appkit-react'; + +export function Balances() { + const ton = useBalance({ query: { refetchInterval: 20000 } }); + const jettons = useJettons({ query: { refetchInterval: 20000 } }); + + if (ton.isLoading || jettons.isLoading) return Loading…; + if (ton.isError) return Failed to load TON; + + return ( +
+

TON: {ton.data ?? '0'}

+

Jettons: {jettons.data?.jettons.length ?? 0}

+
+ ); +} +``` + +`ton.data` is a string in TON units. Render it directly, or use `toNano` only when another API expects raw nanotons. + +Jetton reads return formatted balances in the token's own units. Use the token metadata for symbols and decimals when rendering a list. + +## Read by address + +Use the `*ByAddress` hooks to read any address — useful for receipt screens or admin tooling. + +```tsx +import { useBalanceByAddress, useJettonsByAddress } from '@ton/appkit-react'; + +export function ProfileBalances({ address }: { address: string }) { + const ton = useBalanceByAddress({ address }); + const jettons = useJettonsByAddress({ address }); + return

TON: {ton.data ?? '0'}

; +} +``` + +## Render a jetton row + +The SDK ships `getFormattedJettonInfo` to normalize jetton metadata for display. + +```tsx +import { getFormattedJettonInfo } from '@ton/appkit'; + +const formatted = getFormattedJettonInfo(jetton); +// formatted: { name, symbol, decimals, balance, image, address } +``` + +## Read from vanilla JS + +```ts +import { getBalanceByAddress, getJettonsByAddress } from '@ton/appkit'; + +const ton = await getBalanceByAddress(appKit, { address: 'EQ...' }); +const jettons = await getJettonsByAddress(appKit, { address: 'EQ...' }); +``` + +## Live updates + +Mount `useWatchBalance()` and `useWatchJettons()` once high in the tree to keep the cache pushed live. See [Stream live updates](/ecosystem/appkit/get-started/streaming). + +## Code example + +See a [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) of TON and jetton balances rendered together with the same hooks documented above — [try it live](https://appkit-minter.vercel.app/). + +## Related pages + +- [Streaming](/ecosystem/appkit/howto/streaming) +- [AppKit reference](/ecosystem/appkit/reference) +- [Send Toncoin](/ecosystem/appkit/howto/send-toncoin) diff --git a/ecosystem/appkit/howto/send-jettons.mdx b/ecosystem/appkit/howto/send-jettons.mdx new file mode 100644 index 000000000..cff8d555a --- /dev/null +++ b/ecosystem/appkit/howto/send-jettons.mdx @@ -0,0 +1,137 @@ +--- +title: "Send jettons" +sidebarTitle: "Send jettons" +--- + +Transfer a jetton (a TON fungible token) from the connected wallet to another address. + +## 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 TON 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. + + + To deploy and mint a new jetton on the mainnet without writing code, use the dedicated official tool: [TON MINTER](https://minter.ton.org). + + +## Before you begin + +A connected wallet, the React provider mounted, and the jetton's master address. See [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet) and [Read balances](/ecosystem/appkit/howto/read-balances) to discover what the wallet holds. + + + **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. + + +Before opening the wallet, make sure the sender's wallet has enough Toncoin to cover the [fees](/foundations/fees). + +## Component (recommended) + +`` accepts a `jetton` descriptor with `address`, `symbol`, and `decimals`. + +```tsx +import { SendJettonButton } from '@ton/appkit-react'; +import { getErrorMessage } from '@ton/appkit'; + + console.error(getErrorMessage(e))} + onSuccess={({ boc }) => console.log('sent', boc)} +> + {({ isLoading, onSubmit, disabled, text }) => ( + + )} + +``` + +`amount` is a decimal string in jetton units (so `'1.0'` for one full token, regardless of `decimals`). + +## Hook + +```tsx +import { useTransferJetton } from '@ton/appkit-react'; + +const transferJetton = useTransferJetton(); + +transferJetton.mutate({ + recipientAddress: 'EQ...', + jettonAddress: 'EQ...', + amount: '1.0', + jettonDecimals: 6, +}); +``` + +## Core action + +```ts +import { transferJetton } from '@ton/appkit'; + +await transferJetton(appKit, { + recipientAddress: 'EQ...', + jettonAddress: 'EQ...', + amount: '1.0', + jettonDecimals: 6, +}); +``` + +## 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 + +| 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 + +Jetton transfers settle through the same trace API as Toncoin 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](/ecosystem/appkit/howto/streaming) 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-TON allowance on a built `transferJetton` request is designed to cover this case. + +## 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 TON fee allowance. | +| Invalid address | The recipient or jetton master address is malformed or for the wrong network. | + +## 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 + +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 + +- [Read balances](/ecosystem/appkit/howto/read-balances) +- [Send Toncoin](/ecosystem/appkit/howto/send-toncoin) +- [Use UI widgets](/ecosystem/appkit/howto/use-ui-widgets) diff --git a/ecosystem/appkit/howto/send-toncoin.mdx b/ecosystem/appkit/howto/send-toncoin.mdx new file mode 100644 index 000000000..b8841ba17 --- /dev/null +++ b/ecosystem/appkit/howto/send-toncoin.mdx @@ -0,0 +1,125 @@ +--- +title: "Send Toncoin" +sidebarTitle: "Send Toncoin" +--- + +Transfer Toncoin from the connected wallet. Pick the call style that fits your UI. + +## How it works + +Toncoin is the native asset on TON. AppKit keeps Toncoin amounts as decimal strings in human-readable TON units and converts them when it builds the wallet request. Precision is 9 digits — `'0.1'` is 100,000,000 nanoToncoin; the smallest representable amount is `'0.000000001'` (1 nanoToncoin). + +The flow has three steps: AppKit assembles the request, the wallet signs and broadcasts, and the chain settles. A wallet response means the selected wallet accepted and submitted the request — it does not prove that the transaction settled on chain, so product state should wait for status tracking or a backend verification read. + +## Before you begin + +A connected wallet and the React provider mounted. See [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet). Use testnet during development. + +## Component (recommended) + +`` builds the request, calls the wallet, and exposes the mutation state to a render-prop child. + +```tsx +import { SendTonButton } from '@ton/appkit-react'; +import { getErrorMessage } from '@ton/appkit'; +import { useState } from 'react'; + +export function SendTon() { + const [error, setError] = useState(null); + + return ( + setError(getErrorMessage(e))} + onSuccess={({ boc }) => console.log('sent', boc)} + > + {({ isLoading, onSubmit, disabled, text }) => ( + + )} + + ); +} +``` + +`amount` is a decimal string in TON. The component accepts an optional `comment` for a memo on chain. + +## Hook + +`useTransferTon` is the mutation hook behind the component. + +```tsx +import { useTransferTon } from '@ton/appkit-react'; + +const transferTon = useTransferTon(); + +transferTon.mutate({ + recipientAddress: 'EQ...', + amount: '0.05', + comment: 'Test payment', +}); +``` + +Read `transferTon.isPending`, `transferTon.error`, and use `mutation.onSuccess` / `mutation.onError` if you initialize the hook with options. + +## Core action + +```ts +import { transferTon } from '@ton/appkit'; +import { appKit } from './appkit'; + +await transferTon(appKit, { + recipientAddress: 'EQ...', + amount: '0.05', + comment: 'Test payment', +}); +``` + +## Parameters + +| Field | Type | Required | Notes | +| ------------------ | -------- | -------- | --------------------------------------------------- | +| `recipientAddress` | `string` | Yes | TON address. | +| `amount` | `string` | Yes | Decimal TON, e.g. `'0.05'`. | +| `comment` | `string` | No | Plain text memo. Mutually exclusive with `payload`. | +| `payload` | `string` | No | Base64 cell. Overrides `comment` when set. | +| `stateInit` | `string` | No | Base64 cell, used to deploy. | + +## Confirm settlement + +TON achieves transaction [finality](https://docs.ton.org/payments/overview#transaction-finality) after a single masterchain block confirmation, with new blocks produced roughly every 400 ms. Once a transaction appears in a masterchain block, it becomes irreversible. + +`sendTransaction` returning a `boc` means **the user signed**. Settlement is a separate state — verify with `getTransactionStatus(appKit, { boc })`, which returns `'unknown' | 'pending' | 'completed' | 'failed'` along with `totalMessages`, `pendingMessages`, and `onchainMessages` for trace progress. Treat only `status === 'completed'` as final. + +Applications should not block the UI while waiting for confirmation. Show a "signed / confirming" state, let [Streaming](/ecosystem/appkit/howto/streaming) push live updates, and act on `completed` when it arrives. For finding a transaction by external message hash and applying normalization, see the [message lookup guide](/ecosystem/ton-connect/message-lookup). + +## Common failures + +| Failure | Meaning | +| -------------------- | ------------------------------------------------------------ | +| User rejected | The user closed or rejected the wallet request. | +| Insufficient balance | The wallet cannot pay amount and fees. | +| Expired request | The transaction deadline passed before approval. | +| Invalid address | The recipient address is malformed or for the wrong network. | + +## Tips + +- Validate the recipient address, amount, and expected network before calling any mutation. Do not rely on the wallet as the final input validator. +- Treat `sendTransaction` returning a `boc` as "user signed", not "settled". Wait for `getTransactionStatus` to return `completed` before delivering value. +- Do not block UI while waiting for settlement. Show a "signed / confirming" state, let `watchBalance` / `watchTransactions` update the view, and act on `completed` when it arrives. +- Refetch wallet balances and cached lists after settlement. Use trace status, not the immediate wallet response, as the trigger for product state changes. +- Do not derive product success from the wallet response shape. Verify on the chain. +- Keep amounts as strings or bigints until you display them. Floating-point math loses precision at TON's nine decimal places. + +## Code example + +See a [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) of TON transfers built with these mutations — [try it live](https://appkit-minter.vercel.app/). + +## Related pages + +- [Send jettons](/ecosystem/appkit/howto/send-jettons) +- [NFTs](/ecosystem/appkit/howto/nfts) +- [Use UI widgets](/ecosystem/appkit/howto/use-ui-widgets) diff --git a/ecosystem/appkit/howto/sign-data.mdx b/ecosystem/appkit/howto/sign-data.mdx new file mode 100644 index 000000000..12ba67c69 --- /dev/null +++ b/ecosystem/appkit/howto/sign-data.mdx @@ -0,0 +1,83 @@ +--- +title: "Sign data" +sidebarTitle: "Sign data" +--- + +Ask the wallet to sign a payload. AppKit supports plain text, raw binary, and TON cells. + +## How it works + +Signed data attests to application data without sending a transaction. It fits user-facing acknowledgments, off-chain receipts, and signatures that a backend or TON smart contract verifies later. + +The wallet returns an Ed25519 signature, signing address, wallet timestamp, observed dApp domain, and the original payload. The verifier owns freshness, domain, nonce, and signer checks. For wallet-ownership sign-in, use `ton_proof` during connection instead. + +## Before you begin + +You need a connected wallet and the React provider mounted. See [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet). + +## Pick the right call + +| Payload type | Hook | Core action | +| --------------- | --------------- | ------------ | +| Plain text | `useSignText` | `signText` | +| Binary (base64) | `useSignBinary` | `signBinary` | +| TON cell (BoC) | `useSignCell` | `signCell` | + +All three hooks are mutation hooks. They return `{ mutate, mutateAsync, isPending, error, data }`. + +## Sign text + +```tsx +import { useSignText } from '@ton/appkit-react'; + +const { mutate: signText, data } = useSignText(); + +signText({ + text: 'Message to sign', +}); + +// data: { signature, address, timestamp, domain, payload } +``` + +## Sign binary + +```tsx +import { useSignBinary } from '@ton/appkit-react'; + +const { mutate: signBinary, data } = useSignBinary(); + +signBinary({ + bytes: 'base64-encoded-payload', +}); + +// data: { signature, address, timestamp, domain, payload } +``` + +## Sign a cell + +For application protocols that exchange typed payloads, send a TON cell. + +```tsx +import { useSignCell } from '@ton/appkit-react'; +import { beginCell } from '@ton/core'; + +const { mutate: signCell, data } = useSignCell(); + +const cell = beginCell().storeUint(42, 32).storeStringTail('hello').endCell(); + +signCell({ + cell: cell.toBoc().toString('base64'), + schema: 'message#_ value:uint32 text:Text = Message;', +}); + +// data: { signature, address, timestamp, domain, payload } +``` + +## Verify the signature + +The wallet returns a signature response that includes `signature`, `address`, `timestamp`, `domain`, and the original `payload`. Verify the response server side against the payload and the wallet's public key before trusting it. Treat the client-side response as a hint, not proof. + +## Related pages + +- [AppKit reference](/ecosystem/appkit/reference) +- [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet) diff --git a/ecosystem/appkit/howto/staking.mdx b/ecosystem/appkit/howto/staking.mdx new file mode 100644 index 000000000..21c07a444 --- /dev/null +++ b/ecosystem/appkit/howto/staking.mdx @@ -0,0 +1,149 @@ +--- +title: "Staking" +sidebarTitle: "Staking" +--- + +Quote a stake or unstake, build the transaction, and send it through the connected wallet. + +## How it works + +The `StakingManager` routes each quote and build call to a single registered staking provider — by default the first one you registered, or the one you pass as `providerId`. `setDefaultProvider` on the manager overrides the default; an unknown `providerId` throws. A quote describes the intent (`direction: 'stake' | 'unstake'`, `amount`, optional `unstakeMode`), and `useBuildStakeTransaction` turns an accepted quote into a `TransactionRequest`. + +The protocol shape — derivative jetton, unstake modes, settlement timing, pool model — is provider-specific. Tonstakers, the bundled provider, issues `tsTON` and supports `INSTANT`, `WHEN_AVAILABLE`, and `ROUND_END` unstake modes. Read the user's staked-token balance with `useStakedBalance`, or query the jetton balance directly. `apy` and `instantUnstakeAvailable` from `useStakingProviderInfo` are provider-supplied display data, not guarantees of future yield or withdrawal timing. + +## Before you begin + +You need a connected wallet and a staking provider registered on the AppKit instance. Tonstakers ships bundled — see [Providers → How they are registered](/ecosystem/appkit/howto/providers#how-they-are-registered). + +## Hooks + +| Hook | Purpose | +| -------------------------- | -------------------------------------------------------------------- | +| `useStakingProviders` | List registered staking provider IDs. | +| `useStakingProviderInfo` | Read APY and instant-unstake liquidity for a provider. | +| `useStakedBalance` | Read the user's staked-token balance (e.g. `tsTON`). | +| `useStakingQuote` | Quote a stake or unstake intent. | +| `useBuildStakeTransaction` | Build a `TransactionRequest` from a quote. | + +The first three are query hooks (`{ data, isLoading, isError, refetch }`); the last two are mutation/query pair used together. The built `TransactionRequest` is handed to `` from `@ton/appkit-react`, which exposes loading/error state. + +## Read the user's staking balance + +`useStakedBalance` returns `StakingBalance` for the given user address. The shape is `{ stakedBalance, rawStakedBalance, instantUnstakeAvailable, rawInstantUnstakeAvailable, providerId }`. Render `stakedBalance` as the user's tsTON amount and `instantUnstakeAvailable` as the pool's liquid TON. + +```tsx +import { useAddress, useStakedBalance } from '@ton/appkit-react'; + +export function StakedBalance() { + const address = useAddress(); + const { data, isLoading, isError } = useStakedBalance({ + userAddress: address ?? '', + query: { refetchInterval: 10000, enabled: Boolean(address) }, + }); + + if (isLoading) return ; + if (isError) return ; + + return ( +
+

Staked: {data?.stakedBalance ?? '0'} tsTON

+

+ Instant unstake liquidity:{' '} + {data?.instantUnstakeAvailable ?? '0'} TON +

+
+ ); +} +``` + +## Quote a stake or unstake + +`useStakingQuote` takes the intent and returns settlement amounts. For an unstake, pass `unstakeMode` (`UnstakeMode.INSTANT`, `UnstakeMode.WHEN_AVAILABLE`, or `UnstakeMode.ROUND_END`) when the provider supports more than one. + +```tsx +import { UnstakeMode, useNetwork, useStakingQuote } from '@ton/appkit-react'; + +export function StakePreview({ amount }: { amount: string }) { + const network = useNetwork(); + const { data: quote, isLoading, isError } = useStakingQuote({ + amount, + direction: 'stake', + network, + }); + + if (isLoading) return Fetching quote…; + if (isError || !quote) return Staking unavailable; + + const rate = Number(quote.amountOut) / Number(quote.amountIn); + + return ( +
+

+ Stake {quote.amountIn} TON → receive {quote.amountOut} tsTON +

+

Rate: 1 TON ≈ {rate.toFixed(4)} tsTON

+
+ ); +} +``` + +For an unstake, swap `direction` and add `unstakeMode`: + +```tsx +const { data: quote } = useStakingQuote({ + amount, + direction: 'unstake', + unstakeMode: UnstakeMode.INSTANT, + network, +}); +``` + +## Build and send the transaction + +`useBuildStakeTransaction` is the mutation that turns an accepted quote into a `TransactionRequest`. Hand it to `` through a `request` callback. + +```tsx +import { Send, useAddress, useBuildStakeTransaction } from '@ton/appkit-react'; +import type { StakingQuote } from '@ton/appkit'; + +export function StakeSendButton({ + quote, +}: { + quote: StakingQuote | undefined; +}) { + const address = useAddress(); + const { mutateAsync: buildStakeTransaction } = useBuildStakeTransaction(); + + const request = async () => { + if (!quote || !address) { + throw new Error('Missing quote or address'); + } + return buildStakeTransaction({ quote, userAddress: address }); + }; + + return ( + console.log('sent', boc)} + /> + ); +} +``` + +## After the send + +After the send completes, pass the response to `getTransactionStatus` to confirm settlement, then refetch `useStakedBalance` to see the new staked-token balance. The response carries either a `boc` or a `normalizedHash`; pass whichever is set. + +## Tips + +- The user's stake is a derivative jetton (e.g. `tsTON`), not an opaque AppKit object. Refresh with `useStakedBalance` (or a jetton balance read) after sends. +- APY and instant-unstake liquidity from `useStakingProviderInfo` are provider-supplied display data, not guarantees of future yield or withdrawal timing. +- Unstake mechanics (instant, delayed, round-end) are provider-specific. Pass `unstakeMode` on the quote when the provider supports more than one. + +## Related pages + +- [Providers](/ecosystem/appkit/howto/providers#staking) +- [Read balances](/ecosystem/appkit/howto/read-balances) +- [How to](/ecosystem/appkit/howto) diff --git a/ecosystem/appkit/howto/streaming.mdx b/ecosystem/appkit/howto/streaming.mdx new file mode 100644 index 000000000..66c16eb3a --- /dev/null +++ b/ecosystem/appkit/howto/streaming.mdx @@ -0,0 +1,173 @@ +--- +title: "Streaming" +sidebarTitle: "Streaming" +--- + +Streaming in AppKit is a per-network subscription layer that turns provider events into live balance, jetton, and transaction updates for the app. + +## Live provider signals + +Streaming complements point-in-time API reads. Instead of asking an API client for the latest state after every interaction, the app subscribes to changes for an address on a specific network. + +The `StreamingManager` coordinates providers by `chainId`. A streaming provider implements one transport for one network, such as a Toncenter or TonAPI websocket stream. Registering another provider for the same network replaces the previous stream for that chain. Provider registration is covered on [Providers → How they are registered](/ecosystem/appkit/howto/providers#how-they-are-registered). + +## Subscription lifetime + +A subscription belongs to the consumer that created it. Balance, jetton, transaction, and combined watches all return an unsubscribe function, and that function is the app's handle for stopping updates when a component unmounts or a flow ends. + +When the underlying transport drops and reconnects, the SDK does not replay events from the gap. Updates resume from the provider's current view of the chain, so any intermediate transitions on a watched address are lost. + +## Before you begin + +You need a connected wallet (for the default-address `useWatch*` hooks) and a streaming provider registered on the AppKit instance. See [Providers → How they are registered](/ecosystem/appkit/howto/providers#how-they-are-registered). + +## Watch from React + +Mount watch hooks near the part of the tree that needs live data. The hook keeps related query caches fresh while it is mounted; passing `onChange` adds a side-effect callback on top of the cache refresh. Every React watch hook accepts an optional `{ onChange?, network? }`, and the `*ByAddress` variants additionally accept an `address`. + +Mount as many watchers as you need. The SDK deduplicates subscribers by network, address, and type, so multiple hook instances watching the same data share one underlying socket subscription. There is no benefit to lifting watchers into a single root component just to share them — pull them into the component that actually needs the live value. + +### Watch a balance + +`useWatchBalance` refreshes the cache that `useBalance` reads. `useBalance` is a TanStack Query result (`{ data, isLoading, isError, refetch, ... }`); `data` is the formatted TON balance as a string when present. + +```tsx +import { useBalance, useWatchBalance } from '@ton/appkit-react'; + +export function LiveBalance() { + useWatchBalance(); + const balance = useBalance(); + + return {balance.data ?? '0'} TON; +} +``` + +### Watch transactions + +`useWatchTransactions` reacts to transaction lifecycle changes. The update payload exposes `traceHash` and `status` (`'pending' | 'confirmed' | 'finalized' | 'invalidated'`). Plug in your own notification or state-update logic; the SDK does not pick a UI primitive for you. + +```tsx +import { useWatchTransactions } from '@ton/appkit-react'; + +export function FinalizedLog() { + useWatchTransactions({ + onChange: (update) => { + if (update.status === 'finalized' && update.traceHash) { + console.log('finalized', update.traceHash); + } + }, + }); + return null; +} +``` + +### Watch by address + +For receipt screens, admin tooling, or order-tracking widgets, subscribe to an address that may not be the connected wallet. The `*ByAddress` hooks take an `address` and keep the matching `useBalanceByAddress` / cache fresh while also delivering each update to `onChange`. + +```tsx +import { + useBalanceByAddress, + useWatchBalanceByAddress, + useWatchTransactionsByAddress, +} from '@ton/appkit-react'; + +export function OrderTracker({ orderAddress }: { orderAddress: string }) { + useWatchBalanceByAddress({ + address: orderAddress, + onChange: (update) => { + if (update.status === 'finalized') { + console.log('order balance', update.balance); + } + }, + }); + const balance = useBalanceByAddress({ address: orderAddress }); + + useWatchTransactionsByAddress({ + address: orderAddress, + onChange: (update) => { + if (update.status === 'finalized') { + console.log('finalized txs', update.transactions.length); + } + }, + }); + + return {balance.data ?? '0'} TON; +} +``` + +### Watch jettons + +`useWatchJettons` pushes jetton balance changes for the connected wallet. Each update covers one jetton (`masterAddress`, `walletAddress`, `ownerAddress`, `rawBalance`) and may include `decimals` and a formatted `balance` when the jetton's decimals are known. + +```tsx +import { useWatchJettons } from '@ton/appkit-react'; + +export function PortfolioWatcher() { + useWatchJettons({ + onChange: (update) => { + if (update.status === 'finalized' && update.balance !== undefined) { + console.log(update.masterAddress, update.balance); + } + }, + }); + return null; +} +``` + +## From vanilla code + +A non-React consumer keeps the unsubscribe function returned by each `watch*` action and calls it when the screen or process no longer needs updates. + +```ts +import { watchBalance } from '@ton/appkit'; + +const unsubscribe = watchBalance(appKit, { + onChange: (update) => { + console.log(update.address, update.balance); + }, +}); + +unsubscribe(); +``` + +The same shape applies to `watchBalanceByAddress`, `watchJettons`, `watchJettonsByAddress`, `watchTransactions`, and `watchTransactionsByAddress`. Each takes `{ network?, onChange }` (and `address` on the `*ByAddress` variants) and returns an unsubscribe function. + +## Connection state + +Connection state describes transport health, not chain finality. A websocket can disconnect and reconnect while the underlying account state continues to change. + +After reconnect, updates may resume from the provider's current view of the chain. A flow that needs certainty should refresh from the configured API client before it changes product state. + +`appKit.streamingManager.onConnectionChange(network, callback)` registers a listener for the WebSocket connection state of one network. The callback receives `connected: boolean`; the method returns an unsubscribe function. Pair it with `useNetwork` so the listener follows the currently selected network. + +```tsx +import { useEffect, useState } from 'react'; +import { useAppKit, useNetwork } from '@ton/appkit-react'; + +export function StreamingHealth() { + const appKit = useAppKit(); + const network = useNetwork(); + const [connected, setConnected] = useState(true); + + useEffect(() => { + if (!network) return; + return appKit.streamingManager.onConnectionChange(network, setConnected); + }, [appKit, network]); + + return {connected ? 'live' : 'reconnecting'}; +} +``` + +## Tips + +- Store every unsubscribe function and call it when the consumer goes away. +- Call `hasStreamingProvider` before subscribing on a network that may not have a provider registered, and degrade gracefully when it returns `false`. +- Treat streaming updates as best-effort UI refresh signals. Reconcile with a point-in-time read before changing product state that depends on correctness. +- `update.status` carries one of `'pending' | 'confirmed' | 'finalized' | 'invalidated'` on every streaming payload. Only `'finalized'` is irreversible; treat the others as intermediate. + +## Related pages + +- [Providers → How they are registered](/ecosystem/appkit/howto/providers#how-they-are-registered) +- [Networks](/ecosystem/appkit/howto/networks) +- [Read balances](/ecosystem/appkit/howto/read-balances) diff --git a/ecosystem/appkit/howto/swaps.mdx b/ecosystem/appkit/howto/swaps.mdx new file mode 100644 index 000000000..fc7e7d214 --- /dev/null +++ b/ecosystem/appkit/howto/swaps.mdx @@ -0,0 +1,107 @@ +--- +title: "Swaps" +sidebarTitle: "Swaps" +--- + +Quote a swap, build the transaction, and send it through the connected wallet. + +## How it works + +The `SwapManager` routes each quote and build call to a single registered swap provider — by default the first one you registered, or the one you pass as `providerId`. `setDefaultProvider` on the manager overrides the default; an unknown `providerId` throws. A quote is the chosen provider's current offer for a route — input asset, output asset, amount, fees, and execution assumptions — and `useBuildSwapTransaction` turns an accepted quote into a `TransactionRequest`. AppKit does not price markets itself; the provider does. + +The quote is provider-supplied data and can expire. Refresh it close to the moment the user confirms, and treat the displayed `toAmount` as an estimate, not a guarantee. The wallet still owns approval, signing, and rejection on the final send. + +## Before you begin + +You need a connected wallet and at least one swap provider registered on the AppKit instance. Omniston and DeDust ship bundled — see [Providers → How they are registered](/ecosystem/appkit/howto/providers#how-they-are-registered). + +## Hooks + +| Hook | Purpose | +| ------------------------- | ---------------------------------------------------------------- | +| `useSwapQuote` | Fetch a swap quote. | +| `useBuildSwapTransaction` | Build a `TransactionRequest` from a quote. | + +`useSwapQuote` is a query hook (`{ data, isLoading, isError, refetch }`); `useBuildSwapTransaction` is a mutation hook (`{ mutate, mutateAsync, isPending, error }`). Pass the built `TransactionRequest` to `` from `@ton/appkit-react`, which exposes loading/error state. + +## Quote a swap + +`useSwapQuote` takes the asset pair and amount and returns the provider's current offer. Pass `network`, `slippageBps`, and an optional `providerId` to pin a specific provider. + +```tsx +import { useNetwork, useSwapQuote } from '@ton/appkit-react'; + +const TON = { address: 'ton', decimals: 9, symbol: 'TON' }; +const USDT = { + address: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs', + decimals: 6, + symbol: 'USDT', +}; + +export function SwapPreview({ amount }: { amount: string }) { + const network = useNetwork(); + const { data: quote, isLoading, isError } = useSwapQuote({ + amount, + from: USDT, + to: TON, + network, + slippageBps: 100, + }); + + if (isLoading) return Fetching quote…; + if (isError || !quote) return Swap unavailable; + + return ( + + {quote.fromAmount} {USDT.symbol} → {quote.toAmount} {TON.symbol} + + ); +} +``` + +## Build and send the transaction + +`useBuildSwapTransaction` is the mutation that turns an accepted quote into a `TransactionRequest`. Wrap it in a `request` callback and hand it to ``; the component exposes loading/error state. + +```tsx +import { Send, useAddress, useBuildSwapTransaction } from '@ton/appkit-react'; +import type { SwapQuote } from '@ton/appkit'; + +export function SwapSendButton({ quote }: { quote: SwapQuote | undefined }) { + const address = useAddress(); + const { mutateAsync: buildSwapTransaction } = useBuildSwapTransaction(); + + const request = async () => { + if (!quote || !address) { + throw new Error('Missing quote or address'); + } + return buildSwapTransaction({ quote, userAddress: address }); + }; + + return ( + console.log('sent', boc)} + /> + ); +} +``` + +## After the send + +After the send completes, pass the response to `getTransactionStatus` to confirm settlement before updating balances, orders, or inventory. The response carries either a `boc` or a `normalizedHash`; pass whichever is set. + +## Tips + +- Refresh a quote before building if the user paused on the confirmation screen — the route can move while the modal is open. +- Provider responses are external data; treat `toAmount` as an estimate, not a guarantee. +- `minReceived` is the slippage-protected lower bound on the output after `slippageBps` is applied. Compare against your product floor before letting the user sign. +- Slippage protection is provider-specific. `slippageBps` caps price drift; pass provider-specific fields through `providerOptions` when a provider needs more. + +## Related pages + +- [Providers](/ecosystem/appkit/howto/providers#swaps) +- [Send Toncoin](/ecosystem/appkit/howto/send-toncoin) +- [How to](/ecosystem/appkit/howto) diff --git a/ecosystem/appkit/howto/use-ui-widgets.mdx b/ecosystem/appkit/howto/use-ui-widgets.mdx new file mode 100644 index 000000000..067a0a5fe --- /dev/null +++ b/ecosystem/appkit/howto/use-ui-widgets.mdx @@ -0,0 +1,304 @@ +--- +title: "Use UI widgets" +sidebarTitle: "Use UI widgets" +--- + +`@ton/appkit-react` exports ready-to-use components for the most common AppKit flows. Drop them into your tree and connect callbacks. The default styles ship in `@ton/appkit-react/styles.css`. + +## How it works + +Widgets are React vertical flows over the same AppKit instance, selected wallet, network, and providers used by hooks and core actions. They are not a separate design system. + +Use the default UI when the built-in flow fits. Use render props when the widget should keep AppKit state and behavior but the app owns layout. Use hooks directly when the product flow no longer matches one component. + +## Before you begin + +You need the React providers mounted and the styles imported once at the entry file. See [Installation → Wrap the application](/ecosystem/appkit/get-started/installation/react-app#wrap-the-application). + +```tsx +import '@ton/appkit-react/styles.css'; +``` + +## Component catalog + +| Component | What it does | +| ------------------------- | --------------------------------------------------------------------------- | +| `` | Drop-in connect button. Opens the TON Connect modal and shows the address. | +| `` | Generic transaction submitter. Accepts a `request` factory. | +| `` | Toncoin transfer with `recipientAddress`, `amount`, `comment`. | +| `` | Jetton transfer. Takes a `jetton` descriptor plus the same transfer fields. | +| `` | Renders the lifecycle of a sent transaction. | +| `` | NFT card with image and metadata. | +| `` | Full swap UI: token select, amount, slippage, settings, submit. | +| `` | Full staking UI: stake/unstake tabs, amount, APY and exchange-rate readout. | +| `` | TON logo glyph as an inline SVG. | +| `` | Same glyph framed in a circle background. | + +## Connect button + +```tsx +import { TonConnectButton } from '@ton/appkit-react'; + +; +``` + +See [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet) for the custom-selector alternative. + +## Send Toncoin + +```tsx +import { SendTonButton } from '@ton/appkit-react'; + +; +``` + +The default rendering shows `Send {amount} TON`. Pass a `children` render-prop with `{ isLoading, onSubmit, disabled, text }` if you want to drive a custom button — the component still owns the request, the call to the wallet, and the loading/error state. + +
+ SendTonButton in Storybook + + SendTonButton in Storybook +
+ +## Send a jetton + +`` mirrors `` but takes a `jetton` descriptor with the master address, ticker, and decimals so it knows which jetton wallet to call. + +```tsx +import { SendJettonButton } from '@ton/appkit-react'; + +; +``` + +`jetton.address`, `jetton.symbol`, and `jetton.decimals` are all required. The default rendering shows `Send {amount} {jetton.symbol}`; pass a `children` render-prop if you want to drive a custom button. + +
+ SendJettonButton in Storybook + + SendJettonButton in Storybook +
+ +## Send a prepared transaction + +`` accepts a `request` function that returns a `TransactionRequest`. Use it for transactions you build yourself (offline builders, swap quotes, NFT transfers). Set `text` to label the default button. + +```tsx +import { Send, useAppKit } from '@ton/appkit-react'; +import { createTransferNftTransaction } from '@ton/appkit'; + +const appKit = useAppKit(); + +const request = async () => + createTransferNftTransaction(appKit, { + nftAddress: 'EQ...', + recipientAddress: 'EQ...', + }); + + console.log('sent', boc)} +/>; +``` + +Pass a `children` render-prop with `{ isLoading, onSubmit, disabled, text }` instead of (or alongside) `text` if you want full control over the button markup. + +
+ Send in Storybook + + Send in Storybook +
+ +## Render an NFT + +```tsx +import { NftItem, useNfts } from '@ton/appkit-react'; + +const { data } = useNfts(); +const nfts = data?.nfts ?? []; + +
+ {nfts.map((nft) => ( + /* open detail */ undefined} + /> + ))} +
; +``` + +## Swap widget + +`` is the complete swap UI: pay/receive fields, token selector, flip button, slippage settings, and the submit button. Pass the list of tokens the user can pick between, optionally seed the default pair, and the widget handles quote fetching, building, and the wallet call internally. + +```tsx +import { SwapWidget } from '@ton/appkit-react'; +import { Network } from '@ton/appkit'; +import type { AppkitUIToken } from '@ton/appkit-react'; + +const TOKENS: AppkitUIToken[] = [ + { + symbol: 'TON', + name: 'Toncoin', + decimals: 9, + address: 'ton', + network: Network.mainnet(), + }, + { + symbol: 'USDT', + name: 'Tether USD', + decimals: 6, + address: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs', + network: Network.mainnet(), + }, +]; + +; +``` + +The required prop is `tokens`. `defaultFromSymbol`, `defaultToSymbol`, `defaultSlippage` (basis points, default `100` = 1%), `fiatSymbol` (default `'$'`), and `network` (defaults to the connected wallet's network) are all optional. Pass a `children` render-prop to replace the default UI while keeping the widget's internal state — the function receives `{ fromToken, toToken, fromAmount, toAmount, isQuoteLoading, canSubmit, setFromAmount, onFlip, ... }`. + +
+ SwapWidget in Storybook + + SwapWidget in Storybook +
+ +## Staking widget + +`` is the complete staking UI: stake/unstake tabs, amount input, balance readout, current APY, exchange rate, and provider. The widget reads the registered staking provider (Tonstakers ships bundled — see [Providers → How they are registered](/ecosystem/appkit/howto/providers#how-they-are-registered)) and handles quote, build, and the wallet call internally. + +```tsx +import { StakingWidget } from '@ton/appkit-react'; +import { Network } from '@ton/appkit'; + +; + +// Or pin a specific network: +; +``` + +`network` is the only prop; pass it to override the connected wallet's network. Pass a `children` render-prop for a custom UI; the function receives `{ amount, quote, isQuoteLoading, canSubmit, setAmount, direction, setDirection, unstakeMode, setUnstakeMode, ... }` to drive your own controls against the widget's internal state. + +
+StakingWidget in Storybook +StakingWidget in Storybook +
+ +## Show transaction progress + +After a send call resolves with a `boc`, hand it to `` to render the wallet → chain lifecycle UI. + +```tsx +import { TransactionProgress } from '@ton/appkit-react'; + +; +``` + +The component renders its own default UI. For full layout control, pass a `children` render prop. The child receives `{ status, totalMessages, pendingMessages, onchainMessages, isFetching, error, boc, texts }`. For lighter customization, pass `classNames={{ container, icon, message }}` to restyle internal parts without owning the markup. + +## TON glyphs + +`` is an inline SVG of the TON diamond; it follows the surrounding `color` (so it tints with `currentColor`). `` is the same glyph framed in a circular background. Both accept an optional `size` (defaults to `16`). + +```tsx +import { TonIcon, TonIconCircle } from '@ton/appkit-react'; + + + +``` + +## Common props + +The send components share a callback shape: + +| Prop | Type | Notes | +| ----------- | -------------------------------------------------- | -------------------------------------------------------------- | +| `onSuccess` | `(response: SendTransactionResponse) => void` | Response carries `boc`, `normalizedBoc`, and `normalizedHash`. | +| `onError` | `(error: Error) => void` | Plain `Error`. Pair with `getErrorMessage` for UX-safe text. | +| `disabled` | `boolean` | Disable the underlying button externally. | +| children | `(state: { isLoading, onSubmit, disabled, text })` | Render-prop API for full styling control. | +| `text` | `ReactNode` | Label rendered when no children render-prop is provided. | + +## Code example + +See a [working example](https://github.com/ton-connect/kit/tree/main/apps/appkit-minter) that integrates `` and `` — [try it live](https://appkit-minter.vercel.app/). + +## Related pages + +- [AppKit reference](/ecosystem/appkit/reference) +- [Connect to a wallet](/ecosystem/appkit/howto/connect-to-a-wallet) +- [Send Toncoin](/ecosystem/appkit/howto/send-toncoin) +- [NFTs](/ecosystem/appkit/howto/nfts) diff --git a/ecosystem/appkit/init.mdx b/ecosystem/appkit/init.mdx deleted file mode 100644 index d06b20f49..000000000 --- a/ecosystem/appkit/init.mdx +++ /dev/null @@ -1,677 +0,0 @@ ---- -title: "How to initialize the TON Connect's AppKit" -sidebarTitle: "Initialize the kit" ---- - -import { Aside } from '/snippets/aside.jsx'; - -If there is a dApp that uses [TON Connect UI libraries](#relation-to-ton-connect-libraries), migrate that dApp to AppKit in a few steps: - -- [Migrate from `@tonconnect/ui-react` (React)](#migrate-from-%40tonconnect%2Fui-react) -- [Migrate from `@tonconnect/ui` (Vanilla)](#migrate-from-%40tonconnect%2Fui) - -## Installation - -Before initializing the TON Connect's AppKit, install it with the necessary peer dependencies: - - - ```shell title="React" icon="react" - # AppKit React TanStack Query TON Connect Core packages # Buffer polyfill - npm i @ton/appkit-react @tanstack/react-query @tonconnect/ui-react @ton/core @ton/crypto buffer - ``` - - ```shell title="TypeScript" icon="globe" - # AppKit TanStack Query TON Connect Core packages # Buffer polyfill - npm i @ton/appkit @tanstack/query-core @tonconnect/ui @ton/core @ton/crypto buffer - ``` - - -Core packages require a `Buffer` [polyfill](https://en.wikipedia.org/wiki/Polyfill_\(programming\)). Extend the module bundle configuration according to the bundler in use: - - - ```ts title="Vite" - // vite.config.js - import { defineConfig } from 'vite'; - export default defineConfig({ - // ...prior options... - resolve: { - alias: { - buffer: 'buffer/', - }, - }, - define: { - Buffer: ['buffer', 'Buffer'], - }, - // ...later options... - }); - ``` - - ```ts title="Webpack" - // webpack.config.js - const webpack = require('webpack'); - module.exports = { - // ...prior options... - resolve: { - fallback: { - buffer: require.resolve('buffer/'), - } - }, - plugins: [ - new webpack.ProvidePlugin({ - Buffer: ['buffer', 'Buffer'], - }) - ], - // ...later options... - } - ``` - - -## Initialization - - - -The basic kit initialization does not require any configuration options. - - - ```tsx title="React" icon="react" - import { AppKit, AppKitProvider } from '@ton/appkit-react'; - import '@ton/appkit-react/styles.css'; - - const kit = new AppKit({}); - - // Wrap application in `AppKitProvider` and pass the `AppKit` instance. - export function App() { - return {/* ...app... */}; - }; - ``` - - ```ts title="TypeScript" icon="globe" - import { AppKit } from '@ton/appkit'; - - const kit = new AppKit({}); - ``` - - -To communicate with the blockchain, AppKit uses [TON Center APIs](/ecosystem/api/toncenter/introduction) by default. In particular, operations with assets require many API calls — [obtain a key to access higher limits](/ecosystem/api/toncenter/get-api-key) and prevent [frequent rate limit 429 errors](/ecosystem/api/toncenter/rate-limit). - -Alternative API clients and their configuration options can be supplied per network: - -```ts -// Or @ton/appkit-react for React -import { AppKit, Network } from '@ton/appkit'; - -const kit = new AppKit({ - // Configure networks and API clients. - networks: { - // Mainnet is TON's production network. - // All contracts and funds are real. - [Network.mainnet().chainId]: { - apiClient: { - // Most commonly used, official API client. - url: 'https://toncenter.com', - - // A key to access higher RPS limits. - // Get it from https://t.me/toncenter - key: '', - }, - }, - }, -}); -``` - -### Connectors - -The [basic setup](#initialization) allows for direct API requests but is insufficient for connecting TON wallets via the TON Connect protocol. For that, configure a connector and host a public [app manifest file](/ecosystem/ton-connect/manifest#app-manifest) adjusted to the needs of a given dApp. - -For local development and testing, use the [manifest file from this demo dApp](https://tonconnect-sdk-demo-dapp.vercel.app/tonconnect-manifest.json). - - - ```tsx title="React" icon="react" expandable - import { - AppKit, - AppKitProvider, - // Enables TON wallet connections - createTonConnectConnector, - } from '@ton/appkit-react'; - import '@ton/appkit-react/styles.css'; - - const kit = new AppKit({ - connectors: [ - createTonConnectConnector({ - tonConnectOptions: { - // Public link to the application manifest JSON file. - // For local development and testing, use the one from a demo dApp: - manifestUrl: 'https://tonconnect-sdk-demo-dapp.vercel.app/tonconnect-manifest.json', - }, - }), - ], - }); - - // Wrap application in `AppKitProvider` and pass the `AppKit` instance. - export function App() { - return {/* ...app... */}; - }; - ``` - - ```ts title="TypeScript" icon="globe" expandable - import { - AppKit, - // Enables TON wallet connections - createTonConnectConnector, - } from '@ton/appkit'; - - const kit = new AppKit({ - connectors: [ - createTonConnectConnector({ - tonConnectOptions: { - // Public link to the application manifest JSON file. - // For local development and testing, use the one from a demo dApp: - manifestUrl: 'https://tonconnect-sdk-demo-dapp.vercel.app/tonconnect-manifest.json', - }, - }), - ], - }); - ``` - - -### Providers - -The [setup with connectors](#connectors) connects to TON wallets via services such as [Tonkeeper](/ecosystem/wallet-apps/tonkeeper). To enable advanced DeFi operations like asset swaps or staking, configure DeFi providers. - -Supported swap providers: - -- [Omniston](https://ston.fi/omniston). Requires the `@ston-fi/omniston-sdk` package. -- [DeDust](https://dedust.io). Has no additional dependencies. - -Supported staking providers: - -- [Tonstakers](https://tonstakers.com) is the primary staking provider. - -AppKit uses the first registered swap provider by default. Pass `providerId` when requesting a quote to target a specific provider. - - - - Swaps with Omniston require its SDK to be installed: - - ```shell - npm i @ston-fi/omniston-sdk - ``` - - - - ```ts - // Or @ton/appkit-react for React - import { AppKit } from '@ton/appkit'; - - // Swap providers - import { OmnistonSwapProvider } from '@ton/appkit/swap/omniston'; - import { DeDustSwapProvider } from '@ton/appkit/swap/dedust'; - - // Tonstakers as a staking provider - import { createTonstakersProvider } from '@ton/appkit/staking/tonstakers'; - - const kit = new AppKit({ - // Providers setup - providers: [ - // AppKit uses the first registered swap provider by default. - new OmnistonSwapProvider({ - /* custom configuration options, none are required */ - }), - new DeDustSwapProvider({ - /* custom configuration options, none are required */ - }), - // Staking - createTonstakersProvider({ - /* custom configuration options, none are required */ - }), - ], - }); - ``` - - - -### Queries and mutations - -[TanStack Query](https://tanstack.com/query/latest) is an opinionated library that simplifies fetching, caching, synchronizing and updating server state in web applications. - -To enable [it for React](https://tanstack.com/query/latest/docs/framework/react/overview), wrap the application inside `AppKitProvider` in the `QueryClientProvider` from `@tanstack/react-query`: - -```tsx -// Additional imports -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; - -// Query client -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - // Do not refetch when window is refocused - refetchOnWindowFocus: false, - }, - }, -}); - -// ...AppKit initialization as shown prior... - -// Modified entrypoint -export function App() { - return ( - - - {/* ...app... */} - - - ); -} -``` - -### Complete setup - -The following initialization example sets up everything at once: - - - ```tsx title="React" icon="react" - // AppKit - import { - AppKit, - AppKitProvider, - Network, - // Wallet connector - createTonConnectConnector, - } from '@ton/appkit-react'; - - // Styles - import '@ton/appkit-react/styles.css'; - - // DeFi providers - import { OmnistonSwapProvider } from '@ton/appkit/swap/omniston'; - import { DeDustSwapProvider } from '@ton/appkit/swap/dedust'; - import { createTonstakersProvider } from '@ton/appkit/staking/tonstakers'; - - // TanStack Query - import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; - - // Query client - const queryClient = new QueryClient({ - defaultOptions: { - queries: { - // Do not refetch when window is refocused - refetchOnWindowFocus: false, - }, - }, - }); - - // Initialization - const kit = new AppKit({ - // 1. Configure networks and API clients. - networks: { - // Production network. All contracts and funds are real. - [Network.mainnet().chainId]: { - apiClient: { - // Most commonly used, official API client. - url: 'https://toncenter.com', - - // A key to access higher RPS limits. - // Get it from https://t.me/toncenter - key: '', - }, - }, - }, - // 2. Configure connectors. - connectors: [ - // Enables connections with TON wallet services, such as Tonkeeper or MyTonWallet. - createTonConnectConnector({ - tonConnectOptions: { - // Public link to the application manifest JSON file. - // For local development and testing, use the one from a demo dApp: - manifestUrl: 'https://tonconnect-sdk-demo-dapp.vercel.app/tonconnect-manifest.json', - }, - }), - ], - // 3. Configure providers. - providers: [ - // AppKit uses the first registered swap provider by default. - new OmnistonSwapProvider(), - new DeDustSwapProvider(), - // Staking - createTonstakersProvider(), - ], - }); - - // 4. Wrap the rest of the app in a QueryClientProvider and an AppKitProvider. - export function App() { - return ( - - - {/* ...app... */} - - - ) - }; - ``` - - ```ts title="TypeScript" icon="globe" - // AppKit - import { - AppKit, - Network, - // Wallet connector - createTonConnectConnector, - } from '@ton/appkit'; - - // DeFi providers - import { OmnistonSwapProvider } from '@ton/appkit/swap/omniston'; - import { DeDustSwapProvider } from '@ton/appkit/swap/dedust'; - import { createTonstakersProvider } from '@ton/appkit/staking/tonstakers'; - - // Initialization - const kit = new AppKit({ - // 1. Configure networks and API clients. - networks: { - // Production network. All contracts and funds are real. - [Network.mainnet().chainId]: { - apiClient: { - // Most commonly used, official API client. - url: 'https://toncenter.com', - - // A key to access higher RPS limits. - // Get it from https://t.me/toncenter - key: '', - }, - }, - }, - // 2. Configure connectors. - connectors: [ - // Enables connections with TON wallet services, such as Tonkeeper or MyTonWallet. - createTonConnectConnector({ - tonConnectOptions: { - // Public link to the application manifest JSON file. - // For local development and testing, use the one from a demo dApp: - manifestUrl: 'https://tonconnect-sdk-demo-dapp.vercel.app/tonconnect-manifest.json', - }, - }), - ], - // 3. Configure providers. - providers: [ - // AppKit uses the first registered swap provider by default. - new OmnistonSwapProvider(), - new DeDustSwapProvider(), - // Staking - createTonstakersProvider(), - ], - }); - ``` - - -## Dynamic configuration - -AppKit instance can be dynamically extended with new [connectors](#connectors) and [providers](#providers). - -### Add new connectors - -To add a new connector, call the `.addConnector()` method on the initialized AppKit object: - -```ts -kit.addConnector(connector); -``` - -### Add new providers - -To add a new provider, call the `.registerProvider()` method on the initialized AppKit object: - -```ts -kit.registerProvider(provider); -``` - -To specifically add a swap provider, register it via `kit.swapManager`: - -```ts -kit.swapManager.registerProvider(swapProvider); -``` - -## Configuration parameters - - - For one or more TON networks, configure their respective API or RPC providers to interact with. - - ```ts title="TypeScript" icon="globe" - // Or @ton/appkit-react for React - import { Network } from '@ton/appkit'; - - new AppKit({ - networks: { - // Production network. All contracts and funds are real. - [Network.mainnet().chainId]: { // "-239" - apiClient: { - // Most commonly used, official API client. - // - // To self-host, see: - // * Real-time API: https://github.com/toncenter/ton-http-api-cpp - // * Indexer: https://github.com/toncenter/ton-indexer - // It is important to put real-time API under `/api/v2` route and indexer API under `/api/v3` route. - url: 'https://toncenter.com', - - // Optional key to access higher RPS limits. - key: '', - }, - }, - // Testing network. For experiments, beta tests, and feature previews. - [Network.testnet().chainId]: { // "-3" - apiClient: { - url: 'https://testnet.toncenter.com', - key: '', - }, - }, - }, - // ...later fields... - }); - ``` - - It is also possible to provide an entirely custom provider with its own `ApiClient` interface implementation. - - ```ts title="TypeScript" icon="globe" - // Or @ton/appkit-react for React - import { Network } from '@ton/appkit'; - - new AppKit({ - networks: { - [Network.testnet().chainId]: { // "-3" - apiClient: /* A complete ApiClient interface implementation */, - }, - }, - // ...later fields... - }); - ``` - - - - Array of connectors that enable wallet connections. The primary connector is `TonConnectConnector`, which is created by dedicated function. - - Each connector must expose the following methods: - - - `initialize()` — creates the connector, restores connections, sets up event listeners. - - `destroy()` — cleans up connector resources. - - `connectWallet()` — connects a wallet through the UI. - - `disconnectWallet()` — disconnects a wallet. - - `getConnectedWallets()` — lists connected wallets. - - Additionally, each connector has metadata that can be displayed in the UI: - - ```ts - interface ConnectorMetadata { - /** - * Connector name - */ - name: string; - /** - * Connector icon URL - */ - iconUrl?: string; - } - ``` - - - - Array of DeFi or auxiliary providers that enable additional functionality, such as asset swaps via [Omniston](https://ston.fi/omniston). - - -## Relation to TON Connect libraries - -Lower-level TON Connect libraries, such as `@tonconnect/ui-react` and `@tonconnect/ui`, only give access to wallet services via the TON Connect protocol and basic facilities to query connected TON wallets with direct API calls. - -AppKit builds on the foundation provided by these libraries and extends functionality with convenient components, hooks, and functions. It enables balance tracking and asset management, gives ready-made React UI components for common cases, handles API-related state management, and even registers DeFi integrations for advanced use cases. - -If there is an existing dApp that uses either `@tonconnect/ui-react` or `@tonconnect/ui`, consider migrating to AppKit to simplify development and reduce boilerplate. - -### Migrate from `@tonconnect/ui-react` - -Before migrating, [install AppKit and peer packages](#installation) and add necessary polyfills. - - - - Ensure that TON Connect packages are of the latest version: - - ```shell - npm up @tonconnect/ui-react --save - ``` - - - - Use `AppKitProvider` in place of the `TonConnectUIProvider`: - - ```tsx - import { - AppKit, - // Replaces - AppKitProvider, - // Wallet connector - createTonConnectConnector, - } from '@ton/appkit-react'; - import '@ton/appkit-react/styles.css'; - - const kit = new AppKit({ - connectors: [ - createTonConnectConnector({ - // In place of props on the TonConnectUIProvider - tonConnectOptions: { - // Public link to the application manifest JSON file. - // For local development and testing, use the one from a demo dApp: - manifestUrl: 'https://tonconnect-sdk-demo-dapp.vercel.app/tonconnect-manifest.json', - }, - }), - ], - }); - - export function App() { - return ( - // In place of the TonConnectUIProvider - - {/* ...rest of the app... */} - - ); - } - ``` - - Refer to the [complete initialization setup](#complete-setup) for all the possible AppKit configuration options. - - - - The `AppKitProvider` bridges to TON Connect under the hood. Existing few `@tonconnect/ui-react` hooks, such as `useTonAddress()`, `useTonWallet()`, and others, will continue to work inside the `AppKitProvider` automatically. - - - -### Migrate from `@tonconnect/ui` - -Before migrating, [install AppKit and peer packages](#installation) and add necessary polyfills. - - - - ```shell - npm up @tonconnect/ui --save - ``` - - - - It is possible to reuse the existing `TonConnectUI` object when configuring AppKit [connectors](#connectors): - - ```ts - import { - AppKit, - createTonConnectConnector, - } from '@ton/appkit'; - - const kit = new AppKit({ - connectors: [ - createTonConnectConnector({ - // Pass the existing TonConnectUI instance object - tonConnectUI - }), - ] - }); - ``` - - One can also extend the existing AppKit instance dynamically: - - ```ts - // Passing the existing TonConnectUI instance object - kit.addConnector(createTonConnectConnector({ tonConnectUI })); - ``` - - Refer to the [complete initialization setup](#complete-setup) for all the possible AppKit configuration options. - - - - Instead of consolidating everything within the instantiated `TonConnectUI` object, AppKit offers several tree-shakable functions that enhance and expand the capabilities of the existing `TonConnectUI` functionality. Refer to [relevant how-to pages](/ecosystem/appkit/overview#quick-start) for concrete examples. - - - -## Next steps - - - - - -## See also - -- [AppKit overview](/ecosystem/appkit/overview) -- [TON Connect's app manifest](/ecosystem/ton-connect/manifest#app-manifest) -- [TON Connect overview](/ecosystem/ton-connect/overview) diff --git a/ecosystem/appkit/jettons.mdx b/ecosystem/appkit/jettons.mdx deleted file mode 100644 index d164c5e3e..000000000 --- a/ecosystem/appkit/jettons.mdx +++ /dev/null @@ -1,587 +0,0 @@ ---- -title: "How to work with Jettons using AppKit" -sidebarTitle: "Work with Jettons" ---- - -import { Aside } from '/snippets/aside.jsx'; - - - -[Jettons](/standard/tokens/jettons/overview) are fungible tokens on TON, similar to ERC-20 tokens on Ethereum. Unlike Toncoin, which is the native TON currency used in all transfers, each jetton has a separate master (minter) contract and an individual wallet contract for each holder. - -For example, USDT on TON is implemented as a jetton, and its minter contract address is `EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs`. By providing this address and the recipient's TON wallet contract address, AppKit knows which tokens to send and to whom. - - - -## Metadata - -Retrieve metadata about a specific jetton, such as its name, symbol, and decimals: - - - ```tsx title="React" icon="react" - import { useJettonInfo } from '@ton/appkit-react'; - - export const JettonCard = ({ jettonAddress }) => { - const { - data: info, - isLoading, - error, - } = useJettonInfo({ - // Jetton master (minter) contract address - address: jettonAddress, - }); - - if (isLoading) { - return
Loading...
; - } - - if (error) { - return
Error: {error.message}
; - } - - return ( -
-

Jetton info

-

Name: {info?.name}

-

Symbol: {info?.symbol}

-

Decimals: {info?.decimals}

-
- ); - }; - ``` - - ```ts title="TypeScript" icon="globe" - import { type AppKit, getJettonInfo } from '@ton/appkit'; - - async function fetchJettonInfo( - /** Initialized AppKit instance */ - kit: AppKit, - /** Jetton master (minter) contract address */ - jettonAddress: string, - ) { - const info = await getJettonInfo(kit, { - address: jettonAddress, - }); - console.log('Jetton info:', info); - } - ``` -
- -### Jetton wallet address - -Each jetton holder has a dedicated jetton wallet contract. To resolve its address for a given owner: - - - ```tsx title="React" icon="react" expandable - import { - useJettonWalletAddress, - useAddress, - } from '@ton/appkit-react'; - - export const JettonWalletAddressCard = ({ jettonAddress }) => { - const ownerAddress = useAddress(); - const { - data: walletAddress, - isLoading, - error, - } = useJettonWalletAddress({ - // TON wallet address of the jetton holder - ownerAddress: ownerAddress ?? '', - - // Jetton master (minter) contract address - jettonAddress, - }); - - if (isLoading) { - return
Loading...
; - } - - if (error) { - return
Error: {error.message}
; - } - - return
Jetton wallet address: {walletAddress?.toString()}
; - }; - ``` - - ```ts title="TypeScript" icon="globe" expandable - import { - type AppKit, - getJettonWalletAddress, - getSelectedWallet, - } from '@ton/appkit'; - - async function fetchJettonWalletAddress( - /** Initialized AppKit instance */ - kit: AppKit, - /** Jetton master (minter) contract address */ - jettonAddress: string, - ) { - const selectedWallet = getSelectedWallet(kit); - const address = await getJettonWalletAddress(kit, { - jettonAddress, - // TON wallet address of the jetton holder - ownerAddress: selectedWallet?.getAddress() ?? '', - }); - console.log('Jetton wallet address:', address); - } - ``` -
- -## Balance - -Similar to Toncoin balance checks, [discrete one-off checks](#on-demand-balance-check) have limited value on their own and [continuous monitoring](#continuous-balance-monitoring) should be used for UI display. - -Unlike Toncoin, the balance units and decimal places vary between jettons — use the `decimals` field from the [jetton's metadata](#metadata) to interpret raw amounts correctly. - -USDT has a decimal precision of 6, meaning that the fractional balance string `'0.1'` represents a balance of 0.1 USDT, or 100000 micro USDT (raw units). - -### On-demand balance check - - - -#### Single jetton - -Check the balance of a specific jetton for the connected TON wallet or an arbitrary address: - - - ```tsx title="React" icon="react" - import { - useJettonBalanceByAddress, - useAddress, - } from '@ton/appkit-react'; - - export const JettonBalanceCard = ({ jettonAddress }) => { - const ownerAddress = useAddress(); - const { - data: balance, - isLoading, - error, - } = useJettonBalanceByAddress({ - // TON wallet address of the jetton holder - ownerAddress: ownerAddress ?? '', - - // Jetton master (minter) contract address - jettonAddress, - - // Jetton decimals to calculate raw unit amounts - jettonDecimals: 6, - }); - - if (isLoading) { - return
Loading...
; - } - - if (error) { - return
Error: {error.message}
; - } - - return
Jetton balance: {balance ?? '0'}
; - }; - ``` - - ```ts title="TypeScript" icon="globe" - import { - type AppKit, - getJettonBalance, - getSelectedWallet, - } from '@ton/appkit'; - - async function fetchJettonBalance( - /** Initialized AppKit instance */ - kit: AppKit, - /** Jetton master (minter) contract address */ - jettonAddress: string, - /** Jetton decimals to calculate raw unit amounts */ - jettonDecimals: number = 6, - ) { - const selectedWallet = getSelectedWallet(kit); - const balance = await getJettonBalance(kit, { - jettonAddress, - // TON wallet address of the jetton holder - ownerAddress: selectedWallet?.getAddress() ?? '', - }); - console.log('Jetton balance:', balance ?? '0'); - } - ``` -
- -#### All jettons - -Retrieve every jetton held by the connected TON wallet or an arbitrary address: - - - ```tsx title="React" icon="react" - import { - useJettonsByAddress, - useAddress, - // Helper function targeting the connected wallet - useJettons, - } from '@ton/appkit-react'; - - export const JettonListByAddress = () => { - const address = useAddress(); - const { - data: jettons, - isLoading, - error, - } = useJettonsByAddress({ - // TON wallet address of the jetton holder - address: address ?? '', - }); - - // Alternatively, query the connected wallet directly - // const { data: jettons, isLoading, error } = useJettons(); - - if (isLoading) { - return
Loading...
; - } - - if (error) { - return
Error: {error.message}
; - } - - return ( -
-

Jettons

-
    - {jettons?.jettons.map((jetton) => ( -
  • - {jetton.info.name}: {jetton.balance ?? '0'} -
  • - ))} -
-
- ); - }; - ``` - - ```ts title="TypeScript" icon="globe" - import { - type AppKit, - getJettonsByAddress, - getSelectedWallet, - // Helper function targeting the connected wallet - getJettons, - } from '@ton/appkit'; - - async function fetchJettonsByAddress( - /** AppKit instance */ - kit: AppKit, - ) { - const selectedWallet = getSelectedWallet(kit); - const response = await getJettonsByAddress(kit, { - address: selectedWallet?.getAddress() ?? '', - }); - - // Alternatively, query the connected wallet directly - // const response = await getJettons(); - - console.log('Jettons by address:', response.jettons.length); - response.jettons.forEach((j) => - console.log(`- ${j.info.name}: ${j.balance ?? '0'}`), - ); - } - ``` -
- -### Continuous balance monitoring - -Poll the balance at regular intervals to keep the displayed value up to date. Use an appropriate interval based on UX requirements — shorter intervals provide fresher data but increase API usage. - -Modify the following example according to the application logic: - - - ```tsx title="React" icon="react" - import { - useJettonBalanceByAddress, - useAddress, - } from '@ton/appkit-react'; - - export const JettonBalanceCard = ({ jettonAddress }) => { - const ownerAddress = useAddress(); - const { - data: balance, - isLoading, - error, - refetch, - } = useJettonBalanceByAddress({ - // TON wallet address of the jetton holder - ownerAddress: ownerAddress ?? '', - - // Jetton master (minter) contract address - jettonAddress, - }); - - if (isLoading) { - return
Loading...
; - } - - if (error) { - return ( -
-

Error: {error.message}

- -
- ); - } - - return
Jetton balance: {balance ?? '0'}
; - }; - ``` - - ```ts title="TypeScript" icon="globe" expandable - // Not runnable: implement the updateUI() - import { - type AppKit, - getJettonBalance, - getSelectedWallet, - } from '@ton/appkit'; - - /** - * Starts the monitoring of a specific jetton balance for the connected wallet, - * calling `onBalanceUpdate()` every `intervalMs` milliseconds - * - * @returns a function to stop monitoring - */ - export function startJettonBalanceMonitoring( - kit: AppKit, - jettonAddress: string, - onBalanceUpdate: (balance: string) => void, - intervalMs: number = 10_000, - ): () => void { - let isRunning = true; - - const poll = async () => { - while (isRunning) { - const selectedWallet = getSelectedWallet(kit); - if (selectedWallet) { - const balance = await getJettonBalance(kit, { - jettonAddress, - // TON wallet address of the jetton holder - ownerAddress: selectedWallet?.getAddress() ?? '', - }); - onBalanceUpdate(balance); - } - await new Promise((resolve) => setTimeout(resolve, intervalMs)); - } - }; - - // Start monitoring - poll(); - - // Return a cleanup function to stop monitoring - return () => { - isRunning = false; - }; - } - - // Usage - const stopMonitoring = startJettonBalanceMonitoring( - kit, - // Jetton master (minter) contract address - // E.g., USDT on TON mainnet: - 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs', - // The updateUI() function is exemplary and should be replaced by - // an app function that refreshes the state of the balance displayed - // in the interface - (balance) => updateUI(balance), - ); - - // Stop monitoring once it is no longer needed - stopMonitoring(); - ``` -
- -## Transfers - - - -Before making a transfer, make sure there is enough Toncoin in the balance to cover the [fees](/foundations/fees). - -Modify the following examples according to the application logic: - - - ```tsx title="React (component)" icon="react" - // Pre-built UI component for sending jetton transactions by clicking a button. - // It handles success and error states while being customizable. - import { SendJettonButton } from '@ton/appkit-react'; - - export const SendJetton = () => { - // For example: 'UQ...' - const recipientAddress = ''; - // For example, '0.1' or '1' jetton - const jettonAmount = ''; - // For example, 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs' - const jettonAddress = ''; - - return ( - console.log('Transaction sent:', result)} - - // (optional) Handle errors - onError={(error) => console.error('Transaction failed:', error)} - - // (optional) Add custom CSS classes - className='' - - // (optional) When set to `true`, the button is disabled - disabled={false} - /> - ); - }; - ``` - - ```tsx title="React (hook)" icon="react" - import { useTransferJetton } from '@ton/appkit-react'; - - export const SendJetton = ({ recipientAddress, jettonAddress }) => { - const { mutate: transfer, isPending, error, data } = useTransferJetton(); - - const handleTransfer = () => { - transfer({ - // New owner of the sent jettons. - // For example: 'UQ...' - recipientAddress, - - // Jetton amount string in fractional units. - // For example, '0.1' or '1' jetton - amount: '', - - // Jetton master (minter) contract address. - jettonAddress, - - // Jetton decimals to calculate raw unit amounts. - // For example, USDT defaults to 6, while Toncoin to 9: - jettonDecimals: 6, - - // (optional) Comment string. Defaults to none if not provided. - comment: 'Hello from AppKit!', - }); - }; - - return ( -
- - {error &&
Error: {error.message}
} - {data && ( -
-

Transfer successful: {data.boc}

-
- )} -
- ); - }; - ``` - - ```ts title="TypeScript" icon="globe" - import { - type AppKit, - type Base64String, - // Single-call transfer - transferJetton, - // Two-step transfer: create a transaction object separately from sending it - createTransferJettonTransaction, - sendTransaction, - } from '@ton/appkit'; - - async function sendJetton( - /** Initialized AppKit instance */ - kit: AppKit, - /** Recipient's TON wallet address as a string */ - recipientAddress: string, - /** - * Jetton amount string in fractional units - * E.g., '0.1' or '1' jetton - */ - amount: string, - /** Jetton decimals to calculate raw unit amounts */ - jettonDecimals: number = 6, - /** Jetton master (minter) contract address */ - jettonAddress: string, - /** Optional comment string */ - comment?: string, - ) { - // Sign and send via TON Connect - const result = await transferJetton(kit, { - recipientAddress, - amount, - jettonDecimals, - jettonAddress, - ...(comment && { comment }), - }); - console.log('Transaction sent:', result.boc); - - // Alternatively, build the transaction first with createTransferJettonTransaction, - // then pass the resulting object to the sendTransaction function. - } - ``` -
- -## Next steps - - - - - -## See also - -Jettons: - -- [Jettons overview](/standard/tokens/jettons/overview) -- [Jetton transfers](/standard/tokens/jettons/transfer) -- [Token metadata and decimals](/standard/tokens/metadata#decimals) - -General: - -- [Transaction fees](/foundations/fees) -- [AppKit overview](/ecosystem/appkit/overview) -- [TON Connect overview](/ecosystem/ton-connect) diff --git a/ecosystem/appkit/nfts.mdx b/ecosystem/appkit/nfts.mdx deleted file mode 100644 index 2626f5f5a..000000000 --- a/ecosystem/appkit/nfts.mdx +++ /dev/null @@ -1,478 +0,0 @@ ---- -title: "How to work with NFTs using AppKit" -sidebarTitle: "Work with NFTs" ---- - -import { Aside } from '/snippets/aside.jsx'; - - - -[NFTs](/standard/tokens/nft/overview) (non-fungible tokens) are unique digital assets on TON, similar to ERC-721 tokens on Ethereum. Unlike [jettons](/standard/tokens/jettons/overview), which are fungible and interchangeable, each NFT is unique and represents ownership of a specific item. NFTs consist of a collection contract and individual NFT item contracts for each token. - - - -## Ownership - -NFT ownership is tracked through individual NFT item contracts. Unlike jettons, which have a balance, one either owns a specific NFT item or does not. - -Similar to other asset queries, [discrete one-off checks](#on-demand-ownership-check) have limited value on their own and [continuous monitoring](#continuous-ownership-monitoring) should be used for UI display. - -### On-demand ownership check - - - -#### Single NFT - -Obtain the information of a specific NFT by its address and check the ownership: - - - ```tsx title="React" icon="react" - import { - useNft, - useAddress, - } from '@ton/appkit-react'; - - export const NftCard = ({ nftAddress }) => { - const address = useAddress(); - const { - data: nft, - isLoading, - error, - } = useNft({ - // NFT contract address - address: nftAddress ?? '', - }); - - if (isLoading) { - return
Loading...
; - } - - if (error) { - return
Error: {error.message}
; - } - - return ( -
-

NFT info

-

Name: {nft?.info?.name}

-

Collection: {nft?.collection?.name}

-

Owner address: {nft?.ownerAddress?.toString()}

-

Am I the owner: {address && nft?.ownerAddress === address ? 'yes' : 'no'}

-
- ); - }; - ``` - - ```ts title="TypeScript" icon="globe" - import { - type AppKit, - type NFT, - getNft, - getSelectedWallet, - } from '@ton/appkit'; - - async function fetchNft( - /** Initialized AppKit instance */ - kit: AppKit, - /** NFT contract address */ - nftAddress: string, - ): Promise { - const selectedWallet = getSelectedWallet(kit); - const nft = await getNft(kit, { - address: nftAddress, - }); - console.log('NFT info'); - console.log(`Name: ${nft?.info?.name}`); - console.log(`Collection: ${nft?.collection?.name}`); - console.log(`Owner address: ${nft?.ownerAddress?.toString()}`); - console.log( - `Am I the owner: ${nft?.ownerAddress === selectedWallet?.getAddress() ? 'yes' : 'no'}` - ); - return nft; - } - ``` -
- -#### All NFTs - -Retrieve NFTs held by the connected TON wallet or an arbitrary address. If there are many held NFTs, provide the `limit` option to retrieve fewer items and the `offset` option to paginate the item lists. - - - ```tsx title="React" icon="react" - import { - useNftsByAddress, - useAddress, - // Helper function targeting the connected wallet - useNfts, - } from '@ton/appkit-react'; - - export const NftListByAddress = () => { - const address = useAddress(); - const { - data: nfts, - isLoading, - error, - } = useNftsByAddress({ - // TON wallet address of the NFT holder - address: address ?? '', - }); - - // Alternatively, query the connected wallet directly - // const { data: nfts, isLoading, error } = useNfts(); - - if (isLoading) { - return
Loading...
; - } - - if (error) { - return
Error: {error.message}
; - } - - return ( -
-

NFTs

-
    - {nfts?.nfts.map((nft) => ( -
  • - {nft.info?.name}: {nft.info?.description ?? '—.'} -
  • - ))} -
-
- ); - }; - ``` - - ```ts title="TypeScript" icon="globe" - import { - type AppKit, - type NFT, - getNftsByAddress, - getSelectedWallet, - // Helper function targeting the connected wallet - getNfts, - } from '@ton/appkit'; - - async function fetchNftsByAddress( - /** AppKit instance */ - kit: AppKit, - ): Promise { - const selectedWallet = getSelectedWallet(kit); - const response = await getNftsByAddress(kit, { - address: selectedWallet?.getAddress() ?? '', - }); - - // Alternatively, query the connected wallet directly - // const response = await getNfts(); - - console.log('NFTs by address:', response.nfts.length); - return response.nfts; - } - ``` -
- -### Continuous ownership monitoring - -Poll the NFT ownership at regular intervals to keep the displayed value up to date. Use an appropriate interval based on UX requirements — shorter intervals provide fresher data but increase API usage. - -Modify the following example according to the application logic: - - - ```tsx title="React" icon="react" - import { - useNfts, - } from '@ton/appkit-react'; - - export const NftsCard = () => { - const { - data: nfts, - isLoading, - error, - refetch, - } = useNfts({ - // Only looks for up to 100 NFTs at a time - limit: 100, - }); - - if (isLoading) { - return
Loading...
; - } - - if (error) { - return ( -
-

Error: {error.message}

- -
- ); - } - - return
NFTs by address: {nfts?.nfts.length}
; - }; - ``` - - ```ts title="TypeScript" icon="globe" expandable - // Not runnable: implement the updateNftGallery() - import { - type AppKit, - type NFT, - getNfts, - } from '@ton/appkit'; - - /** - * Starts the monitoring of a given wallet's NFT ownership, - * calling `onNftsUpdate()` every `intervalMs` milliseconds - * - * @returns a function to stop monitoring - */ - export function startNftOwnershipMonitoring( - kit: AppKit, - onNftsUpdate: (nfts: NFT[]) => void, - intervalMs: number = 10_000, - ): () => void { - let isRunning = true; - - const poll = async () => { - while (isRunning) { - // Only looks for up to 100 NFTs. - // To get more, call the `getNfts()` function - // multiple times with increasing offsets - const nfts = await getNfts(kit, { limit: 100 }); - onNftsUpdate(nfts?.nfts ?? []); - await new Promise((resolve) => setTimeout(resolve, intervalMs)); - } - }; - - // Start monitoring - poll(); - - // Return a cleanup function to stop monitoring - return () => { - isRunning = false; - }; - } - - // Usage - const stopMonitoring = startNftOwnershipMonitoring( - kit, - // The updateNftGallery() function is exemplary and should - // be replaced by an app function that refreshes the NFT gallery - // displayed in the interface - (balance) => updateNftGallery(balance), - ); - - // Stop monitoring once it is no longer needed - stopMonitoring(); - ``` -
- -## Transfers - - - -Before making a transfer, make sure there is enough Toncoin in the balance to cover the [fees](/foundations/fees). - -Modify the following examples according to the application logic: - - - ```tsx title="React" icon="react" - import { useTransferNft } from '@ton/appkit-react'; - - export const SendNft = ({ recipientAddress, nftAddress }) => { - const { mutate: transfer, isPending, error, data } = useTransferNft(); - - const handleTransfer = () => { - transfer({ - // New owner of the sent NFT. - // For example: 'UQ...' - recipientAddress, - - // NFT contract address. - nftAddress, - - // (optional) Additional Toncoin sent to recipient. - // An amount string in fractional units. - // For example, '0.1' or '1' Toncoin. - amount: '', - - // (optional) Comment string. Defaults to none if not provided. - comment: 'Hello from AppKit!', - }); - }; - - return ( -
- - {error &&
Error: {error.message}
} - {data && ( -
-

Transfer successful: {data.boc}

-
- )} -
- ); - }; - ``` - - ```ts title="TypeScript" icon="globe" - import { - type AppKit, - type Base64String, - // Single-call transfer - transferNft, - // Two-step transfer: create a transaction object separately from sending it - createTransferNftTransaction, - sendTransaction, - } from '@ton/appkit'; - - async function sendNft( - /** Initialized AppKit instance */ - kit: AppKit, - /** Recipient's TON wallet address as a string */ - recipientAddress: string, - /** NFT contract address */ - nftAddress: string, - /** - * Optional additional Toncoin sent to recipient. - * An amount string in fractional units. - * E.g., '0.1' or '1' Toncoin. - */ - amount?: string, - /** Optional comment string */ - comment?: string, - ) { - // Sign and send via TON Connect - const result = await transferNft(kit, { - recipientAddress, - nftAddress, - ...(amount && { amount }), - ...(comment && { comment }), - }); - console.log('Transaction sent:', result.boc); - - // Alternatively, build the transaction first with createTransferNftTransaction, - // then pass the resulting object to the sendTransaction function. - } - ``` -
- -## `NFT` type - -NFT-related queries produce objects that conform to the following interface: - -```ts title="TypeScript" -/** - * Non-fungible token (NFT) on the TON blockchain. - */ -export interface NFT { - /** - * Contract address of the NFT item - */ - address: string; - - /** - * Index of the item within its collection - */ - index?: string; - - /** - * Display information about the NFT (name, description, images, etc.) - */ - info?: TokenInfo; - - /** - * Custom attributes/traits of the NFT (e.g., rarity, properties) - */ - attributes?: NFTAttribute[]; - - /** - * Information about the collection this item belongs to - */ - collection?: NFTCollection; - - /** - * Address of the auction contract, if the NFT is being auctioned - */ - auctionContractAddress?: string; - - /** - * Hash of the NFT smart contract code - */ - codeHash?: string; // hexadecimal characters - - /** - * Hash of the NFT's on-chain data - */ - dataHash?: string; // hexadecimal characters - - /** - * Whether the NFT contract has been initialized - */ - isInited?: boolean; - - /** - * Whether the NFT is soulbound (non-transferable) - */ - isSoulbound?: boolean; - - /** - * Whether the NFT is currently listed for sale - */ - isOnSale?: boolean; - - /** - * Current owner address of the NFT - */ - ownerAddress?: string; - - /** - * Real owner address when NFT is on sale (sale contract becomes temporary owner) - */ - realOwnerAddress?: string; - - /** - * Address of the sale contract, if the NFT is listed for sale - */ - saleContractAddress?: string; - - /** - * Off-chain metadata of the NFT (key-value pairs) - */ - extra?: { [key: string]: unknown }; -} -``` - -## See also - -NFTs: - -- [NFT overview](/standard/tokens/nft/overview) -- [NFT metadata](/standard/tokens/nft/metadata) - -General: - -- [Transaction fees](/foundations/fees) -- [AppKit overview](/ecosystem/appkit/overview) -- [TON Connect overview](/ecosystem/ton-connect) diff --git a/ecosystem/appkit/overview.mdx b/ecosystem/appkit/overview.mdx index eb16e5383..6a5c21a94 100644 --- a/ecosystem/appkit/overview.mdx +++ b/ecosystem/appkit/overview.mdx @@ -1,83 +1,80 @@ --- -title: "AppKit: SDK for decentralized applications (dApps)" +title: "AppKit" sidebarTitle: "Overview" --- -import { Aside } from '/snippets/aside.jsx'; +import { Aside } from "/snippets/aside.jsx"; -TON Connect's **AppKit** is an open-source SDK that integrates web2 and web3 applications with TON. It enables wallet connection, authorization, balance tracking, asset transfers, and data signing. +| Goal | Read | +| --------------------------- | ---------------------------------------------------------------- | +| Add AppKit to a new app | [Get started](/ecosystem/appkit/get-started) | +| Understand the moving parts | [How to](/ecosystem/appkit/howto) | +| Look up functions and types | [Reference](/ecosystem/appkit/reference) | +| Check upgrade notes | [Changelog](/ecosystem/appkit/changelog) | +| Diagnose common issues | [FAQ and troubleshooting](/ecosystem/appkit/faq-troubleshooting) | -