# TronLink Developer Documentation — Full Text > Concatenation of all English documentation pages for single-fetch LLM ingestion. Generated by scripts/gen_llms_full.py. See ./llms.txt for the curated index. - Generated: 2026-05-21T14:08:15Z - Commit: e724781c635f - Language: en - Pages: 23 - Token estimate: ~56,926 (chars / 4) --- # Introduction TRON is a robust blockchain ecosystem designed and developed by blockchain developers all over the world, which follows the philosophy of "Decentralize the Web". TronLink is a decentralized wallet that connects the DApps running on the TRON ecosystem. It ensures user’s data security through local storage of the private key and multi-layer algorithm encryption. In order to serve users worldwide, TronLink supports TRX, TRC-10, TRC-20, and TRC-721 tokens. Additionally, it offers a secure DApp explorer that facilitates the operation of Tron DApps. --- # HD Wallets ## What is an HD Wallet? HD wallets are hierarchical deterministic wallets proposed in BIP-32 in order to avoid the trouble of managing a bunch of private keys. HD wallets allow selective sharing on multiple keypairs (private and public keys) that are generated from a single root. The seed is represented by mnemonic code (phrase) that consists of 12 words, which makes it easier for account holders to transcribe and memorize. ## TronLink Wallets - HD Wallets TronLink wallets support BIP-32 and BIP-44 proposals, and generate the corresponding TRON addresses based on the BIP-44 hierarchical sub-path. --- # Start Developing This document walks you through connecting a DApp to the TronLink wallet end-to-end — from detecting the wallet, requesting authorization, to signing and broadcasting a transaction. ## Overview TronLink is a browser extension wallet for the TRON network. By integrating TronLink, your DApp can communicate with the TRON network — TronLink injects a `window.tron` object into every page, exposing a `tronWeb` instance, a `request` method, and `on` / `removeListener` for event subscription. ## The `window.tron` Object ```typescript interface TronProvider { isTronLink: true; request: (args: { method: string; params?: any }) => Promise; // Invoke wallet features (e.g. eth_requestAccounts) tronWeb: TronWeb | false; // Bound to the user's current account/network after authorization; `false` until then on(event: string, listener: (...args: any[]) => void): this; // Subscribe to provider events (accountsChanged / chainChanged / connect) removeListener(event: string, listener: (...args: any[]) => void): this; } ``` Once the user authorizes the connection, `window.tron.tronWeb` is fully usable for building, signing and broadcasting transactions. ## Detect TronLink (TIP-6963) TIP-6963 is the recommended way to detect TronLink (and other TRON wallets) without polluting the global namespace. Listen for the `TIP6963:announceProvider` event and dispatch a `TIP6963:requestProvider` to ask installed wallets to announce themselves. ```javascript let tronProvider; window.addEventListener("TIP6963:announceProvider", (e) => { if (e.detail.info && e.detail.info.name === "TronLink") { tronProvider = e.detail.provider; // === window.tron } }); window.dispatchEvent(new Event("TIP6963:requestProvider")); ``` If `tronProvider` is still undefined after dispatching the request, TronLink is not installed — prompt the user to install it. ## Request Authorization Use `eth_requestAccounts` to ask the user to connect their wallet. On approval the promise resolves with an array containing the selected address; on failure it rejects with a `{ code, message }` error. ```typescript try { const accounts: string[] = await tronProvider.request({ method: "eth_requestAccounts", }); console.log("Connected:", accounts[0]); } catch (err) { // err shape: { code: number, message: string } console.warn("Authorization failed:", err); } ``` | code | Description | | ------ | ----------------------------------------------------------------------------------------------------------------- | | 4001 | User rejected the request — clicked **Reject**, closed the popup, or the request did not come from the active tab | | -32000 | Same origin issued another `eth_requestAccounts` within 20 seconds while the wallet was locked (rate-limited) | | 4200 | Unsupported method | For the full TIP-1102 (`eth_requestAccounts`) specification, see [Proactively Request TronLink Plugin Features](../plugin-wallet/active-requests.md); the legacy `tron_requestAccounts` connection method is documented on the same page. ## Get the `tronWeb` Instance The simplest helper — returns the existing `tronWeb` if the user has already authorized this DApp, otherwise requests authorization first. ```javascript async function getTronWeb() { if (!tronProvider) throw new Error("TronLink is not installed"); if (tronProvider.tronWeb?.ready) { return tronProvider.tronWeb; } await tronProvider.request({ method: "eth_requestAccounts" }); return tronProvider.tronWeb; } ``` `tronProvider.tronWeb` is normally usable as soon as `eth_requestAccounts` resolves. For everything that happens _after_ the initial authorization — the user switching accounts, locking the wallet, or changing networks — listen for `accountsChanged` / `chainChanged` on the provider. See [Receive Messages from TronLink](../plugin-wallet/passive-messages.md) for the full event set. After obtaining the `tronWeb` instance you can perform on-chain interactions: TRX/TRC20 transfers, multi-signature transactions, message signing, contract calls, and so on. For full `tronWeb` API usage, see the [TronWeb documentation](https://tronweb.network/docu/docs/intro). ## End-to-End Example: Sign and Broadcast a TRX Transfer The following self-contained HTML example detects TronLink via TIP-6963, requests authorization, builds a TRX transfer, prompts the user to confirm and sign via the TronLink popup, and broadcasts the signed transaction. ```html TronLink Demo ``` ## Related Documentation - [Proactively Request TronLink Plugin Features](../plugin-wallet/active-requests.md) — full request/response reference - [Receive Messages from TronLink](../plugin-wallet/passive-messages.md) — account / network change events - [General Transfer](transfer.md), [Multi-Signature Transfer](multi-sign-transfer.md), [Message Signature](message-signing.md), [Stake 2.0](stake2.md) - [TronWeb Documentation](https://tronweb.network/docu/docs/intro) --- # AI / LLMs TronLink's developer documentation is published in machine-readable form so AI agents and LLM-based tools can discover, read, and integrate it directly. !!! info "Allowed uses vs. crawler policy" This site is curated for **retrieval / RAG / inference-time grounding** — the `llms.txt` and `llms-full.txt` bundles below are the canonical ingestion points for that case, and they are explicitly maintained for AI use. Training-time bulk crawling is a separate question and is governed by the site's `robots.txt` plus the `Content-Signal` directives served at the edge. Some training-only user-agents are currently disallowed there; if you need a different policy for your use case, contact us rather than parsing the bundles to circumvent the signal. ## Available endpoints > **URL ≠ source filename.** Source files carry `.en` / `.zh` suffixes (`llms.zh.txt`, `llms-full.en.txt`, `llms-full.zh.txt`) so the mkdocs i18n plugin can route them. **In the deployed site the suffix is stripped** — Chinese variants live under `/zh/`. Use the deployed URLs below; the source-name URLs (e.g. `/llms-full.en.txt`) return 404. | Endpoint (deployed URL) | Description | | --- | --- | | [/llms.txt](../../llms.txt) | Curated English index of key pages ([llmstxt.org](https://llmstxt.org/) format) | | [/zh/llms.txt](../../zh/llms.txt) | Curated Chinese index — same layout, links into `/zh/` pages | | [/llms-full.txt](../../llms-full.txt) | Every English page concatenated for single-fetch ingestion (built from `docs/llms-full.en.txt`) | | [/zh/llms-full.txt](../../zh/llms-full.txt) | Every Chinese page concatenated for single-fetch ingestion (built from `docs/llms-full.zh.txt`) | Production URLs: `https://docs.tronlink.org/llms.txt`, `https://docs.tronlink.org/zh/llms.txt`, and the matching `llms-full.txt` bundles under each locale root. ## Which file should I use? | Use case | URL | | --- | --- | | Navigate / find the right English page | `/llms.txt` — short, link-only map | | Navigate / find the right Chinese page | `/zh/llms.txt` — same layout, Chinese descriptions | | Ingest the whole English documentation in one request | `/llms-full.txt` | | Ingest the whole Chinese documentation in one request | `/zh/llms-full.txt` | Start with the index for your language and follow its links; fetch a full bundle when you need everything at once. The Chinese index points at `/zh/` slugs; the English index points at root-level slugs. ## Add to your AI tool - **Cursor** — Settings → Features → Docs → *Add new doc*, paste `https://docs.tronlink.org/llms.txt`. - **Claude / MCP-capable agents** — TronLink ships MCP servers for live wallet and chain access; see [MCP Server TronLink](mcp-server-tronlink.md) and [MCP TronLink Signer](mcp-tronlink-signer.md) for host configuration. For documentation context, point the tool at the `llms.txt` URL above. - **Other tools** — any tool that accepts a custom documentation URL can use the `llms.txt` / `llms-full.txt` endpoints. ## Coverage The bundle covers the full documentation, including the AI/agent tooling: - [MCP Server TronLink](mcp-server-tronlink.md) — on-chain, multi-sig, and GasFree tools (Playwright + Direct API) - [TronLink MCP Core](tronlink-mcp-core.md) — framework library: schemas, tool definitions, flow recipes - [TronLink Skills](tronlink-skills.md) — read-only agent skill set (wallet, token, market, swap, resource, staking) - [MCP TronLink Signer](mcp-tronlink-signer.md) — MCP server wrapping the signer SDK - [TronLink Signer](tronlink-signer.md) — standalone signer SDK - [TronLink CLI](tronlink-cli.md) — command-line tool Plus the DApp integration, mobile (DeepLink), and Reference (networks, glossary, FAQ) sections. ## Notes for agents - Start from `llms.txt` — it is the authoritative map. Don't enumerate the site blindly. - For tool calls, branch on the structured `error.code` / `error.retryable`, never on the human-readable `message`. - Read operations are safe to retry; signing / Remote Write operations require user approval (HITL) and must not be auto-retried — see each tool's Safety section. - Default to testnets (`nile` / `shasta`) when experimenting; use `mainnet` only for real funds. --- # Asset Management ## Custom Token ### What is Custom Token? Users can manually add TRC-20 and TRC-721 assets that are not recorded as tokens on TronScan as custom tokens in TronLink, which helps users search and manage their token assets more easily. ### Add Custom Token Add: Add tokens in TronLink App: Home --> My Assets --> Add Custom Token Possible limitations: Certain TronLink features, such as "transfer" and "approve," may be unavailable when custom tokens are added in TronLink. This is because certain ABI in the contract code (e.g., transfer, approve, etc.) are not identified. TronLink will notify users when such cases occur. Synchronization with TronScan: If the custom token has already been recorded on TronScan, Tronlink will notify the user to synchronize token information with TronScan; once done, the custom token will adopt the corresponding token information on TronScan. --- # DeepLink DApps and H5 pages can launch the TronLink App to open the wallet, log in, make transfers, sign transactions, open DApps in the wallet, and more via DeepLink. ![TronLink App DeepLink overview](../images/tronlink-app_deeplink_img_0.jpg) For every action below, the `param` value of `tronlinkoutside://pull.activity?param={}` is the protocol data in JSON format. Note: the JSON string must be URL-encoded before being placed in the link. ## Open Wallet ### Launch TronLink wallet via DeepLink Available since TronLink v4.10.0. ```html Open Tronlink ``` `param` (request): ```json { "action": "open", "protocol": "TronLink", "version": "1.0" } ``` ## Open DApp ### Use DeepLink to launch TronLink and open DApp in the DApp Explorer Available since TronLink v4.10.0. ```html Open DApp ``` `param` (request) — `url` is the target DApp: ```json { "url": "https://www.tronlink.org/", "action": "open", "protocol": "TronLink", "version": "1.0" } ``` ## Login by TronLink Available since TronLink v4.11.0. ```html Login/Request Address ``` `param` (request): ```json { "url": "https://justlend.org/#/home", "callbackUrl": "https://your-backend.example.com/api/tron/v1/callback", "dappIcon": "https://your-dapp.example.com/icon.png", "dappName": "Test demo", "protocol": "TronLink", "version": "1.0", "chainId": "0x2b6653dc", "action": "login", "actionId": "e5471a9c-b0f1-418b-8634-3de60d68a288" } ``` Callback: ```json { "actionId": "e5471a9c-b0f1-418b-8634-3de60d68a288", "address": "TSPrmJetAMo6S6RxMd4tswzeRCFVegBNig", "code": 0, "id": 1780812177, "message": "success" } ``` ## Transfer Available since TronLink v4.11.0. ```html Transfer ``` `param` (request): ```json { "url": "https://justlend.org/#/home", "callbackUrl": "https://your-backend.example.com/api/tron/v1/callback", "dappIcon": "https://your-dapp.example.com/icon.png", "dappName": "Test demo", "protocol": "TronLink", "version": "1.0", "chainId": "0x2b6653dc", "memo": "Reward", "from": "TSPrmJetAMo6S6RxMd4tswzeRCFVegBNig", "to": "TXd9duqtcyyj4pBCKvXKNqmazxxDw5SdBa", "loginAddress": "TSPrmJetAMo6S6RxMd4tswzeRCFVegBNig", "tokenId": "0", "contract": "", "amount": "20", "action": "transfer", "actionId": "408170fc-7919-4459-be5e-05a9d4b4065e" } ``` Callback (`actionId` matches the request): ```json { "actionId": "408170fc-7919-4459-be5e-05a9d4b4065e", "code": 0, "id": 1142367107, "message": "success", "transactionHash": "e8ffe9b92c771e66999732b810bf2493be389464191040d8666a26dc449fa5f0" } ``` ## Sign Transaction Available since TronLink v4.11.0. ```html Sign transaction ``` `param` (request): ```json { "url": "https://justlend.org/#/home", "callbackUrl": "https://your-backend.example.com/api/tron/v1/callback", "dappIcon": "https://your-dapp.example.com/icon.png", "dappName": "Test demo", "protocol": "TronLink", "version": "1.0", "chainId": "0x2b6653dc", "action": "sign", "loginAddress": "TSPrmJetAMo6S6RxMd4tswzeRCFVegBNig", "method": "transfer(address,uint256)", "signType": "signTransaction", "data": "{\"visible\":false,\"txID\":\"dcfaf2c2d75d91994f9a23623e905eaa7d74bc804fa5821640111ada3441376a\",\"raw_data\":{\"contract\":[{\"parameter\":{\"value\":{\"data\":\"a9059cbb000000000000000000000000ed87a3ae2bf2ab8b95486a23f224487ad75c60200000000000000000000000000000000000000000000000000000000000000014\",\"owner_address\":\"41b42b84bad413dde093e27d01bb02ed9eede52c43\",\"contract_address\":\"41eca9bc828a3005b9a3b909f2cc5c2a54794de05f\"},\"type_url\":\"type.googleapis.com/protocol.TriggerSmartContract\"},\"type\":\"TriggerSmartContract\"}],\"ref_block_bytes\":\"84e1\",\"ref_block_hash\":\"1731d6450e11a03f\",\"expiration\":1670168865000,\"fee_limit\":100000000,\"timestamp\":1670168805340},\"raw_data_hex\":\"0a0284e122081731d6450e11a03f40e8d1c9eecd305aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541b42b84bad413dde093e27d01bb02ed9eede52c43121541eca9bc828a3005b9a3b909f2cc5c2a54794de05f2244a9059cbb000000000000000000000000ed87a3ae2bf2ab8b95486a23f224487ad75c6020000000000000000000000000000000000000000000000000000000000000001470dcffc5eecd30900180c2d72f\"}", "actionId": "64fcdb39-2cfa-47f2-85bd-d7e8409809ed" } ``` Callback (`actionId` matches the request): ```json { "actionId": "64fcdb39-2cfa-47f2-85bd-d7e8409809ed", "code": 0, "id": -799302342, "message": "success", "successful": true, "transactionHash": "2fc49e560f648e5ecb455955d8778267ec1f257436425f62393b632c9a7a55ad" } ``` ## Sign Message Available since TronLink v4.11.0. ```html Sign message ``` `param` (request): ```json { "url": "https://justlend.org/#/home", "callbackUrl": "https://your-backend.example.com/api/tron/v1/callback", "dappIcon": "https://your-dapp.example.com/icon.png", "dappName": "Test demo", "protocol": "TronLink", "version": "1.0", "chainId": "0x2b6653dc", "loginAddress": "TSPrmJetAMo6S6RxMd4tswzeRCFVegBNig", "signType": "signStr", "message": "abc", "action": "sign", "actionId": "50554861-4861-41c4-adf3-abf36213f843" } ``` Callback: ```json { "actionId": "50554861-4861-41c4-adf3-abf36213f843", "code": 0, "id": 2001871012, "message": "success", "signedData": "0xffcac5731d9f70a58e5126f44c34b9356ccb9bef53331e33ddab84bb829adc1b77df24362348f8d46e506b489b4af4496600799b173e708faf1b9db99da9d13c1b" } ``` ## Result Code > Note: `tokenId` and `contract` are mutually exclusive in a transfer request — supplying both returns code `10025`. | Code | Message | |:-------|:-------| | 0 | success | | 10001 | Incorrect JSON format | | 10002 | Missing Action | | 10003 | Unknown Action | | 10004 | Missing ActionId | | 10005 | Incorrect DApp URL format | | 10006 | Incorrect CallbackUrl format | | 10007 | Empty DApp name | | 10008 | Version number not supported | | 10009 | Current network not supported | | 10010 | The URL is not supported to open TronLink | | 10011 | Unknown SignType | | 10012 | Incorrect Transaction format | | 10013 | Incorrect Method format | | 10014 | Incorrect Message format | | 10015 | Incorrect toAddress | | 10016 | No wallet created in TronLink | | 10017 | Incorrect fromAddress | | 10018 | Incorrect contractAddress | | 10019 | Incorrect chainId | | 10020 | Incorrect amount | | 10021 | The initiating address does not match the current wallet | | 10022 | Incorrect loginAddress | | 10023 | System contract not support | | 10024 | Incorrect tokenId | | 10025 | TokenId & Contract address should not exist together | | 300 | Transaction canceled | | 301 | Transaction executed in TronLink | | 302 | Broadcast failure - returned with incorrect info | | -1 | Unknown reason | ## JSON Schema Reference The `param` query value is a JSON object that — once URL-encoded — is the **entire DeepLink contract**. Schemas below follow JSON Schema Draft 7. The callback delivered to `callbackUrl` follows a small set of shapes keyed by action. ### Common envelope Every `param` shares this prefix: ```json { "type": "object", "required": ["action", "protocol", "version"], "properties": { "protocol": { "type": "string", "const": "TronLink" }, "version": { "type": "string", "const": "1.0" }, "action": { "type": "string", "enum": ["open", "login", "transfer", "sign"] }, "actionId": { "type": "string", "description": "UUID v4 — required for any action that produces a callback (login, transfer, sign); the callback echoes the same actionId" }, "url": { "type": "string", "format": "uri", "description": "DApp URL — required for everything except 'open wallet'" }, "callbackUrl": { "type": "string", "format": "uri", "description": "HTTPS endpoint that receives the JSON callback" }, "dappName": { "type": "string", "description": "DApp display name shown on the approval screen" }, "dappIcon": { "type": "string", "format": "uri", "description": "DApp icon URI shown on the approval screen" }, "chainId": { "type": "string", "enum": ["0x2b6653dc", "0x94a9059e", "0xcd8690dc"], "description": "Mainnet / Shasta / Nile (case-sensitive hex)" } } } ``` ### Per-action schemas **Open wallet** (no callback): ```json { "type": "object", "required": ["action", "protocol", "version"], "properties": { "action": { "const": "open" }, "protocol": { "const": "TronLink" }, "version": { "const": "1.0" } } } ``` **Open DApp in DApp Explorer** (no callback): ```json { "type": "object", "required": ["action", "protocol", "version", "url"], "properties": { "action": { "const": "open" }, "protocol": { "const": "TronLink" }, "version": { "const": "1.0" }, "url": { "type": "string", "format": "uri" } } } ``` **Login** request: ```json { "type": "object", "required": ["action", "actionId", "callbackUrl", "url", "protocol", "version"], "properties": { "action": { "const": "login" }, "actionId": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "callbackUrl": { "type": "string", "format": "uri" }, "dappName": { "type": "string" }, "dappIcon": { "type": "string", "format": "uri" }, "chainId": { "type": "string" }, "protocol": { "const": "TronLink" }, "version": { "const": "1.0" } } } ``` **Login** callback: ```json { "type": "object", "required": ["actionId", "code", "id", "message"], "properties": { "actionId": { "type": "string" }, "code": { "type": "integer", "description": "0 = success; see Result Code table for failure codes" }, "id": { "type": "integer", "description": "Internal request id (echoed for debugging)" }, "message": { "type": "string" }, "address": { "type": "string", "description": "Authorized TRON address (base58, T-prefix). Present only when code = 0." } } } ``` **Transfer** request — `tokenId` (TRX / TRC10) and `contract` (TRC20) are **mutually exclusive**; supplying both returns `10025`. Set the unused one to `""` (empty string): ```json { "type": "object", "required": ["action", "actionId", "callbackUrl", "url", "from", "to", "loginAddress", "amount", "protocol", "version"], "properties": { "action": { "const": "transfer" }, "actionId": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "callbackUrl": { "type": "string", "format": "uri" }, "from": { "type": "string", "description": "Sender TRON address (base58)" }, "to": { "type": "string", "description": "Recipient TRON address (base58)" }, "loginAddress": { "type": "string", "description": "Address that authorized this session — must match the current wallet (otherwise code 10021)" }, "tokenId": { "type": "string", "description": "TRC10 token id or '0' for TRX; pass '' when sending TRC20" }, "contract": { "type": "string", "description": "TRC20 contract address; pass '' when sending TRX / TRC10" }, "amount": { "type": "string", "description": "Decimal string in human units (e.g. '1.5'); the app handles decimals" }, "memo": { "type": "string", "description": "Optional transaction memo" }, "dappName": { "type": "string" }, "dappIcon": { "type": "string", "format": "uri" }, "chainId": { "type": "string" }, "protocol": { "const": "TronLink" }, "version": { "const": "1.0" } } } ``` **Transfer / sign-transaction** callback: ```json { "type": "object", "required": ["actionId", "code", "id", "message"], "properties": { "actionId": { "type": "string" }, "code": { "type": "integer" }, "id": { "type": "integer" }, "message": { "type": "string" }, "successful": { "type": "boolean", "description": "Only present for sign-transaction; reflects on-chain execution result" }, "transactionHash": { "type": "string", "description": "64-char hex tx id; present when broadcast succeeded" } } } ``` **Sign transaction** request — pass a fully-built TronWeb transaction in `data` (JSON string): ```json { "type": "object", "required": ["action", "actionId", "callbackUrl", "url", "loginAddress", "signType", "data", "protocol", "version"], "properties": { "action": { "const": "sign" }, "actionId": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "callbackUrl": { "type": "string", "format": "uri" }, "loginAddress": { "type": "string" }, "signType": { "const": "signTransaction" }, "method": { "type": "string", "description": "Optional function selector for UI display, e.g. 'transfer(address,uint256)'" }, "data": { "type": "string", "description": "JSON-stringified TronWeb transaction (raw_data, raw_data_hex, txID, visible)" }, "dappName": { "type": "string" }, "dappIcon": { "type": "string", "format": "uri" }, "chainId": { "type": "string" }, "protocol": { "const": "TronLink" }, "version": { "const": "1.0" } } } ``` **Sign message** request — `signType` controls the digest: ```json { "type": "object", "required": ["action", "actionId", "callbackUrl", "url", "loginAddress", "signType", "message", "protocol", "version"], "properties": { "action": { "const": "sign" }, "actionId": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "callbackUrl": { "type": "string", "format": "uri" }, "loginAddress": { "type": "string" }, "signType": { "type": "string", "enum": ["signStr", "signTypedData"], "description": "signStr = signMessageV2 (TIP-191); signTypedData = TIP-712" }, "message": { "type": "string", "description": "Plain text or hex (signStr) / JSON-stringified typed-data domain+message (signTypedData)" }, "dappName": { "type": "string" }, "dappIcon": { "type": "string", "format": "uri" }, "chainId": { "type": "string" }, "protocol": { "const": "TronLink" }, "version": { "const": "1.0" } } } ``` **Sign message** callback: ```json { "type": "object", "required": ["actionId", "code", "id", "message"], "properties": { "actionId": { "type": "string" }, "code": { "type": "integer" }, "id": { "type": "integer" }, "message": { "type": "string" }, "signedData": { "type": "string", "description": "0x-prefixed hex signature; present when code = 0" } } } ``` ### Result code enum (callback `code`) The full set of values is in the [Result Code](#result-code) table above. Branch on `code` (integer), not on `message` (which is human-readable and may be localized). --- # DApp Support ## TronLink Integration TronLink injects a version of TronWeb into the DApp that runs in TronLink's DApp Explorer. This enables the DApp to interact with TronLink DApps and the TRON network. Details: [Go to DApp](../../dapp/getting-started) ## DApp Explorer ### Basic Function The DApp Explorer allows Tron DApps to run and automatically injects tronWeb and TronLink objects. ### Extension Tron DApp running in the DApp Explorer injects iTron objects automatically to offer customized App service. ##### Change screen orientation ```java // url: DApp page url // screenModel: "1" -> vertical;"2" -> horizontal void setScreenModel(String url, String screenModel) ``` --- # Proactively Request TronLink Plugin Features ## Connect Website TIP-1102 ### Overview TronLink can be used to manage wallet private keys. Before performing operations that require signatures, a DApp must connect to TronLink and obtain user signature authorization through TronLink. This protocol explicitly informs users that the DApp is proactively requesting a TronLink connection and requests their authorization consent. This method follows the Ethereum **EIP-1102** protocol. ### Technical Specification #### Code Example ```javascript try { await window.tron.request({method: 'eth_requestAccounts'}); } catch (e) {} ``` #### Return Value If successful, an array is returned with a single element — the currently approved TronLink account. Example: `['TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC']` If it fails, an error code and error message will be returned. See the **Error Codes** section below. #### Error Codes | Error Code | Name | Description | | - | - | - | | 4001 | User rejected request | Triggered when the user clicks “Reject” or closes the popup | | -32002 | Another process in progress | Another DApp process is ongoing, request cannot execute | | -32602 | Invalid parameters | Invalid or extra parameters were provided | | 4200 | Method not supported | This method is not supported | ### Interaction Flow After triggering `eth_requestAccounts`, if TronLink is locked, an unlock popup appears: ![TronLink unlock popup prompting for the wallet password](../images/plugin-wallet_lock-page.jpg) After unlocking, or if already unlocked, a connection confirmation popup appears: ![TronLink connection authorization popup showing the requesting site origin with Confirm and Reject buttons](../images/plugin-wallet_request-accounts.jpg) > **Legacy (not recommended):** [Legacy: tron_requestAccounts](#legacy-tron_requestaccounts) ## Get TronLink Provider via TIP-6963 ### Introduction When multiple wallets exist simultaneously, they may compete to occupy the `window.tron` object. To ensure that a DApp can obtain a specific wallet provider, the TIP-6963 specification is implemented. ### Technical Specification #### Code Example ```typescript interface TIP1193Provider { request: (args: RequestArguments) => Promise; on(event: string, listener: (...args: any[]) => void): this; removeListener(event: string, listener: (...args: any[]) => void): this; tronWeb: TronWeb; [key: `is${string}`]: boolean; } /** * Represents the assets needed to display a wallet */ interface TIP6963ProviderInfo { uuid: string; name: string; icon: string; rdns: string; } interface TIP6963ProviderDetail { info: TIP6963ProviderInfo; provider: TIP1193Provider; } // Announce Event dispatched by a Wallet interface TIP6963AnnounceProviderEvent extends CustomEvent { type: "TIP6963:announceProvider"; detail: TIP6963ProviderDetail; } // The DApp listens to announced providers window.addEventListener( "TIP6963:announceProvider", (event: TIP6963AnnounceProviderEvent) => { // Confirm if it is a Tronlink UUID if (event.detail.info.rdns !== 'org.tronlink.www' || event.detail.info.name !== 'TronLink') { console.error('it is NOT TronLink provider'); return; } // event.detail.provider === window.tron const tronProvider = event.detail.provider; tronProvider.on('accountsChanged', (accountArray) => { console.log('tip-6963 accountsChanged', accountArray); }) } ); // The DApp dispatches a request event which will be heard by // Wallets' code that had run earlier window.dispatchEvent(new Event("TIP6963:requestProvider")); ``` After implementing the above code, the DApp can precisely obtain the provider supplied by TronLink. TronLink’s rdns is `org.tronlink.www`, and its name is `TronLink`. ## Normal Transfer > **Prerequisite:** The DApp connection has been authorized via `eth_requestAccounts` (see [Connect Website TIP-1102](#connect-website-tip-1102) above). ### Overview A DApp needs the user to initiate a TRX transfer. A transfer on the TRON network requires three steps: 1. Construct the transaction 2. Sign the transaction 3. Broadcast the signed transaction TronLink handles **step 2 (signing)**, while steps 1 and 3 must be completed using TronWeb. ### Technical Specification #### Code Example ```typescript const tronweb = window.tron.tronWeb; const fromAddress = tronweb.defaultAddress.base58; const toAddress = "TDvSsdrNM5eeXNL3czpa6AxLDHZA9nwe9K"; const tx = await tronweb.transactionBuilder.sendTrx(toAddress, 10, fromAddress); try { const signedTx = await tronweb.trx.sign(tx); await tronweb.trx.sendRawTransaction(signedTx); } catch (e) {} ``` When executing `await tronweb.trx.sign(tx);`, TronLink displays a confirmation popup. ![TronLink transaction approval popup showing the recipient address, TRX amount in human-readable form, and Reject / Sign buttons](../images/plugin-wallet_sign_trx.jpg) Reject → exception thrown. Sign → signed transaction returned for broadcasting. > **Legacy (not recommended):** [Legacy: sendTrx via window.tronLink](#legacy-sendtrx-via-windowtronlink) ## Multi-Signature Transfer > **Prerequisite:** The DApp connection has been authorized via `eth_requestAccounts` (see [Connect Website TIP-1102](#connect-website-tip-1102) above). ### Overview Refer to **Normal Transfer** above. ### Technical Specification #### Code Example ```typescript const tronweb = window.tron.tronWeb; const toAddress = "TDvSsdrNM5eeXNL3czpa6AxLDHZA9nwe9K"; const activePermissionId = 2; const tx = await tronweb.transactionBuilder.sendTrx( toAddress, 10, { permissionId: activePermissionId} ); try { const signedTx = await tronweb.trx.multiSign(tx, undefined, activePermissionId); await tronweb.trx.sendRawTransaction(signedTx); } catch (e) {} ``` Rejecting triggers an exception; signing returns the signed transaction for broadcasting. > **Legacy (not recommended):** [Legacy: multiSign via window.tronLink](#legacy-multisign-via-windowtronlink) ## Message Signing > **Prerequisite:** The DApp connection has been authorized via `eth_requestAccounts` (see [Connect Website TIP-1102](#connect-website-tip-1102) above). ### Overview A DApp may require users to sign a hex message. The signed message is then sent to the backend for verification to authenticate user login. ### Technical Specification #### Code Example ```typescript const tronweb = window.tron.tronWeb; try { const message = "0x01EF"; const signedString = await tronweb.trx.signMessageV2(message); } catch (e) {} ``` #### Parameter `window.tron.tronWeb.trx.signMessageV2` accepts a hexadecimal string representing the message to sign. #### Return Value If signed successfully: ```text 0xaa302ca153b10dff25b5f00a7e2f603c5916b8f6d78cdaf2122e24cab56ad39a79f60ff3916dde9761baaadea439b567475dde183ee3f8530b4cc76082b29c341c ``` If an error occurs: ```typescript Uncaught (in promise) Invalid transaction provided ``` ### Interaction Flow When executing signing, TronLink shows a confirmation popup with the hex message. ![TronLink message-signing approval popup showing the hex payload to be signed and Reject / Sign buttons](../images/plugin-wallet_sign_message.jpg) Reject → exception. Sign → signed message returned. > **Legacy (not recommended):** [Legacy: signMessageV2 via window.tronLink](#legacy-signmessagev2-via-windowtronlink) ## Add Asset > **Prerequisite:** The DApp connection has been authorized via `eth_requestAccounts` (see [Connect Website TIP-1102](#connect-website-tip-1102) above). ### Overview A DApp can provide a button allowing users to directly add a token to their TronLink asset list. ### Technical Specification #### Code Example ```typescript const res = await window.tron.request({ method: 'wallet_watchAsset', params: { type: 'trc20', options: { address: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t' } }, }); ``` #### Parameters ```typescript interface WatchAssetParams { type: 'trc10' | 'trc20' | 'trc721'; options: { address: string; symbol?: string; decimals?: number; image?: string; } } ``` - **method:** `wallet_watchAsset` - **type:** `trc10`, `trc20`, `trc721` - **address:** token contract address or token ID (required) #### Return Value No return value. ### Interaction Flow #### Add TRC10 ```typescript await window.tron.request({ method: 'wallet_watchAsset', params: { type: 'trc10', options: { address: '1002000' }, }, }); ``` When the code executes, TronLink will display an add-asset popup where the user can confirm adding the TRC10 asset or cancel the request. ![TronLink add-asset prompt showing the TRC10 token id, name, and Add / Cancel buttons](../images/plugin-wallet_add_trc10.jpg) Click the “Add” button and the asset will be added to the asset list, as shown below. ![TronLink asset list after the TRC10 token has been added, with the new token visible at the top](../images/plugin-wallet_add_trc10_success.jpg) #### Add TRC20 ```typescript await window.tron.request({ method: 'wallet_watchAsset', params: { type: 'trc20', options: { address: 'TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9' }, }, }); ``` When the code executes, TronLink will display an add-asset popup where the user can confirm adding the TRC20 asset or cancel the request. ![TronLink add-asset prompt showing the TRC20 contract address, token symbol, and Add / Cancel buttons](../images/plugin-wallet_add_trc20.jpg) Click the “Add” button and the asset will be added to the asset list, as shown below. ![TronLink asset list after the TRC20 token has been added, with the new token visible at the top](../images/plugin-wallet_add_trc20_success.jpg) #### Add TRC721 ```typescript await window.tron.request({ method: 'wallet_watchAsset', params: { type: 'trc721', options: { address: 'TVtaUnsgKXhTfqSFRnHCsSXzPiXmm53nZt' }, }, }); ``` When the code executes, TronLink will display an add-asset popup where the user can confirm adding the TRC721 asset or cancel the request. ![TronLink add-asset prompt showing the TRC721 NFT contract address and Add / Cancel buttons](../images/plugin-wallet_add_trc721.jpg) Click the “Add” button and the asset will be added to the asset list, as shown below. ![TronLink asset list after the TRC721 collection has been added, with the new collection visible at the top](../images/plugin-wallet_add_trc721_success.jpg) > **Legacy (not recommended):** [Legacy: wallet_watchAsset via window.tronLink](#legacy-wallet_watchasset-via-windowtronlink) ## Switch Network TIP-3326 ### Overview Most DApps operate on specific chains. This protocol allows a DApp to request TronLink to switch chains, with user confirmation. After approval, the DApp can operate normally on that chain. This protocol follows **EIP-3326**. ### Technical Specification #### Code Example ```javascript try { await window.tron.request({ method: 'wallet_switchEthereumChain', params: [{chainId: '0x2b6653dc'}] }); } catch (e) {} ``` #### Parameters ```typescript interface SwitchTronChainParameter { chainId: string; } ``` Supported chain IDs: - Mainnet: `0x2b6653dc` - Shasta Testnet: `0x94a9059e` - Nile Testnet: `0xcd8690dc` #### Return Value - Success → `null` - Failure → error code + message #### Error Codes | Error Code | Name | Description | | - | - | - | | 4001 | User rejected request | | 4902 | Invalid chainId | | -32002 | Another process in progress | | -32602 | Invalid parameters | | 4200 | Method not supported | ### Interaction Flow Triggering the request shows an unlock popup if TronLink is locked, then a network switch confirmation popup after unlocking. ![TronLink unlock popup prompting for the wallet password](../images/plugin-wallet_lock-page.jpg) ![TronLink network-switch confirmation popup showing the target network name and chainId with Confirm / Reject buttons](../images/plugin-wallet_switch-chain.jpg) > **Legacy (not recommended):** [Legacy: wallet_switchEthereumChain via tronLink.request](#legacy-wallet_switchethereumchain-via-tronlinkrequest) --- ## JSON Schema Reference All RPC-style methods below are invoked via `window.tron.request({ method, params? })`. tronWeb-based methods (sign, multiSign, signMessageV2) take a positional argument and return a promise. Schemas follow JSON Schema Draft 7. Branch on integer `error.code` (table per method), never on `error.message`. ### `eth_requestAccounts` (TIP-1102) **Request** — `params` must be omitted or `[]`: ```json { "type": "object", "required": ["method"], "properties": { "method": { "const": "eth_requestAccounts" }, "params": { "type": "array", "maxItems": 0 } } } ``` **Response** — array of exactly one approved base58 address: ```json { "type": "array", "minItems": 1, "maxItems": 1, "items": { "type": "string", "description": "TRON address (base58, T-prefix)" } } ``` **Errors** — `4001` user rejected · `-32002` another request pending · `-32602` invalid params · `4200` method not supported. ### `wallet_watchAsset` (Add Asset) **Request**: ```json { "type": "object", "required": ["method", "params"], "properties": { "method": { "const": "wallet_watchAsset" }, "params": { "type": "object", "required": ["type", "options"], "properties": { "type": { "type": "string", "enum": ["trc10", "trc20", "trc721"] }, "options": { "type": "object", "required": ["address"], "properties": { "address": { "type": "string", "description": "Token contract address (TRC20 / TRC721) or token id (TRC10)" }, "symbol": { "type": "string", "description": "Optional display symbol" }, "decimals": { "type": "integer", "minimum": 0, "description": "Optional display decimals" }, "image": { "type": "string", "format": "uri", "description": "Optional icon URI" } } } } } } } ``` **Response** — no return value (`undefined`). The promise resolves when the user clicks Add and rejects with code `4001` on Cancel. ### `wallet_switchEthereumChain` (TIP-3326) **Request**: ```json { "type": "object", "required": ["method", "params"], "properties": { "method": { "const": "wallet_switchEthereumChain" }, "params": { "type": "array", "minItems": 1, "maxItems": 1, "items": { "type": "object", "required": ["chainId"], "properties": { "chainId": { "type": "string", "enum": ["0x2b6653dc", "0x94a9059e", "0xcd8690dc"], "description": "Mainnet / Shasta / Nile (case-sensitive)" } } } } } } ``` **Response** — `null` on success. **Errors** — `4001` user rejected · `4902` invalid chainId · `-32002` another request pending · `-32602` invalid params · `4200` method not supported. ### `tronweb.trx.sign(transaction)` — sign a TRON transaction **Argument** — an unsigned TronWeb transaction object (the same shape returned by `tronweb.transactionBuilder.*`): ```json { "type": "object", "required": ["txID", "raw_data", "raw_data_hex"], "properties": { "visible": { "type": "boolean" }, "txID": { "type": "string", "description": "64-char hex transaction id" }, "raw_data": { "type": "object", "description": "Protocol-level transaction body (ref_block_*, expiration, contract[], timestamp, fee_limit?)" }, "raw_data_hex": { "type": "string" } } } ``` **Return** — same shape plus a `signature` array: ```json { "type": "object", "required": ["txID", "raw_data", "raw_data_hex", "signature"], "properties": { "signature": { "type": "array", "items": { "type": "string", "description": "65-byte hex signature (r||s||v)" } } } } ``` Rejection throws `Error("Confirmation declined by user")` (no numeric code). ### `tronweb.trx.multiSign(transaction, privateKey?, permissionId)` — multi-sig signing Same input/output shape as `sign`. `permissionId` is an integer; `2` typically corresponds to the first active permission. Each call appends one signature to the array — collect signatures until the threshold is reached before broadcasting via `sendRawTransaction`. ### `tronweb.trx.signMessageV2(message)` — TIP-191 message signing **Argument** — a string (plain UTF-8 or `0x`-prefixed hex): ```json { "type": "string" } ``` **Return** — `0x`-prefixed 65-byte hex signature: ```json { "type": "string", "pattern": "^0x[0-9a-fA-F]{130}$" } ``` Rejection throws `Error("Invalid transaction provided")` / `Error("user rejected request")` — branch on the thrown exception (try / catch); there is no `code` field. ### TIP-6963 (provider discovery) Event-based, not a `request` method. The shape is defined by TypeScript interfaces in [Get TronLink Provider via TIP-6963](#get-tronlink-provider-via-tip-6963) above; the wire format is two `CustomEvent` types: - `TIP6963:requestProvider` — dispatched by the DApp, no payload. - `TIP6963:announceProvider` — dispatched by each installed wallet with `detail = { info: { uuid, name, icon, rdns }, provider }`. TronLink uses `rdns = "org.tronlink.www"` and `name = "TronLink"`. ### Legacy `tron_requestAccounts` Retained for back-compat; new integrations should use `eth_requestAccounts`. **Request**: ```json { "type": "object", "required": ["method"], "properties": { "method": { "const": "tron_requestAccounts" }, "params": { "type": "object", "properties": { "websiteIcon": { "type": "string", "format": "uri" }, "websiteName": { "type": "string" } } } } } ``` **Response**: ```json { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "integer", "enum": [200, 4000, 4001] }, "message": { "type": "string" } } } ``` `code`: `200` site already authorized **or** user approved · `4000` duplicate authorization request pending · `4001` user rejected. Wallet-locked is signaled by an empty string return, not a code. --- ## Legacy Usage (Not Recommended) The following interfaces are retained as compatibility aliases. New integrations should use the recommended usage above. `window.tronLink` and `window.tron` are functionally equivalent, but the former is being phased out and is no longer actively maintained. ## Legacy: tron_requestAccounts ### Overview TronLink provides external TRX transfer, contract signing, authorization, and other functions. For security reasons, users must first authorize the requesting DApp via **Connect Website** before critical operations are allowed. Therefore, the DApp must perform the **Connect Website** request first and wait for user approval before initiating operations requiring authorization. ### Technical Specification #### Code Example ```typescript const res = await tronWeb.request( { method: 'tron_requestAccounts', params: { websiteIcon: '', websiteName: '', } as RequestAccountParams, } ); ``` #### Parameters ```typescript interface RequestAccountsParams { websiteIcon?: string; websiteName?: string; } ``` - **method:** fixed string `tron_requestAccounts` - **params:** `RequestAccountParams` type: - `websiteIcon`: DApp website icon URI (displayed in connected site list) - `websiteName`: DApp website name #### Return Value ```typescript interface ReqestAccountsResponse { code: 200 | 4000 | 4001, message: string } ``` | Return Code | Description | Message | | - | - | - | | None | Wallet locked | Empty string | | 200 | Site already authorized | The site is already in the whitelist | | 200 | User approved connection | User allowed the request. | | 4000 | Duplicate authorization request pending | Authorization requests are being processed, please do not resubmit | | 4001 | User rejected connection | User rejected the request | ## Legacy: sendTrx via window.tronLink ```typescript if (window.tronLink.ready) { const tronweb = tronLink.tronWeb; const fromAddress = tronweb.defaultAddress.base58; const toAddress = "TDvSsdrNM5eeXNL3czpa6AxLDHZA9nwe9K"; const tx = await tronweb.transactionBuilder.sendTrx(toAddress, 10, fromAddress); try { const signedTx = await tronweb.trx.sign(tx); await tronweb.trx.sendRawTransaction(signedTx); } catch (e) {} } ``` ## Legacy: multiSign via window.tronLink ```typescript if (window.tronLink.ready) { const tronweb = tronLink.tronWeb; const toAddress = "TDvSsdrNM5eeXNL3czpa6AxLDHZA9nwe9K"; const activePermissionId = 2; const tx = await tronweb.transactionBuilder.sendTrx( toAddress, 10, { permissionId: activePermissionId} ); try { const signedTx = await tronweb.trx.multiSign(tx, undefined, activePermissionId); await tronweb.trx.sendRawTransaction(signedTx); } catch (e) {} } ``` ## Legacy: signMessageV2 via window.tronLink ```typescript if (window.tronLink.ready) { const tronweb = tronLink.tronWeb; try { const message = "0x01EF"; const signedString = await tronweb.trx.signMessageV2(message); } catch (e) {} } ``` ## Legacy: wallet_watchAsset via window.tronLink ```typescript // Add TRC10 if (window.tronLink.ready) { const tronweb = tronLink.tronWeb; try { tronweb.request({ method: 'wallet_watchAsset', params: { type: 'trc10', options: { address: '1002000' }, }, }); } catch (e) {} } // Add TRC20 if (window.tronLink.ready) { const tronweb = tronLink.tronWeb; try { tronweb.request({ method: 'wallet_watchAsset', params: { type: 'trc20', options: { address: 'TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9' }, }, }); } catch (e) {} } // Add TRC721 if (window.tronLink.ready) { const tronweb = tronLink.tronWeb; try { tronweb.request({ method: 'wallet_watchAsset', params: { type: 'trc721', options: { address: 'TVtaUnsgKXhTfqSFRnHCsSXzPiXmm53nZt' }, }, }); } catch (e) {} } ``` ## Legacy: wallet_switchEthereumChain via tronLink.request ```javascript try { await tronLink.request({ method: 'wallet_switchEthereumChain', params: [{chainId: '0x2b6653dc'}] }); } catch (e) {} ``` --- # Passively Receiving Messages from the TronLink Plugin TronLink supports the TRON mainnet and testnets (Shasta, Nile). Developers can listen for the event messages dispatched by TronLink in their DApp to detect the currently selected network and the currently active account. Let's learn it with a simple example. ```html TronLink Events Demo ``` The remainder of this page documents each listener individually, including return values and screenshots. --- ## Detect TronLink (TIP-6963) Event identifier: `TIP6963:announceProvider` ### Overview TronLink announces its provider object via the TIP-6963 protocol. Listen for `TIP6963:announceProvider` and dispatch `TIP6963:requestProvider` to safely discover the wallet without polluting the global namespace or polling `window.tron`. The provider returned by the announce event is the same instance as `window.tron`. For the full TIP-6963 specification, see [Proactively Request TronLink Plugin Features](./active-requests.md#get-tronlink-provider-via-tip-6963). ### Technical Specification #### Code Example ```javascript let tron; window.addEventListener('TIP6963:announceProvider', (e) => { if (e.detail.info && e.detail.info.name === 'TronLink') { tron = e.detail.provider; // === window.tron console.log('TronLink successfully detected!'); } }); // Ask already-loaded wallets to re-announce themselves window.dispatchEvent(new Event('TIP6963:requestProvider')); ``` If `tron` is still undefined after dispatching the request, TronLink is not installed — prompt the user to install it. ## Account Change Message Message identifier: `accountsChanged` ### Overview This message is generated in the following situations: 1. User logs in 2. User switches account 3. User locks the wallet 4. Wallet auto-locks due to timeout ### Technical Specification #### Code Example ```typescript window.tron.on('accountsChanged', (accountArray) => { // handler logic console.log('got accountsChanged event', accountArray) }) ``` #### Return Value ```typescript ['your_current_account_address'] ``` ##### Return Value Examples 1. When the user logs in: ```javascript ['TZ5XixnRyraxJJy996Q1sip85PHWuj4793'] ``` (Current account) 2. When the user switches accounts: ```javascript ['TRKb2nAnCBfwxnLxgoKJro6VbyA6QmsuXq'] ``` (Newly selected account) 3. When the wallet is locked or auto-locked: ```javascript [] ``` > **Legacy (not recommended):** [setAccount (3.x)](#account-switch-setaccount) ## Network Change Message Message identifier: `chainChanged` ### Overview Developers can listen to this message to detect network changes. This message is generated when: 1. The user changes the network. ### Technical Specification #### Code Example ```typescript window.tron.on('chainChanged', ({chainId}) => { // handler logic console.log('got chainChanged event', chainId) }) ``` #### Return Value ```typescript { chainId: string; } ``` Currently supported `chainId` values: - Mainnet: `0x2b6653dc` - Shasta Testnet: `0x94a9059e` - Nile Testnet: `0xcd8690dc` **Note:** Chain IDs are case-sensitive. > **Legacy (not recommended):** [setNode (3.x)](#network-switch-setnode) or [tabReply (3.x)](#network-initialization-tabreply) ## TronLink Service Availability Message Message identifier: `connect` ### Overview If TronLink and the `window.tron` object are available, this event will be emitted once after the provider finishes initialization (it includes the case where the provider reconnects after a `disconnect`). **Timing caveat:** the event is emitted shortly (~100ms) after the extension finishes initializing. If your DApp registers the listener *after* the extension has already initialized — e.g. when the DApp loads via TIP-6963 `requestProvider` on a slow / late-mounted page — the `connect` event may have already fired and the listener will not be called. In that case, treat the provider's existence as the "connected" signal and fall back to `tron.tronWeb?.ready` for the current state. ### Technical Specification #### Code Example ```typescript window.tron.on('connect', ({chainId}) => { // handler logic console.log('got connect event', chainId) }) ``` > **Legacy (not recommended):** [connectWeb (postMessage)](#user-proactively-connected-website-message) or [acceptWeb (postMessage)](#user-confirmed-connection-message) ## Website Disconnect Message Message identifier: `disconnect` ### Overview The TIP-1193 spec defines `disconnect` to fire with a `ProviderRpcError` when the provider disconnects from all chains. **Current TronLink behavior:** TronLink does **not** currently emit `disconnect` on its provider. When the user disconnects a site (or the wallet is locked), the disconnect is surfaced via `accountsChanged` with an empty array (`[]`). Treat that as the disconnect signal until TronLink implements `disconnect`. ### Technical Specification #### Code Example ```typescript // Reserved by the spec — listener is harmless to register, but will not fire today. window.tron.on('disconnect', (providerRpcError) => { console.error(providerRpcError); // { code: 4900, message: 'Disconnected' } }) ``` > **Legacy (not recommended):** [disconnectWeb (postMessage)](#user-disconnected-website-message) or [rejectWeb (postMessage)](#user-rejected-connection-message) --- ## Legacy Compatibility Messages The remaining messages are dispatched via `window.postMessage`. The content received by a DApp is a `MessageEvent` — see the [MessageEvent MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent) for the event shape. The following four messages are retained for compatibility with version 3.x and will be deprecated in future versions: 1. User rejected connection → `rejectWeb` 2. User disconnected the website → `disconnectWeb` 3. User confirmed connection → `acceptWeb` 4. User proactively connected the website → `connectWeb` ### User Rejected Connection Message Message identifier: `rejectWeb` Generated when: 1. A DApp requests connection and the user rejects it in the popup. ![TronLink connection popup with the user clicking Reject, generating the rejectWeb event](../images/plugin-wallet_rejectWeb.jpg){ width="350" } Developers can listen for this message: ```typescript window.addEventListener('message', function (e) { if (e.data.message && e.data.message.action == "rejectWeb") { // handler logic console.log('got rejectWeb event', e.data) } }) ``` ### User Disconnected Website Message Message identifier: `disconnectWeb` Generated when: 1. The user manually disconnects the website. ![TronLink connected-sites screen with the user clicking Disconnect on the listed DApp, generating the disconnectWeb event](../images/plugin-wallet_disconnectWeb.jpg){ width="350" } Developers can listen for this message: ```typescript window.addEventListener('message', function (e) { if (e.data.message && e.data.message.action == "disconnectWeb") { // handler logic console.log('got disconnectWeb event', e.data) } }) ``` ### User Confirmed Connection Message Message identifier: `acceptWeb` Generated when: 1. The user confirms the connection request. ![TronLink connection popup with the user clicking Accept, generating the acceptWeb event](../images/plugin-wallet_acceptWeb.jpg){ width="350" } Developers can listen for this message: ```typescript window.addEventListener('message', function (e) { if (e.data.message && e.data.message.action == "acceptWeb") { // handler logic console.log('got acceptWeb event', e.data) } }) ``` ### User Proactively Connected Website Message Message identifier: `connectWeb` Generated when: 1. The user proactively connects to the website. ![TronLink connected-sites screen with the user clicking Connect on a previously authorized DApp, generating the connectWeb event](../images/plugin-wallet_connectWeb.jpg){ width="350" } Developers can listen for this message: ```typescript window.addEventListener('message', function (e) { if (e.data.message && e.data.message.action == "connectWeb") { // handler logic console.log('got connectWeb event', e.data) } }) ``` --- ## Deprecated 3.x Events (Mainchain / Sidechain) The following events were used by TronLink 3.x to detect the active network and account via raw `window.postMessage`. They have been superseded by the modern `accountsChanged` / `chainChanged` listeners on `window.tron` and are kept here for reference only — new integrations should not use them. ### Network Initialization (`tabReply`) Fired once after page load when TronLink finishes initializing. Inspect `e.data.message.data.data.node.chain` to determine whether the wallet is on the mainchain (`'_'`) or a sidechain (any other identifier). ```javascript window.addEventListener('message', function (e) { if (e.data.message && e.data.message.action == "tabReply") { if (e.data.message.data.data.node.chain == '_') { console.log("tronLink currently selects the main chain"); } else { console.log("tronLink currently selects the side chain"); } } }); ``` ### Account Switch (`setAccount`) Fired when the user switches the active account. ```javascript window.addEventListener('message', function (e) { if (e.data.message && e.data.message.action == "setAccount") { console.log("current address:", e.data.message.data.address); } }); ``` ### Network Switch (`setNode`) Fired when the user changes the network. Same `chain == '_'` convention as `tabReply`. ```javascript window.addEventListener('message', function (e) { if (e.data.message && e.data.message.action == "setNode") { if (e.data.message.data.node.chain == '_') { console.log("tronLink currently selects the main chain"); } else { console.log("tronLink currently selects the side chain"); } } }); ``` --- # Upcoming implementation: compatibility adaptation for the v field in the signature returned after Ledger signing For Ledger signing (including all transaction signatures, message signatures, and EIP-712 signatures), a new technical measure will be rolled out in the coming months. The trailing `01` in the `signature` field will be replaced with `1c`, and `00` will be replaced with `1b`. This change ensures that the last two bytes of Ledger account signatures are consistent with those of regular account signatures. ### Transaction Signing The following is a successfully signed Ledger transaction body: ```text { "txID": "......", .....other property, "signature": [ "......01" ] } ``` It will become: ```text { "txID": "......", .....other property, "signature": [ "......1c" ] } ``` When broadcasting, the DApp will use the modified transaction body. ### Message Signing The DApp sends a message signing request: ```javascript tron.tronWeb.trx.sign('.....unsign_string'); ``` or ```javascript tron.tronWeb.trx.signMessageV2('.....unsign_string'); ``` Actual signature hash returned by Ledger: ```text ......50f400 ``` Modified hash: ```text ......50f41b ``` ### EIP-712 Signing The DApp sends an EIP-712 signing request: ```javascript const types = { Person: [ { name: 'name', type: 'string', }, { name: 'wallet', type: 'address', }, ], Mail: [ { name: 'from', type: 'Person', }, { name: 'to', type: 'Person', }, { name: 'contents', type: 'string', }, ], }; const primaryType = 'Mail'; const domain = { name: 'Ether Mail', version: '1', chainId: '0x2b6653dc', verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', }; const message = { from: { name: 'Cow', wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', }, to: { name: 'Bob', wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', }, contents: 'Hello, Bob!', }; await tron.tronWeb.trx._signTypedData(domain, types, message); ``` Actual signature hash returned by Ledger: ```text .....7ee700 ``` Modified hash: ```text ......7ee71b ``` --- # MCP Server TronLink ## Overview **GitHub**: [https://github.com/TronLink/mcp-server-tronlink](https://github.com/TronLink/mcp-server-tronlink) **mcp-server-tronlink** is a production-ready Model Context Protocol (MCP) server that enables AI agents (Claude, GPT, etc.) to interact with the TRON blockchain through natural language. Built on `@tronlink/tronlink-mcp-core`, it provides **55 tools** in `list_tools` — **52 core tools** registered by [`tronlink-mcp-core`](tronlink-mcp-core.md) + **3 wallet management tools** registered locally by this server's [`src/wallet-tools.ts`](https://github.com/TronLink/mcp-server-tronlink/blob/main/src/wallet-tools.ts) — across two complementary operation modes. **Key Highlights:** - Dual-mode architecture: **Playwright** (browser automation) + **Direct API** (on-chain operations) - 32 built-in Flow Recipes with pre-checks and dependency resolution - Non-custodial local transaction signing via encrypted `agent-wallet` - Multi-signature management with real-time WebSocket monitoring - Gas-free TRC20 transfers via GasFree service integration --- ## Architecture ```mermaid flowchart TD Agent["AI Agent
(Claude Desktop / Claude Code)"] Server["TronLink MCP Server"] PW["Playwright Mode
TronLinkSessionManager
(browser automation + extension UI)"] API["Direct API Mode"] OnChain["TronLinkOnChainCapability (14 tools)"] Multi["TronLinkMultiSigCapability (5 tools)"] GasFree["TronLinkGasFreeCapability (3 tools)"] Util["Utility Capabilities
Build · StateSnapshot · TRON Crypto"] Flow["Flow Recipes
(32 built-in, pre-checked)"] Ext["TronGrid API / Multi-Sig Service / GasFree Service"] Chain["TRON Blockchain"] Agent -- "MCP Protocol — stdio / JSON-RPC 2.0" --> Server Server --> PW Server --> API Server --> Util Server --> Flow API --> OnChain API --> Multi API --> GasFree PW --> Ext OnChain --> Ext Multi --> Ext GasFree --> Ext Ext --> Chain ``` Both modes can run simultaneously and tools are auto-enabled based on configuration. --- ## Dual-Mode Operation ### Mode 1: Playwright (Browser Automation) Controls the TronLink Chrome extension via Playwright Chromium. Ideal for **E2E testing, UI validation, and DApp interaction**. **Capabilities:** - Launch browser with `--load-extension` flag for TronLink - Auto-detect extension ID from Chrome API - Multi-tab tracking with automatic role classification (extension / notification / dapp / other) - DOM-based state extraction (TRON address, TRX balance, network detection) - Screenshot capture with base64 encoding - Automatic browser dialog handling (alerts, confirms, prompts) **27 Playwright tools include:** `tl_launch`, `tl_cleanup`, `tl_navigate`, `tl_click`, `tl_type`, `tl_screenshot`, `tl_accessibility_snapshot`, `tl_describe_screen`, etc. > The "27" is **`52 core − 22 chain/multisig/gasfree − 3 mode-agnostic (run_steps, list_flows, set_context) = 27`**. `tl_clipboard`, `tl_keyboard`, `tl_scroll`, etc. are Playwright-mode UI tools and counted in the 27. ### Mode 2: Direct API + Wallet Management Operates directly against TronGrid / multi-sig / GasFree REST APIs — no browser required. Ideal for **account queries, transfers, swaps, staking, multi-sig management, and runtime wallet hot-swap**. **28 tools** = 22 mode-2 API tools (from core) + 3 mode-agnostic core tools + 3 wallet management tools (from this server). Grouping: | Group | Tools | Source | Description | |-------|-------|--------|-------------| | On-Chain | 14 | core | Transfer, stake, swap, query, multisig setup | | Multi-Signature | 5 | core | Permission query, tx submit, WebSocket monitoring | | GasFree | 3 | core | Zero-gas TRC20 transfers | | Wallet Management | 3 | **this server** (`src/wallet-tools.ts`) | List wallets, auto-create a wallet, switch the active wallet | | Mode-agnostic | 3 | core | `tl_run_steps`, `tl_list_flows`, `tl_set_context` — invoked from either mode | > **Why the breakdown differs from the architecture diagram.** The architecture node only enumerates capability classes (`OnChainCapability`, `MultiSigCapability`, `GasFreeCapability`). Wallet management lives outside the capability interface — it's registered by `src/wallet-tools.ts` and hot-swaps wallets into running capabilities via the `onWalletSwap` callback in [`src/index.ts`](https://github.com/TronLink/mcp-server-tronlink/blob/main/src/index.ts). --- ## Core Components ### 1. TronLinkSessionManager Full browser lifecycle management: | Method | Description | |--------|-------------| | `launch()` | Initialize browser with TronLink extension | | `getExtensionState()` | Extract wallet state from UI | | `navigateToUrl()` | Navigate to a specific URL | | `navigateToNotification()` | Open TronLink notification popup | | `screenshot()` | Capture current UI state | | `getTrackedPages()` | List all open browser tabs | | `cleanup()` | Graceful shutdown of all resources | **Screen Detection:** Auto-detects 15 TronLink screens: `home`, `login`, `settings`, `send`, `receive`, `sign`, `broadcast`, `assets`, `address_book`, `node_management`, `dapp_list`, `create_wallet`, `import_wallet`, `notification`, `unknown`. ### 2. TronLinkOnChainCapability (14 Tools) Direct API wrapper for TronGrid: **Query Operations:** - `getAddress()` — Get the TRON address from encrypted local `agent-wallet` - `getAccount()` — Balance, bandwidth, energy, permissions - `getTokens()` — TRC10 and TRC20 token balances - `getTransaction()` — Transaction details by txID - `getHistory()` — Transaction history with pagination - `getStakingInfo()` — Staking status (frozen amounts, votes, unfreezing) **Transaction Operations:** - `send()` — Transfer TRX, TRC10, or TRC20 tokens - `stake()` — Freeze/unfreeze TRX for bandwidth or energy (Stake 2.0) - `resource()` — Delegate/undelegate bandwidth or energy - `swap()` — Token swap via SunSwap V2 - `swapV3()` — Token swap via SunSwap V3 Smart Router **Multi-Sig Operations:** - `setupMultisig()` — Configure multi-sig permissions - `createMultisigTx()` — Create unsigned multi-sig transaction - `signMultisigTx()` — Sign multi-sig transaction ### 3. TronLinkMultiSigCapability (5 Tools) REST + WebSocket API for TRON multi-signature service: - `queryAuth()` — Query multi-sig permissions (owner/active, thresholds, weights) - `submitTransaction()` — Submit signed transaction (auto-broadcast when threshold reached) - `queryTransactionList()` — List transactions with filtering - `connectWebSocket()` — Real-time transaction monitoring - `disconnectWebSocket()` — Stop monitoring **Implementation:** HmacSHA256 signature generation for API auth, UUID-based request signing, supports both Nile testnet and Mainnet credentials. ### 4. TronLinkGasFreeCapability (3 Tools) Zero-gas TRC20 transfers via GasFree service: - `getAccount()` — Query eligibility, supported tokens, daily quota - `getTransactions()` — Query gas-free transaction history - `send()` — Send TRC20 with zero gas fee ### 5. Wallet Management (3 Tools) Runtime wallet management via `@bankofai/agent-wallet` (encrypted `local_secure` storage): - `tl_wallet_list` — List all wallets with IDs, types, active status, and TRON addresses - `tl_wallet_create` — Auto-generate an encrypted wallet and attach it to the running MCP session - `tl_wallet_set_active` — Switch the active wallet by ID (hot-swaps into all capabilities) If no wallet exists at startup, the server prompts two paths: call `tl_wallet_create` to auto-generate one, or create one manually via CLI and set `AGENT_WALLET_PASSWORD`. The auto-create path generates a random password, saves it to `~/.agent-wallet/runtime_secrets.json`, creates an encrypted `main` wallet, and enables the running session to use it. ### 6. TRON Cryptography Utils Pure cryptographic functions — no external service calls: ```text signTransaction() raw_data_hex → 65-byte signature (via agent-wallet) base58CheckEncode() Payload → base58check address base58CheckDecode() TRON address → 21-byte payload addressToHex() T-address → 0x41... hex hexToAddress() 0x41... → T-address ``` Uses `@noble/curves` (secp256k1 ECDSA) and `@noble/hashes` (Keccak-256, SHA256). Private keys are never exposed — all signing is done through the encrypted `agent-wallet`. --- ## Flow Recipes (32 Built-In) Pre-configured multi-step workflows with dependency checks and parameter templates. ### Playwright Flows | Flow | Description | |------|-------------| | `switchNetworkFlow` | Switch to Mainnet/Nile/Shasta | | `enableTestNetworksFlow` | Enable testnet visibility | | `transferTrxFlow` | TRX transfer via UI | | `transferTokenFlow` | Token transfer via UI | ### On-Chain Flows (11) | Flow | Description | |------|-------------| | `chainCheckBalanceFlow` | Query balance | | `chainTransferTrxFlow` | TRX transfer with pre-checks | | `chainTransferTrc20Flow` | TRC20 transfer with pre-checks | | `chainStakeFlow` | Stake TRX | | `chainUnstakeFlow` | Unstake TRX | | `chainGetStakingFlow` | Query staking info | | `chainDelegateResourceFlow` | Delegate bandwidth/energy | | `chainUndelegateResourceFlow` | Undelegate resources | | `chainSetupMultisigFlow` | Setup multi-sig permissions | | `chainCreateMultisigTxFlow` | Create unsigned multi-sig tx | | `chainSwapV3Flow` | SunSwap V3 token swap | ### Multi-Sig Flows (6) | Flow | Description | |------|-------------| | `multisigQueryAuthFlow` | Query permissions | | `multisigListTransactionsFlow` | List pending transactions | | `multisigMonitorFlow` | WebSocket real-time monitoring | | `multisigStopMonitorFlow` | Stop monitoring | | `multisigSubmitTxFlow` | Submit signed transaction | | `multisigCheckFlow` | Full status check | ### GasFree Flows (3) | Flow | Description | |------|-------------| | `gasfreeCheckAccountFlow` | Query eligibility | | `gasfreeTransactionHistoryFlow` | Query history | | `gasfreeSendFlow` | Gas-free TRC20 transfer | --- ## Configuration ### Environment Variables **Playwright Mode:** | Variable | Description | |----------|-------------| | `TRONLINK_EXTENSION_PATH` | TronLink extension build directory | | `TRONLINK_SOURCE_PATH` | Enable build capability | | `TL_MODE` | `e2e` (test) or `prod` (production) | | `TL_HEADLESS` | Browser headless mode | | `TL_SLOW_MO` | Playwright slow-motion delay (ms) | **TronGrid API:** | Variable | Description | |----------|-------------| | `TL_TRONGRID_URL` | Full-node API URL | | `TL_TRONGRID_API_KEY` | API key (required for Mainnet). Free tier ≈ 100k requests/day at ~5 QPS; paid tiers raise QPS, daily quota, and add billing. Quotas and headers change over time — see [TronGrid Pricing](https://www.trongrid.io/pricing) and the dashboard for current values, and inspect `X-Ratelimit-*` response headers in your own runtime. Hitting the limit returns HTTP 429 (mapped to `TL_CHAIN_QUERY_FAILED`, retryable). For long-running agents, set up billing alerts at 50% / 80% / 95% of your plan. | | `TL_SUNSWAP_ROUTER` | SunSwap V2 router address. **No built-in default** — pin to the current router; the value in the example below is **effective as of 2026-05** (Mainnet). Source: [docs.sun.io](https://docs.sun.io). When SunSwap publishes a new router, set this env var rather than waiting on a docs/code change. | | `TL_SUNSWAP_V3_ROUTER` | SunSwap V3 smart router address. Same rules as V2. | | `TL_WTRX_ADDRESS` | WTRX contract address. Mainnet WTRX is `TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR`. Effective as of 2026-05. | **Wallet (`agent-wallet`):** | Variable | Description | |----------|-------------| | `AGENT_WALLET_PASSWORD` | Encryption password (optional if using `tl_wallet_create`; required for manual or existing wallets) | | `AGENT_WALLET_DIR` | Custom wallet storage directory | | `TL_OWNER_WALLET_ID` | Owner wallet ID for multisig signing | | `TL_COSIGNER_WALLET_ID` | Co-signer wallet ID for multisig | **Multi-Signature Service:** | Variable | Description | |----------|-------------| | `TL_MULTISIG_BASE_URL` | API base URL | | `TL_MULTISIG_SECRET_ID` | Project credential | | `TL_MULTISIG_SECRET_KEY` | HmacSHA256 signing key | | `TL_MULTISIG_CHANNEL` | Channel/project name | **GasFree Service:** | Variable | Description | |----------|-------------| | `TL_GASFREE_BASE_URL` | Service URL | | `TL_GASFREE_API_KEY` | API key | | `TL_GASFREE_API_SECRET` | API secret | ### Integration Options **1. Project-Level MCP Config (`.mcp.json`)** Auto-detected by Claude Code: ```json { "mcpServers": { "tronlink": { "command": "node", "args": ["dist/index.js"], "cwd": ".", "env": { "TL_TRONGRID_URL": "https://nile.trongrid.io" } } } } ``` If no wallet exists yet, startup shows two paths: - Auto-create in the running MCP session: call `tl_wallet_create` - Manual setup: create the wallet locally with `agent-wallet start local_secure --generate --wallet-id main`, then set `AGENT_WALLET_PASSWORD` and restart If you choose auto-create, the server generates a random password, saves it to `~/.agent-wallet/runtime_secrets.json`, creates an encrypted `main` wallet, and continues with the current session. For a ready-to-use Nile setup with the common fields already filled, you can extend the config like this: ```json { "mcpServers": { "tronlink": { "command": "node", "args": ["dist/index.js"], "cwd": ".", "env": { "TRONLINK_EXTENSION_PATH": "/path/to/tronlink-extension/dist", "TL_MODE": "prod", "TL_HEADLESS": "false", "TL_TRONGRID_URL": "https://nile.trongrid.io", "AGENT_WALLET_PASSWORD": "your-wallet-password", "TL_SUNSWAP_ROUTER": "TKzxdSv2FZKQrEqkKVgp5DcwEXBEKMg2Ax", "TL_SUNSWAP_V3_ROUTER": "TB6xBCixqRPUSKiXb45ky1GhChFJ7qrfFj", "TL_MULTISIG_BASE_URL": "https://apinile.walletadapter.org", "TL_MULTISIG_SECRET_ID": "TEST", "TL_MULTISIG_SECRET_KEY": "TESTTESTTEST", "TL_MULTISIG_CHANNEL": "test", "TL_GASFREE_BASE_URL": "https://open-test.gasfree.io/nile/", "TL_GASFREE_API_KEY": "your_gasfree_api_key", "TL_GASFREE_API_SECRET": "your_gasfree_api_secret" } } } } ``` If you only need direct API tools and do not need browser automation, you can keep the same structure and omit the Playwright-related fields: ```json { "mcpServers": { "tronlink": { "command": "node", "args": ["dist/index.js"], "cwd": ".", "env": { "TL_TRONGRID_URL": "https://nile.trongrid.io", "AGENT_WALLET_PASSWORD": "your-wallet-password", "TL_MULTISIG_BASE_URL": "https://apinile.walletadapter.org", "TL_MULTISIG_SECRET_ID": "TEST", "TL_MULTISIG_SECRET_KEY": "TESTTESTTEST", "TL_MULTISIG_CHANNEL": "test", "TL_GASFREE_BASE_URL": "https://open-test.gasfree.io/nile/", "TL_GASFREE_API_KEY": "your_gasfree_api_key", "TL_GASFREE_API_SECRET": "your_gasfree_api_secret" } } } } ``` **2. Claude Desktop** Edit `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json { "mcpServers": { "tronlink": { "command": "node", "args": ["/absolute/path/to/dist/index.js"], "env": { ... } } } } ``` **3. Claude Code Global Settings** Edit `~/.claude/settings.json` or `.claude/settings.json`. **4. Any MCP Client** Supports stdio transport protocol — compatible with any MCP-compliant client. --- ## Project Structure ```text mcp-server-tronlink/ ├── src/ │ ├── index.ts # Server entry, config, capability registration │ ├── wallet.ts # Encrypted wallet loading and password handling │ ├── wallet-tools.ts # Wallet list/create/switch tools │ ├── session-manager.ts # Browser lifecycle (TronLinkSessionManager) │ ├── capabilities/ │ │ ├── on-chain.ts # 14 on-chain operations (TronGrid) │ │ ├── multisig.ts # 5 multi-sig operations (REST + WS) │ │ ├── gasfree.ts # 3 gas-free transfer operations │ │ ├── build.ts # Extension webpack build │ │ ├── state-snapshot.ts # UI state extraction │ │ └── tron-crypto.ts # Address derivation, signing, Base58 │ └── flows/ │ ├── index.ts # Flow registry (32 recipes) │ ├── switch-network.ts # Network switching flows │ ├── transfer-trx.ts # Transfer flows │ ├── multisig.ts # 6 multi-sig flows │ ├── onchain.ts # 11 on-chain flows │ └── gasfree.ts # 3 gas-free flows ├── dist/ # Compiled output ├── .mcp.json # MCP configuration ├── .env.example # Environment variable reference ├── package.json ├── tsconfig.json └── README.md ``` --- ## Dependencies Pinned to the `package.json` of `mcp-server-tronlink@0.1.1`. Re-verify when bumping major versions of the wallet, MCP, or crypto libraries. | Package | Version | Purpose | |---------|---------|---------| | `@noble/curves` | ^2.0.1 | secp256k1 ECDSA signing | | `@noble/hashes` | ^2.0.1 | Keccak-256, SHA256 | | `@tronlink/tronlink-mcp-core` | ^0.1.0 | Core MCP server framework | | `playwright` | ^1.49.0 | Browser automation | | `@bankofai/agent-wallet` | ^2.3.0 | Encrypted local wallet management (`local_secure`) — pinned, not `latest`, to keep wallet behavior reproducible | | `ws` | ^8.18.0 | WebSocket (multi-sig monitoring) | --- ## Tool Contract & Side Effects **Input/output schemas and error contract.** Each tool's input/output schema and the structured error envelope are defined by the underlying framework — see [TronLink MCP Core](tronlink-mcp-core.md#error-codes) for the SSOT error code table (`code` / `retryable` / `hint` / triggered_by). Every response carries `meta.schemaVersion`; field meanings are stable within a major version. Agents should branch on `error.code` and `error.retryable`, never on the human-readable `message`. **Per-tool input schemas are discoverable at runtime.** Every tool's parameters are Zod-validated in core and exposed as a JSON `inputSchema` via the MCP `list_tools` method, so a client can enumerate names, types, and required fields without reading this page. The tables below summarize tools by capability; `list_tools` is the authoritative, machine-readable source. **Side-effect classification.** Classify before calling; never auto-retry a write whose outcome is uncertain. | Side effect | Examples | | --- | --- | | **Read-only** (Network Read) | `tl_chain_get_account`, `tl_chain_get_tx`, `tl_gasfree_get_account`, `tl_wallet_list`, screen/state reads | | **Remote Write** (signs / changes remote state) | `tl_chain_send`, `tl_chain_stake`, `tl_chain_swap_v3`, `tl_multisig_submit_tx`, transfers, delegation | - **Pre-checks:** all transaction tools validate (balances, reverts, resource burn) before execution. - **Human-in-the-loop:** write tools sign with the encrypted local `agent-wallet`; in browser-mode flows the user approves in the TronLink UI. Treat every Remote Write tool as requiring confirmation in production. - **Retry:** read-only tools are safe to retry; Remote Write tools must not be auto-retried unless proven idempotent. ### Selected tool schemas (inline mirror) These are **docs-side mirrors** of the most critical tool inputs — useful when an agent is writing a tool-call call site without an MCP session open. Runtime `list_tools` remains the authoritative source: the schemas there carry full Zod metadata (descriptions, `default`, etc.) plus `meta.schemaVersion`. Fields below are derived from `@tronlink/tronlink-mcp-core` `src/mcp-server/schemas.ts` and follow JSON Schema Draft 7. The full set of 52 tool schemas is **not** reproduced here — see core for the SSOT. > **Parity is enforced.** `scripts/check_doc_schema_parity.py` (run on push, PR, and daily via [`check-doc-schema-parity.yml`](https://github.com/xueyuanying/docs/blob/main/.github/workflows/check-doc-schema-parity.yml)) diffs the top-level field set + required-flag set of every block below against the live `schemas.ts`. Upstream rename or required→optional drift fails CI. #### `tl_chain_send` — **Remote Write** ```json { "type": "object", "required": ["to", "amount"], "properties": { "to": { "type": "string", "description": "Recipient TRON address (T-prefix, 34 chars)" }, "amount": { "type": "string", "description": "Amount to send (e.g. \"1.5\" for TRX, or token amount string)" }, "token_type": { "type": "string", "enum": ["TRX", "TRC10", "TRC20"], "description": "Default: TRX" }, "token_id": { "type": "string", "description": "TRC10 token ID (required when token_type=TRC10)" }, "contract_address": { "type": "string", "description": "TRC20 contract address (required when token_type=TRC20)" }, "memo": { "type": "string", "description": "Optional transaction memo" } } } ``` #### `tl_chain_swap_v3` — **Remote Write** (when `action=execute`) ```json { "type": "object", "required": ["action", "from_token", "to_token", "amount"], "properties": { "action": { "type": "string", "enum": ["estimate", "execute"], "description": "estimate = quote-only (Network Read); execute = sign & broadcast (Remote Write)" }, "from_token": { "type": "string", "description": "Source token address or 'TRX' for native" }, "to_token": { "type": "string", "description": "Target token address or 'TRX' for native" }, "amount": { "type": "string", "description": "Input amount in token units" }, "fee_tier": { "type": "number", "enum": [500, 3000, 10000], "description": "Pool fee tier in bps: 500=0.05%, 3000=0.3%, 10000=1% (default: 3000)" }, "slippage": { "type": "number", "description": "Slippage tolerance percent (default: 0.5). See 'Swap safety' above — never accept an unstated default for production execution." }, "sqrt_price_limit": { "type": "string", "description": "Optional price limit for partial fills (advanced)" } } } ``` #### `tl_chain_stake` — **Remote Write** ```json { "type": "object", "required": ["action", "amount_trx"], "properties": { "action": { "type": "string", "enum": ["freeze", "unfreeze"], "description": "freeze locks TRX for resources; unfreeze starts the 14-day withdrawal" }, "amount_trx": { "type": "number", "description": "Amount of TRX to freeze / unfreeze" }, "resource": { "type": "string", "enum": ["BANDWIDTH", "ENERGY"], "description": "Resource type (default: BANDWIDTH)" } } } ``` #### `tl_multisig_submit_tx` — **Remote Write** ```json { "type": "object", "required": ["address", "transaction"], "properties": { "address": { "type": "string", "description": "Signer address submitting this transaction" }, "function_selector": { "type": "string", "description": "e.g. 'transfer(address,uint256)' (optional)" }, "expire_time": { "type": "number", "description": "Expiration timestamp in ms (default: now + 24h)" }, "transaction": { "type": "object", "description": "Signed transaction { raw_data, signature[] }. Each contract entry may carry a Permission_id." } } } ``` The full `transaction` shape (raw_data → contract[] → parameter, etc.) is in [`tronlink-mcp-core` `schemas.ts`](https://github.com/TronLink/tronlink-mcp-core/blob/main/src/mcp-server/schemas.ts) — too verbose to mirror inline. #### `tl_gasfree_send` — **Remote Write** ```json { "type": "object", "required": ["to", "amount", "contract_address"], "properties": { "to": { "type": "string", "description": "Recipient TRON address" }, "amount": { "type": "string", "description": "Token amount in token units (e.g. '10.5')" }, "contract_address": { "type": "string", "description": "TRC20 token contract address" } } } ``` #### `tl_chain_get_account` — **Network Read** ```json { "type": "object", "properties": { "address": { "type": "string", "description": "TRON address to query (default: configured wallet)" } } } ``` #### `tl_evaluate` — **High-risk / Destructive** (Playwright-only) ```json { "type": "object", "required": ["script"], "properties": { "script": { "type": "string", "description": "JavaScript expression to evaluate in the controlled browser page context. Return value is serialized." }, "timeout": { "type": "number", "description": "Timeout in ms (default: 30000)" } } } ``` Reminder: `tl_evaluate` runs arbitrary JS in the controlled Playwright browser. Disable it from the MCP host's tool allowlist unless strictly needed (see Security Model below). --- ## Security Model | Aspect | Implementation | |--------|----------------| | Key storage | Encrypted local wallet managed by `@bankofai/agent-wallet` | | Key exposure | No key material logged to stderr | | Signing | Local transaction signing via encrypted `agent-wallet` — plain-text private keys are not supported | | Pre-checks | All transactions validate before execution | | Git safety | Config files in `.gitignore` prevent accidental commits | | Default network | Nile testnet with safe defaults | ### Security Boundaries | Boundary | Guarantee | Agent / operator obligation | |---|---|---| | **Prompt injection** | Tool inputs are consumed verbatim as call arguments. The server never concatenates tool inputs into a prompt re-sent to an LLM. Strings retrieved from chain or third-party APIs (account memos, contract revert reasons, transaction notes) **may contain attacker-controlled text** — treat them as untrusted. | Do not let the agent auto-route Remote Write tools off prose returned from a read. Always require structured fields (`txId`, `code`, `retryable`) for branching. | | **Outbound host allowlist (SSRF)** | The server only originates HTTPS to the four configured endpoints: `TL_TRONGRID_URL` (TronGrid), `TL_MULTISIG_BASE_URL`, `TL_GASFREE_BASE_URL`, and SunSwap routers via TronWeb. Tools never accept user-supplied URLs that get fetched verbatim. | Pin these env vars to known hosts in production; do not let LLM input populate any `*_BASE_URL`. | | **API key handling (token passthrough)** | `TL_TRONGRID_API_KEY`, `TL_MULTISIG_SECRET_KEY`, `TL_GASFREE_API_SECRET` are read from env at startup and used only on the outbound leg. They are **not** returned in any tool response, error `details`, or Knowledge Store record. The server does not accept Authorization headers from MCP clients and forward them upstream. | Audit env capture in your MCP host config (some hosts log env); store secrets in the host's secret manager, not in `.mcp.json` committed to git. | | **Browser JS execution** | `tl_evaluate` runs arbitrary JavaScript in the controlled Playwright browser context. This is a **High-risk / Destructive** primitive — it can read DOM, click invisible elements, exfiltrate state, and bypass UI HITL. | Disable `tl_evaluate` from the MCP host's tool allowlist for any agent that does not strictly require it. Never expose it to a remote/multi-user MCP deployment. | | **HITL bypass** | Direct-API tools (`tl_chain_send`, `tl_chain_swap_v3`, etc.) sign with the local encrypted `agent-wallet` and broadcast **without** a TronLink browser approval. The `agent-wallet` password is the only barrier. | Hold `AGENT_WALLET_PASSWORD` outside the agent's reach. For production, prefer `mcp-tronlink-signer` (browser approval) over Direct-API for any tool that moves funds. | | **Confused deputy** | Tools operate under the local `agent-wallet` identity, not the calling user's identity. There is no per-call authorization scope. | One MCP session = one wallet identity; do not multiplex multiple end users through the same server. | | **Transport** | stdio transport; the server does not bind a network listener. | Do not wrap this server behind a public HTTP transport without re-introducing auth and rate limiting. | #### Swap safety (`tl_chain_swap` / `tl_chain_swap_v3`) Swaps are **Remote Write** and execute against a public DEX router, so they are exposed to **price slippage** and **front-running / MEV** (e.g. sandwich attacks): the realized output can be worse than quoted if the pool moves between quote and execution. - **Always bound the trade with a minimum-output / slippage limit.** Inspect the `tl_chain_swap_v3` input schema via `list_tools` (the `SwapV3Params` shape) for the exact slippage / minimum-output field names — do **not** rely on an unstated default, and treat a missing or zero minimum-output as unsafe. - **Quote immediately before executing.** Get a fresh quote/route (e.g. Skills `tron-swap` `swap-quote` / `swap-route`), pick a tolerance you accept, and pass it explicitly. - **Pin the router.** `TL_SUNSWAP_V3_ROUTER` has no built-in default; a stale or wrong router can route funds unexpectedly. Set it to the current SunSwap V3 router (see Environment Variables). - **No auto-retry.** A failed/uncertain swap is a Remote Write — confirm on-chain before re-issuing (`TL_CHAIN_SWAP_FAILED` is not retryable). #### Multi-sig credential hygiene (`TL_MULTISIG_SECRET_ID` / `TL_MULTISIG_SECRET_KEY`) These are HMAC-SHA256 API credentials for the multi-sig service (not on-chain keys), but they authorize transaction submission — treat them as secrets. - **Per-environment isolation.** Use distinct credentials for Mainnet vs testnet and per project/channel (`TL_MULTISIG_CHANNEL`). Never reuse a Mainnet secret in a test/staging MCP host. - **Storage.** Keep them in the host's secret manager / env, never in a `.mcp.json` committed to git (see the token-passthrough boundary above). - **Rotation.** Rotate `TL_MULTISIG_SECRET_KEY` periodically, and immediately if a host or log may have captured it. The server reads credentials from env at startup, so rotation on this side is an **env update + server restart**; issue/revoke the credential itself through the multi-sig service console. - **Revocation.** If a secret is suspected leaked, revoke it at the service and rotate before the next signing session — an exposed secret lets an attacker submit transactions to the multi-sig queue. - **Least privilege.** Scope each credential to the channel/project it needs; do not share one secret across unrelated agents. #### Disabling `tl_evaluate` If your workflow does not require running arbitrary JS in the controlled browser, take it off the tool surface explicitly. The exact key depends on the host: ```jsonc // Claude Code — .claude/settings.json (project) or ~/.claude/settings.json (user) { "permissions": { "deny": ["mcp__tronlink__tl_evaluate"] } } ``` ```jsonc // Claude Desktop — claude_desktop_config.json { "mcpServers": { "tronlink": { "command": "node", "args": ["dist/index.js"], "disabledTools": ["tl_evaluate"] } } } ``` ```jsonc // Generic MCP client: prefer client-side filtering via list_tools. // Filter the server's announced tools before exposing them to the model; // drop any tool whose name is "tl_evaluate". ``` Verify after restart with `list_tools` — `tl_evaluate` should not appear. The same pattern works for `tl_seed_contract` / `tl_seed_contracts` (e2e-only contract deployment). ### Wallet Secret Storage The Direct-API path signs with a local encrypted wallet managed by `@bankofai/agent-wallet`. Two paths exist for unlocking it; pick deliberately. **Path A — Manual (recommended for production).** Create the wallet out-of-band, set `AGENT_WALLET_PASSWORD` via the MCP host's secret manager, and start the server. The password lives only in process memory; nothing is written by this server. **Path B — Auto-create (convenience for local dev).** If no wallet exists at startup and the agent calls `tl_wallet_create`, the server: 1. Generates a random password. 2. Writes it in plaintext to `~/.agent-wallet/runtime_secrets.json` so a restart can reuse the wallet. 3. Creates an encrypted `main` wallet at `~/.agent-wallet/` (override with `AGENT_WALLET_DIR`). | Aspect | Behavior | |---|---| | **File** | `~/.agent-wallet/runtime_secrets.json` (plaintext JSON containing the password) | | **Recommended permissions** | `chmod 600` — the file is created under the user's `$HOME`, but no umask hardening is enforced. Verify after first run. | | **Git safety** | `~/.agent-wallet/` is outside any repo by default. If you point `AGENT_WALLET_DIR` inside a repo, add it to `.gitignore` explicitly. | | **Knowledge Store redaction** | The Knowledge Store auto-redacts `password`, `mnemonic`, `private_key`, `seed` fields in tool inputs / outputs. It does **not** read or sanitize `runtime_secrets.json`. The file is independent of the Knowledge Store. | | **Logs / stderr** | The auto-generated password is not logged. The file path may appear in startup output. | | **Backups** | Backing up `~/.agent-wallet/` without also protecting `runtime_secrets.json` defeats encryption-at-rest. Either back up both encrypted, or back up the encrypted wallet and re-set the password by hand on restore. | **Production guidance.** - Prefer Path A. Source `AGENT_WALLET_PASSWORD` from the host's secret manager (Claude Desktop env, vault, etc.). - If you must use Path B (e.g., ephemeral CI), set `AGENT_WALLET_DIR` to a tmpfs path that is destroyed at job end. - For any tool that moves real funds, prefer `mcp-tronlink-signer` (browser approval, no on-disk password) over Direct-API. **How to enforce Path A (doc-side, today).** 1. Provision the wallet **before** the server boots. From a separate shell: ```bash agent-wallet start local_secure --generate --wallet-id main # take note of the password you supply here — it is the only copy ``` 2. Inject the password via the MCP host's secret manager so it lands in the server's `env` at launch: ```jsonc // .mcp.json — secret comes from host env, not from this file { "mcpServers": { "tronlink": { "command": "node", "args": ["dist/index.js"], "env": { "AGENT_WALLET_PASSWORD": "${AGENT_WALLET_PASSWORD}" } } } } ``` 3. **Do not let the agent call `tl_wallet_create`.** Disable it the same way as `tl_evaluate` above (Claude Code `permissions.deny`, Claude Desktop `disabledTools`, or client-side `list_tools` filtering on the name `tl_wallet_create`). 4. Verify on first launch: `list_tools` should not include `tl_wallet_create`, and the absence of `~/.agent-wallet/runtime_secrets.json` confirms Path B did not run. **Tracking issue (code-side).** A `--no-auto-create` / `AGENT_WALLET_DISABLE_AUTOCREATE=1` flag that makes the server fail-loud at startup when no wallet exists is the proper long-term fix; until that ships upstream, the doc-side enforcement above is the defense in depth. --- ## Typical Usage Scenarios 1. **Wallet Operations** — List wallets, auto-create or switch the active wallet, check balance, send transfers 2. **DApp Testing** — Launch browser, connect wallet, sign transactions, verify state 3. **On-Chain Trading** — Direct API swaps, staking, token transfers without browser 4. **Multi-Sig Workflows** — Set up permissions, submit/monitor transactions 5. **Gas-Free Operations** — TRC20 transfers without TRX balance requirements 6. **Infrastructure Testing** — Contract deployment, fixture management, mock servers --- ## Quick Start ```bash # 1. Build npm install && npm run build # 2. Configure .mcp.json (Nile testnet example) # Add: # TL_TRONGRID_URL=https://nile.trongrid.io # # 3. If no wallet exists, choose one path: # Option A: call tl_wallet_create after startup # Option B: create one locally, then set AGENT_WALLET_PASSWORD # 4. Use with Claude Code # "Check my TRX balance" # "Send 10 TRX to TAddress..." # "Swap 100 TRX for USDT on SunSwap V3" ``` ## Version & License - **Package:** `@tronlink/mcp-server-tronlink` v0.1.1 - **License:** MIT — `SPDX-License-Identifier: MIT` - **Changelog / releases:** [https://github.com/TronLink/mcp-server-tronlink/releases](https://github.com/TronLink/mcp-server-tronlink/releases) — no GitHub-tagged releases yet; pre-1.0 ships via `package.json` version bumps. Track changes by commit until the first tag. ### Compatibility & migration policy - **Semver.** Pre-1.0: a **minor** bump (0.x → 0.y) may introduce breaking changes; a **patch** bump (0.1.x → 0.1.y) will not change tool names, input schemas, `error.code` values, or `meta.schemaVersion` semantics. Post-1.0: standard semver — major-only breaking changes. - **Stable contracts** (won't change in a patch): - Tool names (`tl_chain_send`, `tl_chain_swap_v3`, `tl_multisig_*`, `tl_gasfree_*`, `tl_evaluate`, etc.) - `error.code` enum (SSOT: [TronLink MCP Core — Error Codes](tronlink-mcp-core.md#error-codes)) - `error.retryable` semantics - `meta.schemaVersion` major component - Required env var names (`TL_TRONGRID_URL`, `TL_MULTISIG_SECRET_KEY`, `AGENT_WALLET_PASSWORD`, …) - **Volatile contracts** (may change at any time): - Prose `message` text, log line formats, stderr output - Internal Knowledge Store keys (consumers should not parse them) - Pre-check error detail strings (branch on `code`, not on `details.reason`) - **Deprecation window.** When a tool or input field is deprecated, the next minor release retains the old form alongside the new one for at least one minor cycle, with a `meta.deprecated` flag exposed via `list_tools`; removal lands no earlier than the cycle after that. - **Verifying after upgrade.** Re-call `list_tools` and confirm the tool names + `inputSchema` you depend on are still present before resuming the workflow. Compare `meta.schemaVersion` against the value cached at session start. --- # TronLink MCP Core ## Overview **GitHub**: [https://github.com/TronLink/tronlink-mcp-core](https://github.com/TronLink/tronlink-mcp-core) **@tronlink/tronlink-mcp-core** is the foundational framework library for building TronLink MCP (Model Context Protocol) servers. It is not a standalone application — consumers must implement the `ISessionManager` interface and inject capabilities to create a working server. **Key Highlights:** - Interface-driven, pluggable architecture with **9 capability interfaces** - **52 pre-defined tool handlers** with Zod-validated schemas - Built-in Knowledge Store for cross-session learning and step replay - Flow Recipe system for codifying multi-step workflows - Standardized response format with a documented error-code table (`code` / `retryable` / `hint`) - Dual-mode support: Playwright (UI automation) + Direct API (on-chain) --- ## Architecture ```mermaid flowchart TD Agent["AI Agent (Claude, GPT, etc.)"] Server["MCP Server Instance
createMcpServer()"] Tools["52 tl_* tool handlers"] KS["Knowledge Store
(cross-session persistence)"] FR["Flow Registry
(recipe management)"] Disc["Discovery utilities"] SM["ISessionManager Interface (consumer implements)
session lifecycle · page/tab mgmt · capability injection · e2e/prod"] Caps["Capability System — 9 pluggable interfaces
Build · Fixture · Chain · ContractSeeding · StateSnapshot · MockServer · OnChain · MultiSig · GasFree"] PW["Optional: Playwright
(browser automation)"] Agent -- "MCP Protocol — stdio / JSON-RPC 2.0" --> Server Server --> Tools Server --> KS Server --> FR Server --> Disc Server --> SM SM --> Caps SM -. "when configured" .-> PW ``` **Design Principles:** 1. **Interface-driven** — All major components use interfaces for extensibility 2. **Composition over inheritance** — Capabilities are injected, not inherited 3. **Singleton patterns** — SessionManager, KnowledgeStore, FlowRegistry use global holders 4. **Pre-checks** — On-chain tools validate before execution 5. **Data redaction** — Knowledge store automatically masks sensitive fields --- ## Relationship to mcp-server-tronlink | Aspect | tronlink-mcp-core | mcp-server-tronlink | |--------|-------------------|---------------------| | Type | Core library (framework) | Standalone MCP server | | Role | Defines interfaces, tools, protocol | Concrete implementation | | Usage | Import via npm, extend | Direct CLI invocation | | Extensibility | 9 pluggable capability interfaces | Pre-configured capabilities | | Dependency | None (it IS the dependency) | Depends on @tronlink/tronlink-mcp-core | **mcp-server-tronlink is a consumer of tronlink-mcp-core.** The core defines WHAT tools exist; the server provides HOW they work. --- ## ISessionManager Interface The critical interface that consumers must implement (25+ methods): ### Session Lifecycle ```typescript hasActiveSession(): boolean getSessionId(): string getSessionState(): SessionState getSessionMetadata(): SessionMetadata launch(input: LaunchInput): Promise cleanup(): Promise ``` ### Page Management ```typescript getPage(): Page setActivePage(page: Page): void getTrackedPages(): TrackedPage[] classifyPageRole(page: Page): PageRole getContext(): ContextInfo ``` ### Extension State ```typescript getExtensionState(): Promise ``` ### Accessibility References ```typescript setRefMap(map: Map): void getRefMap(): Map clearRefMap(): void resolveA11yRef(ref: string): any ``` ### Navigation ```typescript navigateToHome(): Promise navigateToSettings(): Promise navigateToUrl(url: string): Promise navigateToNotification(): Promise waitForNotificationPage(timeoutMs?: number): Promise ``` ### Screenshots ```typescript screenshot(options?: ScreenshotOptions): Promise ``` ### Capability Getters (9 optional) ```typescript getBuildCapability(): BuildCapability | undefined getFixtureCapability(): FixtureCapability | undefined getChainCapability(): ChainCapability | undefined getContractSeedingCapability(): ContractSeedingCapability | undefined getStateSnapshotCapability(): StateSnapshotCapability | undefined getMockServerCapability(): MockServerCapability | undefined getOnChainCapability(): OnChainCapability | undefined getMultiSigCapability(): MultiSigCapability | undefined getGasFreeCapability(): GasFreeCapability | undefined ``` ### Environment ```typescript getEnvironmentMode(): 'e2e' | 'prod' setContext(context: string, options?: any): Promise getContextInfo(): ContextInfo ``` --- ## 9 Capability Interfaces Each capability is optional and independently injectable: ### 1. BuildCapability Build TronLink extension from source. ```typescript interface BuildCapability { build(options?: BuildOptions): Promise } ``` ### 2. FixtureCapability Manage wallet state JSON (default, onboarding, custom presets). ```typescript interface FixtureCapability { applyPreset(preset: string): Promise getAvailablePresets(): string[] exportState(): Promise importState(state: WalletState): Promise } ``` ### 3. ChainCapability Control local TRON node (tron-quickstart, etc.). ```typescript interface ChainCapability { startNode(): Promise stopNode(): Promise getNodeStatus(): Promise fundAccount(address: string, amount: number): Promise } ``` ### 4. ContractSeedingCapability Deploy smart contracts (TRC20/721/1155/10/multisig/staking/energy_rental). ```typescript interface ContractSeedingCapability { seedContract(type: string, options?: any): Promise seedContracts(specs: ContractSpec[]): Promise getContractAddress(name: string): string | undefined listContracts(): ContractInfo[] } ``` ### 5. StateSnapshotCapability Extract wallet state from UI. ```typescript interface StateSnapshotCapability { getSnapshot(): Promise // screen, address, balance, energy, bandwidth } ``` ### 6. MockServerCapability Mock API server for isolated testing. ```typescript interface MockServerCapability { start(config?: MockConfig): Promise stop(): Promise addRoute(route: MockRoute): void getRequests(): MockRequest[] } ``` ### 7. OnChainCapability Direct on-chain operations via TronGrid REST API. ```typescript interface OnChainCapability { getAddress(): Promise getAccount(address?: string): Promise getTokens(address?: string): Promise send(params: SendParams): Promise getTransaction(txId: string): Promise getHistory(params?: HistoryParams): Promise stake(params: StakeParams): Promise getStakingInfo(address?: string): Promise resource(params: ResourceParams): Promise swap(params: SwapParams): Promise swapV3(params: SwapV3Params): Promise setupMultisig(params: MultisigSetupParams): Promise createMultisigTx(params: MultisigTxParams): Promise signMultisigTx(params: SignMultisigParams): Promise } ``` ### 8. MultiSigCapability Multi-signature service integration (REST + WebSocket). ```typescript interface MultiSigCapability { queryAuth(address: string): Promise submitTransaction(params: SubmitParams): Promise queryTransactionList(params: ListParams): Promise connectWebSocket(params: WsParams): Promise disconnectWebSocket(): Promise } ``` ### 9. GasFreeCapability Gas-free TRC20 transfer service. ```typescript interface GasFreeCapability { getAccount(address: string): Promise getTransactions(params: GasFreeTxParams): Promise send(params: GasFreeSendParams): Promise } ``` --- ## 52 Tool Definitions All tools use the `tl_` prefix. Organized into 13 categories: > **Schema SSOT.** Every tool's `inputSchema` is generated from the Zod schemas in [`src/mcp-server/schemas.ts`](https://github.com/TronLink/tronlink-mcp-core/blob/main/src/mcp-server/schemas.ts) and surfaced at runtime via `list_tools`. Tables below list **name + one-line description only**; for parameter types, required fields, enums, and defaults, either call `list_tools` against a running server, or read the Zod source. The downstream [`mcp-server-tronlink` page](mcp-server-tronlink.md#selected-tool-schemas-inline-mirror) mirrors JSON Schema for a curated set of 7 high-impact tools (`tl_chain_send`, `tl_chain_swap_v3`, `tl_chain_stake`, `tl_multisig_submit_tx`, `tl_gasfree_send`, `tl_chain_get_account`, `tl_evaluate`) — useful when writing a call site without an MCP session open. The SSOT is still this package. ### 1. Session Management (2) | Tool | Description | |------|-------------| | `tl_launch` | Launch browser with extension | | `tl_cleanup` | Close browser and services | ### 2. State & Discovery (4) | Tool | Description | |------|-------------| | `tl_get_state` | Get wallet state | | `tl_describe_screen` | Screen description with state + testIds + a11y + screenshot | | `tl_list_testids` | List data-testid attributes | | `tl_accessibility_snapshot` | Get accessibility tree with refs (e1, e2...) | ### 3. Navigation (4) | Tool | Description | |------|-------------| | `tl_navigate` | Navigate to TronLink screen or URL | | `tl_switch_to_tab` | Switch tabs by role/URL | | `tl_close_tab` | Close tabs | | `tl_wait_for_notification` | Wait for confirmation popups | ### 4. UI Interaction (6) | Tool | Description | |------|-------------| | `tl_click` | Click elements (a11yRef, testId, selector) | | `tl_type` | Type text into inputs | | `tl_wait_for` | Wait for element state | | `tl_scroll` | Scroll page/element | | `tl_keyboard` | Send keyboard events | | `tl_evaluate` | Execute JavaScript | ### 5. Screenshots & Clipboard (2) | Tool | Description | |------|-------------| | `tl_screenshot` | Capture screen | | `tl_clipboard` | Read/write clipboard | ### 6. Contract Deployment — e2e only (4) | Tool | Description | |------|-------------| | `tl_seed_contract` | Deploy single contract | | `tl_seed_contracts` | Deploy multiple contracts | | `tl_get_contract_address` | Query contract address | | `tl_list_contracts` | List deployed contracts | ### 7. Context Management (2) | Tool | Description | |------|-------------| | `tl_set_context` | Switch e2e/prod mode | | `tl_get_context` | Get context info | ### 8. Knowledge Store (4) | Tool | Description | |------|-------------| | `tl_knowledge_last` | Get last N steps | | `tl_knowledge_search` | Search history | | `tl_knowledge_summarize` | Generate recipe | | `tl_knowledge_sessions` | List sessions | ### 9. Flow Recipes (1) | Tool | Description | |------|-------------| | `tl_list_flows` | List/get flow recipes | ### 10. Batch Execution (1) | Tool | Description | |------|-------------| | `tl_run_steps` | Execute multiple steps sequentially | ### 11. On-Chain Operations (14) | Tool | Description | |------|-------------| | `tl_chain_get_address` | Get address from private key | | `tl_chain_get_account` | Query account details | | `tl_chain_get_tokens` | Query TRC10/TRC20 balances | | `tl_chain_send` | Send TRX/TRC10/TRC20 | | `tl_chain_get_tx` | Get transaction details | | `tl_chain_get_history` | Query tx history | | `tl_chain_stake` | Freeze/unfreeze TRX (Stake 2.0) | | `tl_chain_get_staking` | Query staking info | | `tl_chain_resource` | Delegate/undelegate resources | | `tl_chain_swap` | SunSwap V2 swap | | `tl_chain_swap_v3` | SunSwap V3 swap | | `tl_chain_setup_multisig` | Configure multisig permissions | | `tl_chain_create_multisig_tx` | Create unsigned multisig tx | | `tl_chain_sign_multisig_tx` | Sign multisig transaction | ### 12. Multi-Signature (5) | Tool | Description | |------|-------------| | `tl_multisig_query_auth` | Query multisig permissions | | `tl_multisig_submit_tx` | Submit signed tx | | `tl_multisig_list_tx` | List multisig transactions | | `tl_multisig_connect_ws` | Connect WebSocket | | `tl_multisig_disconnect_ws` | Disconnect WebSocket | ### 13. GasFree (3) | Tool | Description | |------|-------------| | `tl_gasfree_get_account` | Query eligibility & quota | | `tl_gasfree_get_transactions` | Query tx history | | `tl_gasfree_send` | Send with zero gas | --- ## Standardized Response Format All tools return a consistent structure: ```typescript // Success { ok: true, result: { /* tool-specific data */ }, meta: { timestamp: "2026-03-09T10:15:23.456Z", sessionId: "tl-1741504523", durationMs: 234 } } // Error { ok: false, error: { code: "TL_CLICK_FAILED", // stable code — see table below message: "Element not found", retryable: false, // explicit hint for agents hint: "Re-snapshot the page and retry with a fresh a11yRef.", details: { /* optional */ } }, meta: { timestamp, sessionId, durationMs, schemaVersion: "1.0" } } ``` ### Error Codes This is the single source of truth (SSOT) for error codes returned by every tool in this framework. Downstream servers (`mcp-server-tronlink`, `mcp-tronlink-signer`) inherit these codes and may extend them. `retryable` reflects framework-level safety; an agent **must** still apply the side-effect classification of the calling tool — never auto-retry a Remote Write whose outcome is unknown, regardless of `retryable`. | Code | Retryable | Hint | Typical trigger | |------|:---:|------|-----------------| | `TL_BUILD_FAILED` | false | Inspect build logs; fix source/config before retrying. | `tl_launch` with `BuildCapability` enabled | | `TL_SESSION_ALREADY_RUNNING` | false | Call `tl_cleanup` before launching a new session. | `tl_launch` while a session is active | | `TL_NO_ACTIVE_SESSION` | false | Call `tl_launch` first. | Any session-bound tool without a session | | `TL_LAUNCH_FAILED` | true | Transient browser/extension boot failure; retry once. | `tl_launch` | | `TL_INVALID_INPUT` | false | Fix the arguments; do not retry with the same payload. | Zod schema validation | | `TL_NAVIGATION_FAILED` | true | Page may still be transitioning; wait for the target screen and retry. | `tl_navigate`, `tl_switch_to_tab` | | `TL_TARGET_NOT_FOUND` | false | Refresh refs via `tl_accessibility_snapshot` and retry with a fresh ref. | `tl_click`, `tl_type`, `tl_wait_for` | | `TL_CLICK_FAILED` | true | Element may have re-rendered; refresh refs and retry once. | `tl_click` | | `TL_TYPE_FAILED` | true | Same as click — refresh refs and retry once. | `tl_type` | | `TL_WAIT_TIMEOUT` | true | Increase the timeout or wait on a different selector. | `tl_wait_for`, `tl_wait_for_notification` | | `TL_SCREENSHOT_FAILED` | true | Transient; retry. | `tl_screenshot`, `tl_describe_screen` | | `TL_CAPABILITY_NOT_AVAILABLE` | false | The session was launched without this capability; reconfigure and relaunch. | Any tool that requires an optional capability | | `TL_CHAIN_QUERY_FAILED` | true | TronGrid transient or rate-limited; back off and retry. | `tl_chain_get_*` | | `TL_CHAIN_SEND_FAILED` | false | Do **not** auto-retry. Verify on-chain state with `tl_chain_get_tx` before re-issuing. | `tl_chain_send`, `tl_chain_stake`, `tl_chain_resource` | | `TL_CHAIN_SWAP_FAILED` | false | Same as send — confirm the previous tx did not land before retrying. | `tl_chain_swap`, `tl_chain_swap_v3` | | `TL_GASFREE_QUERY_FAILED` | true | Transient; retry. | `tl_gasfree_get_account`, `tl_gasfree_get_transactions` | | `TL_GASFREE_SEND_FAILED` | false | Do **not** auto-retry; query the GasFree service for the latest status. | `tl_gasfree_send` | | `TL_MULTISIG_QUERY_FAILED` | true | Transient; retry. | `tl_multisig_query_auth`, `tl_multisig_list_tx` | | `TL_MULTISIG_SUBMIT_FAILED` | false | Do **not** auto-retry; the request may already have been accepted. | `tl_multisig_submit_tx` | | `TL_MULTISIG_WS_FAILED` | true | Reconnect the WebSocket. | `tl_multisig_connect_ws` | | `TL_INTERNAL_ERROR` | true | Generic framework failure; retry once and escalate with logs. | Any tool | Custom codes added by downstream servers must follow these rules: - Never reuse a code listed above with a different meaning. - `retryable` is required for every new code. - Meaning is stable within a major version. - New codes are documented in the consuming server's docs, not silently introduced. --- ## Knowledge Store Cross-session learning and step replay system. ### Storage Structure ```text test-artifacts/llm-knowledge/ ├── tl-1741504523/ │ ├── session.json │ └── steps/ │ ├── 2026-03-09T10-15-23-456Z-tl_click.json │ └── ... └── tl-1741504600/ └── ... ``` ### Features - **Auto-recording:** Every tool invocation is logged (timestamp, screen, target, result) - **Sensitive data redaction:** Passwords, mnemonics, private keys, seeds are automatically masked - **Search:** Query by tool name, screen, testId, accessibility name - **Summarization:** Generate reusable "recipes" from session history - **Session management:** List sessions with metadata --- ## Flow Recipe System Codifies common multi-step workflows with template parameters. ### FlowRecipe Structure ```typescript { id: "transfer_trx", name: "Send TRX", description: "Transfer TRX to another address", context: "both", // "playwright" | "api" | "both" preconditions: ["wallet unlocked"], params: [ { name: "recipient", description: "Target TRON address", required: true }, { name: "amount", description: "TRX amount", required: true } ], steps: [ { tool: "navigate", input: { target: "send" } }, { tool: "type", input: { testId: "address-input", text: "{{recipient}}" } }, { tool: "type", input: { testId: "amount-input", text: "{{amount}}" } }, { tool: "click", input: { a11yRef: "e5" } } ], tags: ["transfer", "basic"] } ``` ### FlowRegistry - `register(recipe)` — Register a new flow recipe - `get(id)` — Get recipe by ID - `list(filter?)` — List recipes by tag, context, or search - Singleton pattern via global holder --- ## Element Targeting (3 Methods) ```typescript // 1. Accessibility Reference (recommended) tl_click({ a11yRef: "e5" }) // 2. data-testid tl_click({ testId: "send-button" }) // 3. CSS Selector tl_click({ selector: ".confirm-btn" }) ``` Accessibility references come from `tl_accessibility_snapshot` output — the most reliable targeting method. --- ## Installation & Usage ### Install ```bash npm install @tronlink/tronlink-mcp-core ``` ### Basic Setup ```typescript import { createMcpServer, setSessionManager, type ISessionManager, } from '@tronlink/tronlink-mcp-core'; // 1. Implement ISessionManager class MySessionManager implements ISessionManager { // ... implement all 25+ methods } // 2. Register setSessionManager(new MySessionManager()); // 3. Create and start server const server = createMcpServer({ name: 'My TronLink Server', version: '1.0.0', }); await server.start(); ``` --- ## Project Structure ```text tronlink-mcp-core/ ├── src/ │ ├── index.ts # Public API exports │ ├── mcp-server/ │ │ ├── server.ts # createMcpServer() factory │ │ ├── session-manager.ts # ISessionManager interface │ │ ├── knowledge-store.ts # Persistent step recording │ │ ├── discovery.ts # Page inspection utilities │ │ ├── schemas.ts # Zod validation schemas │ │ ├── constants.ts # TIMEOUTS, LIMITS, URLs, SCREENS │ │ ├── tools/ │ │ │ ├── definitions.ts # Tool registry & routing │ │ │ ├── run-tool.ts # Execution wrapper (auto-logs) │ │ │ ├── launch.ts # tl_launch │ │ │ ├── cleanup.ts # tl_cleanup │ │ │ ├── state.ts # tl_get_state │ │ │ ├── navigation.ts # Navigation tools │ │ │ ├── interaction.ts # UI interaction tools │ │ │ ├── discovery-tools.ts # Element discovery │ │ │ ├── screenshot.ts # Screenshot capture │ │ │ ├── on-chain.ts # 14 on-chain tools │ │ │ ├── multisig.ts # 5 multisig tools │ │ │ ├── gasfree.ts # 3 gasfree tools │ │ │ ├── knowledge.ts # Knowledge store tools │ │ │ ├── flows.ts # Flow listing │ │ │ ├── batch.ts # Batch execution │ │ │ └── helpers.ts # Shared utilities │ │ ├── types/ │ │ │ ├── responses.ts # McpResponse types │ │ │ ├── errors.ts # ErrorCodes enum │ │ │ ├── session.ts # Session types │ │ │ ├── step-record.ts # Step recording types │ │ │ ├── tool-inputs.ts # All tool input types │ │ │ └── tool-outputs.ts # All tool output types │ │ └── utils/ │ │ ├── response.ts # createSuccessResponse / createErrorResponse │ │ ├── errors.ts # extractErrorMessage │ │ └── zod-to-json-schema.ts # Zod → JSON Schema conversion │ ├── capabilities/ │ │ ├── types.ts # 9 capability interfaces │ │ └── context.ts # Environment config types │ ├── flows/ │ │ ├── types.ts # FlowRecipe, FlowParam types │ │ └── registry.ts # FlowRegistry class │ ├── launcher/ │ │ ├── extension-id-resolver.ts # Extension ID extraction │ │ └── extension-readiness.ts # Extension load detection │ └── utils/ │ └── index.ts # generateSessionId, etc. ├── dist/ # Compiled output + .d.ts ├── package.json ├── tsconfig.json └── README.md ``` --- ## Dependencies | Package | Version | Purpose | |---------|---------|---------| | `@modelcontextprotocol/sdk` | ^1.12.0 | MCP protocol implementation | | `zod` | ^3.23.0 | Input validation schemas | **Peer Dependencies (optional):** | Package | Version | Purpose | |---------|---------|---------| | `playwright` | ^1.49.0 | Browser automation (Playwright mode only) | | `@playwright/test` | ^1.49.0 | Test utilities | --- ## Build & Development ```bash npm run build # Compile TypeScript to dist/ npm run dev # Watch mode npm run test # Run Vitest tests npm run lint # ESLint npm run clean # Remove dist/ ``` **Output:** Compiled JavaScript + type definitions in `dist/`. --- ## Key Design Patterns 1. **Global Singletons** — `setSessionManager()` / `getSessionManager()`, `setKnowledgeStore()` / `getKnowledgeStore()`, `FlowRegistry.getInstance()` 2. **Composition over Inheritance** — Capabilities injected via getter methods, not class hierarchy 3. **Interface Segregation** — Each capability has a focused, minimal interface 4. **Pre-checks** — On-chain tools validate balances, permissions, quotas before sending transactions 5. **Data Redaction** — Knowledge store auto-masks password, mnemonic, private_key, seed fields 6. **Standardized Responses** — Consistent `{ ok, result/error, meta }` structure across all 52 tools 7. **Template System** — Flow recipes use `{{param}}` placeholders for reusable workflows ## Version & License - **Package:** `@tronlink/tronlink-mcp-core` v0.1.0 - **License:** MIT — `SPDX-License-Identifier: MIT` - **Changelog / releases:** [https://github.com/TronLink/tronlink-mcp-core/releases](https://github.com/TronLink/tronlink-mcp-core/releases) — no GitHub-tagged releases yet; pre-1.0 ships via `package.json` version bumps. Track changes by commit until the first tag. ### Compatibility & migration policy This package is **the SSOT** for tool schemas, error codes, and `meta.schemaVersion` consumed by `mcp-server-tronlink` and any downstream MCP server built on this core. The compatibility surface is therefore wider than a typical library: - **Semver.** Pre-1.0: a **minor** bump may change the `ISessionManager` interface, capability shapes, or `Tool[]` registration order; a **patch** will not. Post-1.0: standard semver — major-only breaking changes. - **Stable contracts** (won't change in a patch): - The `error.code` enum (the SSOT exported as `ERROR_CODES`) — adding a new code is non-breaking; renaming or removing one is breaking. - The `{ ok, result/error, meta }` response envelope and `meta.schemaVersion` major component. - Tool names and the **shape** of each tool's `inputSchema` (adding optional fields is non-breaking; renaming or making a field required is breaking). - The 9 capability interfaces (`OnChainCapability`, `MultiSigCapability`, …) — adding an optional method is non-breaking. - **Volatile contracts** (may change at any time): - Internal helper exports under `src/internal/*`, `Knowledge Store` keys, recipe-runner internals. - Pre-check error `details` strings (branch on `code`, not `details.reason`). - **Deprecation window.** A deprecated tool / field / capability method stays present for at least one minor cycle alongside its replacement, marked with `meta.deprecated` in `list_tools` output; removal lands no earlier than the cycle after. - **Adopting downstream.** Bump the `@tronlink/tronlink-mcp-core` peer / dependency only after re-running your downstream's `list_tools` snapshot test against the new core; assert `meta.schemaVersion` major matches the version your harness was written for. --- # TronLink Skills ## Overview **GitHub**: [https://github.com/TronLink/tronlink-skills](https://github.com/TronLink/tronlink-skills) **TronLink Wallet Skills** is an AI Agent skill set that provides complete TRON blockchain wallet and DeFi functionality through natural language. Designed for Claude Code, Cursor, OpenCode, Codex CLI, and other AI agents. **Key Highlights:** - **6 skills, 33 commands** covering wallet, token research, market data, swaps, resources, and staking - **Zero npm dependencies** — uses native Node.js 18+ `fetch` and `crypto`, no `npm install` needed - **TRON-specific domain knowledge** — dedicated handling of Energy + Bandwidth resource model - **Multi-platform support** — Claude Code, Cursor, OpenCode, Codex CLI, LangChain/CrewAI - **Read-only & safe** — all commands are query-only, no private keys or signing involved - **MCP server wrapper** for structured AI agent integration --- ## Why TronLink Skills? TRON has a fundamentally different fee model than EVM chains. Instead of unified gas, TRON uses **Energy** (for smart contracts) and **Bandwidth** (for all transactions). No existing AI agent skill properly covers: - TRON's unique resource model and cost optimization strategies - Stake 2.0 (freezing TRX to obtain resources and earn rewards) - Super Representative (SR) voting mechanics - DEX aggregation across SunSwap V2/V3 and Sun.io - The 14-day unfreezing wait period and its implications TronLink Skills fills this gap with deep TRON-specific domain knowledge. --- ## Architecture ```text Natural Language Input | v AI Agent (Claude Code / Cursor / OpenCode / Custom) | v tron_api.mjs (Node.js 18+, native fetch, zero dependencies) ├── Zero npm dependencies ├── TronGrid HTTP API (public or with API key) └── Tronscan API for token metadata | v Structured JSON → Agent interprets → Natural language response ``` --- ## The 6 Skills ### 1. tron-wallet (6 commands) Wallet queries and account information. | Command | Description | |---------|-------------| | `wallet-balance` | TRX balance and frozen amounts | | `token-balance` | Check TRC-20 token balance | | `wallet-tokens` | List all token holdings | | `tx-history` | Recent transaction history | | `account-info` | Full account details | | `validate-address` | Address format validation | **Features:** Handles both Base58Check (T...) and hex address formats, supports known token symbols, auto-converts decimals. **When NOT to use:** Sending TRX/tokens — these are read-only; use the [signer SDK](tronlink-signer.md) or [MCP Server TronLink](mcp-server-tronlink.md). For deep token-level analytics (rug-pull / liquidity locks), prefer `tron-token`. ### 2. tron-token (7 commands) Token research and security analysis. | Command | Description | |---------|-------------| | `token-info` | Metadata, supply, issuer, socials | | `token-search` | Find tokens by name/symbol | | `contract-info` | ABI, bytecode, verification status | | `token-holders` | Top holders and holder analysis | | `trending-tokens` | Highest volume tokens (24h) | | `token-rankings` | Sort by market cap, volume, holders, gainers/losers | | `token-security` | Security audit (honeypot, proxy, owner permissions) | **Features:** Detects rug pulls, analyzes holder concentration, checks liquidity locks. **When NOT to use:** Per-account balances or transaction history — that's `tron-wallet`. Real-time price/volume — that's `tron-market`. ### 3. tron-market (8 commands) Real-time market data and whale monitoring. | Command | Description | |---------|-------------| | `token-price` | Current price USD/TRX, 24h change | | `kline` | OHLCV candlestick data (1m to 1w intervals) | | `trade-history` | Recent DEX trades | | `dex-volume` | Buy/sell volume, trade count | | `whale-transfers` | Large transfers (configurable threshold) | | `large-transfers` | TRX whale activity | | `pool-info` | Liquidity pools (SunSwap V2/V3 TVL, APY) | | `market-overview` | TRON network stats (price, cap, volume, active accounts) | **Features:** Multi-DEX aggregation, smart money signal detection, K-line analysis. **When NOT to use:** Quotes or routes for swapping right now — that's `tron-swap` (which factors in slippage). Static token metadata — `tron-token`. ### 4. tron-swap (3 commands) DEX swap quotes and route optimization. | Command | Description | |---------|-------------| | `swap-quote` | Expected output, price impact, slippage | | `swap-route` | Optimal route across SunSwap V2/V3, Sun.io (multi-hop) | | `tx-status` | Track transaction status | **Features:** Aggregates liquidity from multiple sources, estimates Energy cost, handles multi-hop routes. **When NOT to use:** Executing the swap — quotes are read-only; the swap itself goes through [MCP Server TronLink](mcp-server-tronlink.md) (`tl_chain_swap_v3`) or the signer SDK. Historical trade data — `tron-market`. ### 5. tron-resource (6 commands) Energy & Bandwidth management — TRON-specific. | Command | Description | |---------|-------------| | `resource-info` | Current Energy/Bandwidth available and staked | | `estimate-energy` | Energy cost for smart contract calls | | `estimate-bandwidth` | Bandwidth cost (free daily allowance: 600) | | `energy-price` | Current SUN cost per Energy unit | | `energy-rental` | Query rental marketplace options | | `optimize-cost` | Personalized recommendation (freeze vs. rent vs. burn) | **Features:** Decision tree logic for cost optimization, tracks daily free bandwidth, calculates TRX burn equivalent. **When NOT to use:** Actually freezing TRX to acquire Energy/Bandwidth — that's a Remote Write; use the signer SDK / MCP Server. SR voting strategy after freezing — see `tron-staking`. ### 6. tron-staking (3 commands) Stake 2.0 queries and SR information. | Command | Description | |---------|-------------| | `sr-list` | List SRs with votes, block rate, APY | | `staking-info` | Frozen amount, votes, unclaimed rewards, pending unfreezes | | `staking-apy` | Calculate estimated annual yield | **Features:** Stake 2.0 status queries, APY calculation, SR commission tracking. **When NOT to use:** Performing the freeze/vote/unfreeze actions — those are Remote Writes; route through the signer SDK / MCP Server. Calculating Energy cost per operation — `tron-resource`. --- ## Skill ↔ MCP Tool Map `scripts/mcp_server.mjs` (the wrapper from [Method 2](#method-2-mcp-server)) exposes **25 of the 33 commands** as MCP tools — every signature, every input field, every output shape is generated from the same `tron_api.mjs` implementation, so the CLI and the MCP tool are guaranteed equivalent. The 8 CLI-only commands stay reachable through Method 1 (skill prompt) and Method 3 (direct CLI). Use this table when an agent needs to route a user request to a specific tool or when you're inspecting `tools/list` output. | Skill | CLI command | MCP tool name | Side effect | Retryable | |---|---|---|---|:---:| | `tron-wallet` | `wallet-balance` | `tron_wallet_balance` | Network Read | Yes | | `tron-wallet` | `token-balance` | `tron_token_balance` | Network Read | Yes | | `tron-wallet` | `wallet-tokens` | `tron_wallet_tokens` | Network Read | Yes | | `tron-wallet` | `tx-history` | `tron_tx_history` | Network Read | Yes | | `tron-wallet` | `account-info` | `tron_account_info` | Network Read | Yes | | `tron-wallet` | `validate-address` | `tron_validate_address` | Local (pure) | Yes | | `tron-token` | `token-info` | `tron_token_info` | Network Read | Yes | | `tron-token` | `token-search` | `tron_token_search` | Network Read | Yes | | `tron-token` | `contract-info` | — _(CLI only)_ | Network Read | Yes | | `tron-token` | `token-holders` | `tron_token_holders` | Network Read | Yes | | `tron-token` | `trending-tokens` | `tron_trending_tokens` | Network Read | Yes | | `tron-token` | `token-rankings` | `tron_token_rankings` | Network Read | Yes | | `tron-token` | `token-security` | `tron_token_security` | Network Read | Yes | | `tron-market` | `token-price` | `tron_token_price` | Network Read | Yes | | `tron-market` | `kline` | `tron_kline` | Network Read | Yes | | `tron-market` | `trade-history` | — _(CLI only)_ | Network Read | Yes | | `tron-market` | `dex-volume` | — _(CLI only)_ | Network Read | Yes | | `tron-market` | `whale-transfers` | `tron_whale_transfers` | Network Read | Yes | | `tron-market` | `large-transfers` | — _(CLI only)_ | Network Read | Yes | | `tron-market` | `pool-info` | — _(CLI only)_ | Network Read | Yes | | `tron-market` | `market-overview` | `tron_market_overview` | Network Read | Yes | | `tron-swap` | `swap-quote` | `tron_swap_quote` | Network Read | Yes | | `tron-swap` | `swap-route` | — _(CLI only)_ | Network Read | Yes | | `tron-swap` | `tx-status` | `tron_tx_status` | Network Read | Yes | | `tron-resource` | `resource-info` | `tron_resource_info` | Network Read | Yes | | `tron-resource` | `estimate-energy` | `tron_estimate_energy` | Network Read | Yes | | `tron-resource` | `estimate-bandwidth` | — _(CLI only)_ | Network Read | Yes | | `tron-resource` | `energy-price` | `tron_energy_price` | Network Read | Yes | | `tron-resource` | `energy-rental` | — _(CLI only)_ | Network Read | Yes | | `tron-resource` | `optimize-cost` | `tron_optimize_cost` | Network Read | Yes | | `tron-staking` | `sr-list` | `tron_sr_list` | Network Read | Yes | | `tron-staking` | `staking-info` | `tron_staking_info` | Network Read | Yes | | `tron-staking` | `staking-apy` | `tron_staking_apy` | Network Read | Yes | **Totals.** 33 CLI commands · 25 MCP tools · 8 CLI-only commands. Every command is read-only — no signing, no broadcast, no fund movement. To execute a transaction, route to [MCP Server TronLink](mcp-server-tronlink.md) (`tl_chain_*`) or [signer SDK](tronlink-signer.md) (`sendTrx`, `sendTrc20`, `sign*`). ### Intent → Skill → Tool Routing Pick a skill first by the **kind of question**, then a command by the **field the user asked about**. Common intents: | User says (intent) | Skill | Command / MCP tool | |---|---|---| | "What's the TRX balance of T…?" / "How many tokens in this wallet?" | `tron-wallet` | `wallet-balance` · `tron_wallet_balance` | | "Is `Tabcd…` a valid TRON address?" | `tron-wallet` | `validate-address` · `tron_validate_address` | | "Show recent transactions for T…" | `tron-wallet` | `tx-history` · `tron_tx_history` | | "Is this token safe / a honeypot?" | `tron-token` | `token-security` · `tron_token_security` | | "Who are the top holders of USDT?" | `tron-token` | `token-holders` · `tron_token_holders` | | "What's the contract ABI of …?" | `tron-token` | `contract-info` (CLI only) | | "What are the top-volume tokens today?" | `tron-token` | `trending-tokens` · `tron_trending_tokens` | | "What's TRX / USDT price?" | `tron-market` | `token-price` · `tron_token_price` | | "Show 1h K-line for SUN" | `tron-market` | `kline` · `tron_kline` | | "Recent SunSwap trades for USDT?" | `tron-market` | `trade-history` (CLI only) | | "What's the TVL of SUN/TRX pool?" | `tron-market` | `pool-info` (CLI only) | | "How much USDT will I get for 100 TRX?" | `tron-swap` | `swap-quote` · `tron_swap_quote` | | "What's the cheapest route TRX → JST?" | `tron-swap` | `swap-route` (CLI only) | | "Did transaction `0xabc…` succeed?" | `tron-swap` | `tx-status` · `tron_tx_status` | | "How much Energy / Bandwidth do I have?" | `tron-resource` | `resource-info` · `tron_resource_info` | | "Should I freeze, rent, or burn?" | `tron-resource` | `optimize-cost` · `tron_optimize_cost` | | "How much Energy does a USDT transfer cost?" | `tron-resource` | `estimate-energy` · `tron_estimate_energy` | | "Where can I rent Energy?" | `tron-resource` | `energy-rental` (CLI only) | | "List the current Super Representatives" | `tron-staking` | `sr-list` · `tron_sr_list` | | "What's my staking position?" | `tron-staking` | `staking-info` · `tron_staking_info` | | "If I stake 10000 TRX, what's my APY?" | `tron-staking` | `staking-apy` · `tron_staking_apy` | If the request implies **changing on-chain state** (transfer, swap execution, freeze, vote, claim), this skill set is the wrong layer — see the routing notes under each skill's "When NOT to use". ### ❌ When NOT to route here (negative examples) Skills are **read-only**. If the user intent implies a signed / Remote Write action, do **not** dispatch to a skill — the underlying command will succeed but only as a query/estimate, and the user's actual goal will go unfulfilled. Route to the signer SDK or `mcp-server-tronlink` instead: | User says (intent) | ❌ Wrong route (looks plausible, but read-only) | ✅ Correct route | |---|---|---| | "Send 100 TRX to `T…`" | `tron-wallet wallet-balance` then stop — this only checks the balance, never sends. | [signer SDK](tronlink-signer.md) `sendTrx` (HITL) or [`mcp-server-tronlink`](mcp-server-tronlink.md) `tl_chain_send` | | "Freeze 1000 TRX to get Energy" | `tron-resource optimize-cost` — this only computes the recommendation. | `mcp-server-tronlink` `tl_chain_stake` (Remote Write, HITL) | | "Vote 5000 votes for SR `T…`" | `tron-staking sr-list` — only reads the SR list, no vote is cast. | `mcp-server-tronlink` `tl_chain_stake` / signer SDK `signTransaction` | | "Approve USDT spending for the SunSwap router" | `tron-token token-info` / `contract-info` — pure metadata, no approval is broadcast. | [signer SDK](tronlink-signer.md) `signTransaction` or `mcp-server-tronlink` `tl_chain_send` | | "Swap 100 TRX for USDT now" | `tron-swap swap-quote` — only quotes price, never executes. | `mcp-server-tronlink` `tl_chain_swap_v3` (Remote Write, HITL, set `minOut`) | | "Claim my staking rewards" | `tron-staking staking-info` — only shows the pending balance. | `mcp-server-tronlink` `tl_chain_stake` (withdraw / claim) or signer SDK | **Heuristic.** If the user's verb is *send / freeze / unfreeze / vote / unvote / approve / swap (execute) / claim / sign / broadcast*, the answer never starts in this Skills set. Skills can still **precede** the write (quote, estimate cost, validate address, check balance) — just don't claim a Skills call finished the user's request. --- ## Recommended Skill Workflows ### Balance & Token Check ```text tron-wallet (check balance) → tron-wallet (list tokens) → tron-resource (check energy status) ``` ### Research & Swap Quote ```text tron-token (search) → tron-market (price/chart) → tron-resource (check energy) → tron-swap (get quote) ``` ### Staking Analysis ```text tron-wallet (check balance) → tron-staking (staking info) → tron-staking (APY estimate) → tron-staking (SR list) ``` ### Resource Optimization ```text tron-resource (check status) → tron-resource (estimate cost) → tron-resource (optimize-cost) ``` --- ## TRON Resource Model Reference ### Energy vs. Bandwidth | Resource | Consumed By | Free Allowance | How to Get | |----------|-------------|----------------|------------| | **Bandwidth** | ALL transactions | 600/day | Freeze TRX or burn TRX | | **Energy** | Smart contracts only | None | Freeze TRX, rent, or burn TRX | ### Cost Examples > **Effective as of 2026-05.** Energy figures shift with contract upgrades (notably USDT TRC-20) and network parameters; the bandwidth/energy unit prices used to derive the TRX-burned column also change. Treat these as **order-of-magnitude reference**, not contractual values. Source: TronGrid / Tronscan transaction samples and TRON network parameters. For runtime accuracy, call the `tron-resource` skill's `estimate-energy` / `estimate-bandwidth` commands. | Operation | Bandwidth | Energy | TRX Burned (no resources) | |-----------|-----------|--------|---------------------------| | TRX transfer | ~267 | 0 | 0 (within free limit) | | USDT transfer | ~345 | ~65,000 | ~13-27 TRX | | SunSwap swap | ~345 | ~65,000-200,000 | ~13-40 TRX | | Token approve | ~345 | ~30,000 | ~6-12 TRX | ### Stake 2.0 Key Facts - Freeze TRX → Get Energy or Bandwidth → Vote for SR → Earn rewards - Unfreezing has **14-day wait** before withdrawal - Votes reset if you unfreeze; must re-vote after re-freezing - 1 frozen TRX ≈ 4.5 Energy/day — **dynamic**: depends on total network stake; use `tron-resource estimate-energy` for the live value. Effective figure as of 2026-05. - Voting rewards claimable every 6 hours --- ## Integration Methods ### Method 1: Claude Code (Recommended) ```bash # Clone and use directly git clone cd tronlink-skills claude # Auto-discovers SKILL.md files ``` No `npm install` needed for read-only operations. ### Method 2: MCP Server ```bash # Register as MCP server claude mcp add tronlink -- node ~/.tronlink-skills/scripts/mcp_server.mjs # Provides 25 MCP tools callable by Claude Desktop / Claude Code # (see "Skill ↔ MCP Tool Map" above for the per-command mapping; 8 commands are CLI-only) ``` ### Method 3: Manual CLI ```bash # Direct command execution node scripts/tron_api.mjs wallet-balance --address TAddress... node scripts/tron_api.mjs token-price --token USDT node scripts/tron_api.mjs swap-quote --from TRX --to USDT --amount 100 ``` ### Method 4: Other AI Platforms | Platform | Integration | |----------|-------------| | **Cursor / Windsurf** | Clone repo, use MCP or direct skill reading | | **Codex CLI** | Symlink to `~/.agents/skills/tronlink-skills` | | **OpenCode** | Register plugin, symlink skills | | **LangChain / CrewAI** | Wrap `tron_api.mjs` as a Tool | #### Cursor (or Windsurf, same schema) `~/.cursor/mcp.json`: ```jsonc { "mcpServers": { "tronlink-skills": { "command": "node", "args": ["/absolute/path/to/tronlink-skills/scripts/mcp_server.mjs"] } } } ``` #### Codex CLI ```bash # Symlink the skills bundle into the agent's discovery path ln -s "$(pwd)/tronlink-skills" ~/.agents/skills/tronlink-skills # Verify discovery codex skills list | grep tron ``` #### OpenCode `~/.config/opencode/config.json`: ```jsonc { "plugins": { "tronlink-skills": { "path": "/absolute/path/to/tronlink-skills" } } } ``` #### LangChain / CrewAI (Python) ```python from langchain_core.tools import Tool import subprocess, json def tron_call(cmd: str) -> dict: out = subprocess.check_output( ["node", "scripts/tron_api.mjs", *cmd.split()], cwd="/absolute/path/to/tronlink-skills", ) return json.loads(out) tron_wallet_balance = Tool.from_function( func=lambda addr: tron_call(f"wallet-balance --address {addr}"), name="tron_wallet_balance", description="TRX balance and frozen amounts. Address is a TRON Base58 (T...) string.", ) ``` ### Quick Setup Script ```bash # Auto-install for all AI environments bash install.sh # Clean uninstall bash uninstall.sh ``` --- ## Configuration ### Environment Variables ```bash # Optional: TronGrid API key for higher rate limits export TRONGRID_API_KEY="your-api-key" # Optional: Switch network (default: mainnet) export TRON_NETWORK="mainnet" # or "shasta" / "nile" ``` ### Network Support | Network | URL | Use Case | |---------|-----|----------| | Mainnet | https://api.trongrid.io | Production | | Shasta | https://api.shasta.trongrid.io | Testing | | Nile | https://nile.trongrid.io | Testing | ### Built-In Token Shortcuts | Symbol | Contract Address | |--------|------------------| | TRX | Native (no contract) | | USDT | TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t | | USDC | TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8 | | WTRX | TNUC9Qb1rRpS5CbWLmNMxXBjyFoydXjWFR | | BTT | TAFjULxiVgT4qWk6UZwjqwZXTSaGaqnVp4 | | JST | TCFLL5dx5ZJdKnWuesXxi1VPwjLVmWZZy9 | | SUN | TSSMHYeV2uE9qYH95DqyoCuNCzEL1NvU3S | | WIN | TLa2f6VPqDgRE67v1736s7bJ8Ray5wYjU7 | --- ## Project Structure ```text tronlink-skills/ ├── README.md # Main documentation ├── package.json # Node.js manifest ├── install.sh # Auto-install for all AI environments ├── uninstall.sh # Clean uninstall │ ├── scripts/ │ ├── tron_api.mjs # Main CLI (33 commands, zero dependencies) │ └── mcp_server.mjs # MCP protocol server wrapper │ ├── skills/ # Skill definitions (auto-discovered) │ ├── tron-wallet/SKILL.md │ ├── tron-token/SKILL.md │ ├── tron-market/SKILL.md │ ├── tron-swap/SKILL.md │ ├── tron-resource/SKILL.md │ └── tron-staking/SKILL.md │ ├── docs/ │ ├── claude-integration-guide.md # 3 integration methods │ ├── resource-model.md # Energy & Bandwidth deep dive │ ├── staking-guide.md # Stake 2.0 & APY explanation │ └── integration-guide.sh │ ├── .claude-plugin/ # Claude Code plugin config ├── .cursor-plugin/ # Cursor IDE plugin config ├── .opencode/ # OpenCode config ├── .codex/ # Codex CLI setup ├── .claude/ # Pre-configured test commands ├── _meta.json # Metadata for skill registries └── LICENSE # MIT ``` --- ## Dependencies | Dependency | Required? | Purpose | |------------|-----------|---------| | Node.js >= 18 | Yes | Runtime (native fetch, crypto) | | npm install | Not needed | All operations work without any npm dependencies | --- ## Security Model | Aspect | Implementation | |--------|----------------| | Read-only design | All commands are queries — no private keys, no signing, no fund movements | | Side effects | Every command is **Network Read**: it calls public APIs but changes no state. All commands are safe to retry; no human-in-the-loop confirmation is needed | | No secrets required | Only optional TRONGRID_API_KEY for higher rate limits | | Rate limits | Public TronGrid API; use TRONGRID_API_KEY for higher limits | | Error handling | Failures are query errors: rate limit (retryable, back off), network errors (retryable), invalid address/parameters (not retryable — fix the input). To execute a transaction (transfer, swap, stake), use the [signer SDK](tronlink-signer.md) or [MCP Server TronLink](mcp-server-tronlink.md) — these skills never sign or broadcast | --- ## Address Format Support Both formats are supported and auto-normalized across all commands: | Format | Example | Description | |--------|---------|-------------| | Base58Check | `T...` (34 chars) | Standard display format | | Hex | `41...` (42 hex chars) | Internal representation | --- ## Key Design Decisions 1. **Zero Dependencies** — No npm install required, making it lightweight and instant for AI agents 2. **Read-Only & Safe** — All commands are queries only, no private keys or signing involved 3. **TRON-Specific Domain Knowledge** — Dedicated skills for Energy/Bandwidth and Stake 2.0, acknowledging TRON's unique architecture 4. **Multi-Format Address Support** — Handles both Base58Check and hex formats transparently 5. **Token Symbol Resolution** — Common tokens have built-in shortcuts; unknown contracts work by address 6. **Cost Optimization Recommendations** — The `optimize-cost` command provides personalized strategies 7. **MCP Server Wrapper** — Provides structured integration for Claude Desktop and modern AI agents --- ## Quick Start ```bash # 1. Clone git clone cd tronlink-skills # 2. Use with Claude Code (no install needed for reads) claude > "What's the TRX balance of TAddress...?" > "Show me the top trending tokens on TRON" > "How much Energy do I need to send USDT?" > "What's the best way to get Energy — freeze, rent, or burn?" # 3. Or use directly node scripts/tron_api.mjs wallet-balance --address TAddress... node scripts/tron_api.mjs token-price --token USDT node scripts/tron_api.mjs optimize-cost --address TAddress... ``` ## Version & License - **Package:** `tronlink-skills` v1.0.1 - **License:** MIT — `SPDX-License-Identifier: MIT` - **Changelog / releases:** [https://github.com/TronLink/tronlink-skills/releases](https://github.com/TronLink/tronlink-skills/releases) — no GitHub-tagged releases yet for v1.0.x; track changes by commit until the first tag. ### Compatibility & migration policy Skills are at **v1.0.x**, so standard semver applies — only **major** bumps may break the public surface. - **Stable contracts** (won't change in a minor or patch): - The 33 CLI command names and their required / optional flags (`tron_api.mjs [...]`). - The 25 MCP tool names listed in [Skill ↔ MCP Tool Map](#skill--mcp-tool-map) (`tron_*` form) and their `inputSchema` keys. - Exit codes: `0` success, `1` query error / invalid input, `2` unsupported / unknown command. - The `Network Read` side-effect classification — no command will ever become a Remote Write without a major bump. - **Volatile contracts** (may change in a minor): - The exact field layout of JSON `stdout` payloads — new fields can be added in any minor; renames or removals are major. Use a tolerant parser. - Built-in token-symbol shortcut list (`USDT`, `USDC`, `WTRX`, …) — symbols may be added in any minor; existing mappings won't be repointed in a minor. - Heuristics and thresholds (`whale-transfers` default cutoff, `optimize-cost` decision tree weights, etc.). - **Subset relationship.** The MCP tool subset (currently 25 of 33) may **grow** in a minor (a previously CLI-only command exposed as an MCP tool); it will not **shrink** in a minor. - **Deprecation window.** A command / tool marked deprecated continues to work for at least one minor cycle; the runtime prints a `STDERR: [DEPRECATED]` warning. Removal lands no earlier than the next major. - **Verifying after upgrade.** Re-run `tron_api.mjs --help` and (if using MCP) `tools/list` to confirm the names you depend on are still present. The MCP `serverInfo.version` exposed during `initialize` should match the bumped `package.json` version. --- # mcp-tronlink-signer **GitHub**: https://github.com/TronLink/mcp-tronlink-signer MCP Server that exposes [tronlink-signer](https://github.com/TronLink/mcp-tronlink-signer/tree/main/packages/tronlink-signer) as MCP tools for Claude and other AI clients. Sign TRON transactions via TronLink browser wallet with user approval — private keys never leave the wallet. ## Setup ### Claude Code ```bash claude mcp add -s user tronlink-signer -- npx mcp-tronlink-signer ``` ### Claude Desktop / Cursor ```json { "mcpServers": { "tronlink-signer": { "command": "npx", "args": ["mcp-tronlink-signer"] } } } ``` ### From Source ```bash git clone https://github.com/TronLink/mcp-tronlink-signer.git cd mcp-tronlink-signer pnpm install && pnpm build ``` Then point to the built CLI: ```bash claude mcp add -s user tronlink-signer -- node /path/to/packages/mcp-tronlink-signer/dist/cli.js ``` ## MCP Tools | Tool | Description | Parameters | Side effect | Auto-retry safe? | | ---- | ----------- | ---------- | --- | :---: | | `connect_wallet` | Connect TronLink wallet | `network?` | Local Write (caches session) | Yes — user re-approves if needed | | `send_trx` | Send TRX to an address | `to`, `amount`, `network?` | **Remote Write** | **No** — verify on-chain before re-issuing | | `send_trc20` | Send TRC20 tokens | `contractAddress`, `to`, `amount`, `decimals?`, `network?` | **Remote Write** | **No** — same as `send_trx` | | `sign_message` | Sign a message | `message`, `network?` | Local Write (signs only; no broadcast) | Yes — re-prompts the user | | `sign_typed_data` | Sign EIP-712 typed data | `typedData`, `network?` | Local Write (signs only) | Yes — re-prompts the user | | `sign_transaction` (`broadcast=false`) | Sign a raw transaction | `transaction`, `broadcast=false`, `network?` | Local Write | Yes — re-prompts the user | | `sign_transaction` (`broadcast=true`) | Sign + broadcast | `transaction`, `broadcast=true`, `network?` | **Remote Write** | **No** — verify on-chain before re-issuing | | `get_balance` | Get TRX balance | `address`, `network?` | Network Read | Yes | All tools support an optional `network` parameter (`mainnet` / `nile` / `shasta`), defaulting to `mainnet`. **Human-in-the-loop.** Every tool that signs (`send_trx`, `send_trc20`, `sign_message`, `sign_typed_data`, `sign_transaction`) opens the TronLink approval page in the browser. The AI agent **cannot** sign without the user clicking Approve. Treat Remote Write tools as requiring confirmation in production. ## MCP Resources | URI | Description | | --- | ----------- | | `wallet://networks` | Available networks and their configurations | | `wallet://config` | Current signer configuration | ## MCP Prompts | Prompt | Description | | ------ | ----------- | | `send-trx` | Guided workflow for sending TRX | | `check-balance` | Guided workflow for checking balance | | `send-token` | Guided workflow for sending TRC20 tokens | ## How It Works 1. AI agent calls an MCP tool (e.g., `send_trx`) — a signing notice is shown in the CLI 2. The server delegates to `tronlink-signer`, which opens a **single browser tab** for approval (reuses existing tab if open) 3. The approval page discovers TronLink via **TIP-6963** protocol 4. Auto-unlocks wallet and switches network if needed 5. `connect_wallet` auto-completes if the wallet is already connected 6. Transaction details are parsed into human-readable format (TRX transfer, TRC20, TRC721 NFT, stake, delegate, vote, etc.) 7. User reviews and approves in the browser 8. TronLink signs the transaction — private keys never leave the wallet 9. Result is returned to the AI agent — the page stays open for the next operation ## End-to-End Example A typical `send_trx` flow, from the user's prompt to the on-chain result: **1. User → agent** > "Send 5 TRX to TJRabc…xyz on nile." **2. Agent → MCP tool call** ```json { "name": "send_trx", "arguments": { "to": "TJRabc...xyz", "amount": 5, "network": "nile" } } ``` **3. Approval (human-in-the-loop)** — the server opens the TronLink approval page; the user reviews "Send 5 TRX → TJRabc…xyz (Nile)" and clicks **Approve**. Clicking Reject returns `USER_REJECTED` (not retryable). **4. MCP tool → agent (result)** ```json { "txId": "0a1b2c...", "status": "success" } ``` **5. Agent → user** > "Sent 5 TRX — confirmed on-chain (tx `0a1b2c…`)." > Branch on `status` / `error.code`, never on prose. `status: "pending"` means the broadcast succeeded but confirmation timed out — reconcile with `get_balance` or an explorer lookup rather than resending (see [Errors](#errors)). ## Cancellation All signing tools support MCP cancellation. If the AI client cancels a pending tool call (e.g., user presses Ctrl+C in Claude Code), the in-flight request is automatically aborted and the browser approval page is not opened for already-cancelled requests. ## Transaction Confirmation When `sign_transaction` is called with `broadcast: true`, the server automatically polls for on-chain confirmation after broadcast and returns the execution status (`success` or `pending`). If the transaction fails on-chain (e.g., `OUT_OF_ENERGY`, Solidity revert), the error is returned to the AI agent with a decoded reason. ## Errors The server returns errors in the standard MCP shape. Each error carries a stable `code` and a `retryable` hint so an agent can branch without parsing prose. Framework-level codes follow the SSOT table in [TronLink MCP Core — Error Codes](tronlink-mcp-core.md#error-codes). Server-specific conditions are: | Condition | Retryable | When | | --- | :---: | --- | | `USER_REJECTED` | No | User clicked Reject on the TronLink approval page. | | `TIMEOUT` | Yes | No approval within the request timeout (default 5 min). Re-issuing re-opens the prompt; do **not** auto-retry a broadcast that may already be in flight. | | `BROWSER_DISCONNECTED` | Yes (signing only) | Approval page was closed or lost heartbeat. Reconnect by re-issuing the call. Never re-issue a broadcast that may have already landed. | | `NETWORK_ERROR` | Yes | A TronGrid / RPC request failed. Transient. | | `BROADCAST_FAILED` | No | Signing succeeded but submission was rejected by the node. Inspect the message; **do not** auto-retry — the signature may already have been accepted by another node. | | `ON_CHAIN_FAILED` | No | Broadcast OK but on-chain execution failed (`OUT_OF_ENERGY`, Solidity revert, `FAILED`). The transaction is final; address the root cause and submit a new tx. | | `INVALID_INPUT` | No | The tool input failed validation. Fix the payload. | | `CANCELLED` | No | The MCP client cancelled the call (e.g., user pressed Ctrl+C). | **Retry policy.** Read calls (`get_balance`) and pre-sign failures (`USER_REJECTED`, `INVALID_INPUT`, `CANCELLED`) are agent-safe to re-issue with corrected input. For any sign + broadcast path, treat the outcome as unknown the moment the request leaves the server — confirm with `get_balance` or an explorer lookup before re-issuing. ## Security Boundaries | Boundary | Guarantee | Agent / operator obligation | |---|---|---| | **Prompt injection** | Tool inputs are consumed verbatim as call arguments; the server does not re-prompt an LLM with them. The TronLink approval page renders parsed transaction fields, not the agent's free-text. | Treat any on-chain string returned (memos, revert reasons) as untrusted; branch on `txId` / `status` / `code`, not on prose. | | **Local HTTP listener** | The local approval server **binds to `127.0.0.1` only** (port `TRON_HTTP_PORT`, default 3386, auto-increments on conflict). It never accepts off-host connections. Each server session has a unique ID; old browser tabs from previous sessions are invalidated automatically. | Do not port-forward 3386 over a network. Do not run two copies of this server with the same `TRON_HTTP_PORT` on a shared box. | | **Outbound host allowlist (SSRF)** | The signer only talks to TronGrid endpoints listed in [Networks](#environment-variables) and to the local browser. Tools do not accept user-supplied URLs that get fetched. | Pin `TRON_NETWORK` and (optionally) `TRON_API_KEY` to known values in production. | | **API key handling (token passthrough)** | `TRON_API_KEY` is read from env at startup and used only on the outbound leg to TronGrid. It is **not** returned in any tool response, error `details`, or MCP resource. The server does not accept Authorization headers from MCP clients and forward them upstream. | Store the key in the MCP host's secret manager, not in plain `mcpServers` config committed to git. | | **HITL is mandatory for signing** | Every signing tool (`send_trx`, `send_trc20`, `sign_message`, `sign_typed_data`, `sign_transaction`) opens the TronLink approval page. **There is no programmatic bypass.** Private keys never leave TronLink. | Operators do not need to enforce HITL — it is structural. Do not attempt to harden by removing the browser layer. | | **Browser tab hijacking** | The approval page authenticates each request against the server session ID and ignores stale tabs. Heartbeat detection closes the session on browser disconnect. | If the same user runs multiple agents, ensure each spawns its own signer instance; cross-instance request mixing is prevented by session ID, but UI confusion is not. | | **Confused deputy** | The signer acts under the connected TronLink account; there is no per-call authorization scope from the MCP client. | One signer instance = one TronLink account; do not multiplex multiple end users through the same instance. | ## Environment Variables | Variable | Description | Default | | -------- | ----------- | ------- | | `TRON_NETWORK` | Default network (mainnet/nile/shasta) | `mainnet` | | `TRON_HTTP_PORT` | Local HTTP server port | `3386` | | `TRON_API_KEY` | TronGrid API key (optional) | - | ## Version & License - **Package:** `mcp-tronlink-signer` v0.1.4 - **License:** MIT — `SPDX-License-Identifier: MIT` - **Changelog / releases:** [https://github.com/TronLink/mcp-tronlink-signer/releases](https://github.com/TronLink/mcp-tronlink-signer/releases) ### Inline changelog This page mirrors a downstream README; for the source of truth see the GitHub releases above and the `CHANGELOG.md` in each package. Entries below cover the **MCP-visible** surface (tools, schema, security boundaries) — internal refactors are omitted. #### v0.1.4 _(npm-only, not GitHub-tagged at time of writing)_ Patch-only. No new tools, no breaking input/output shapes. Verify against `list_tools` after upgrade. #### v0.1.3 _(npm-only, not GitHub-tagged at time of writing)_ Patch-only. No new tools, no breaking input/output shapes. #### v0.1.2 — 2026-04-15 Co-released with `tronlink-signer@0.1.2`. **Major UX overhaul** on the approval flow; no breaking MCP tool schemas. - **New** — `sign_transaction` accepts `broadcast: true` to sign and broadcast in one step (was sign-only previously). - **New** — `connect_wallet` auto-completes when the wallet is already connected; no extra approval round-trip. - **New** — Transaction parsing: TRX transfer, TRC10, TRC20, TRC721 NFT, stake/unstake, delegate, vote, etc. are rendered in human-readable form on the approval page. - **Improved** — Single-page approval flow: one persistent browser tab with heartbeat-based liveness; stale tabs across server restarts are invalidated automatically. - **Improved** — TRC20 amount validation now uses BigInt-based decimal conversion (handles 0-decimal and >18-decimal edge cases). - **Improved** — `send_trx` and `sign_transaction` return real broadcast errors instead of empty messages on submission failure. - **Migration** — None required if you were already branching on `error.code` / `status`; if you parsed message prose, switch now (see [Errors](#errors)). #### v0.1.1 — 2026-04-15 - Initial npm release of `tronlink-signer@0.1.1` + `mcp-tronlink-signer@0.1.1`. - Per-package README with usage instructions and API documentation. - npm package metadata (keywords, repository, license, files). - Copyright notice added to LICENSE. ### Compatibility & migration policy - **Semver.** Pre-1.0: a **minor** bump (0.x → 0.y) may introduce breaking changes; a **patch** bump (0.1.x → 0.1.y) will not change MCP tool names, input schemas, or `error.code` values. Post-1.0: standard semver — major-only breaking changes. - **Deprecation window.** When a tool or input field is deprecated, the next minor release retains the old form alongside the new one for at least one minor cycle, with a `meta.deprecated` flag in the schema; removal lands no earlier than the cycle after that. - **Stable contracts.** Tool names, the `error.code` enum, and `status` values (`success` / `pending`) are part of the public surface — they don't change in a patch. - **Volatile contracts.** Prose `message` text, log line formats, and the layout of the browser approval page are **not** part of the public surface and may change at any time. - **Verifying after upgrade.** Re-call `list_tools` and confirm the names + schemas you depend on are still present before resuming a workflow. --- # tronlink-signer **GitHub**: https://github.com/TronLink/mcp-tronlink-signer/tree/main/packages/tronlink-signer Standalone SDK for signing TRON transactions via the TronLink browser wallet. Private keys never leave TronLink — all signing happens in the browser through a local approval page. ## Installation ```bash npm install tronlink-signer ``` ## Quick Start ```ts import { TronSigner } from "tronlink-signer"; const signer = new TronSigner(); await signer.start(); const { address, network } = await signer.connectWallet(); const { txId, status } = await signer.sendTrx("TXxx...", 1); // status: "success" | "pending" | "failed" const { txId: txId2, status: s2 } = await signer.signTransaction(tx, "nile", true); // broadcast + auto-confirm const { balance } = await signer.getBalance("TXxx..."); await signer.stop(); ``` ## API ### `new TronSigner(config?: Partial)` Creates a new signer instance. If no config is provided, it reads from environment variables via `loadConfig()`. ### `signer.start(): Promise` Starts the local HTTP server for browser approval. ### `signer.stop(): Promise` Stops the server and clears all pending requests. ### `signer.getConfig(): AppConfig` Returns the current configuration. ### `signer.connectWallet(network?, options?): Promise<{ address: string; network: string }>` Opens the browser to connect TronLink and retrieve the wallet address and current network. If the wallet is already connected (same browser tab still open), auto-completes without user interaction. ### `signer.sendTrx(to, amount, network?, options?): Promise` Sends TRX to a recipient address. Opens a browser approval page for the user to confirm. Returns `{ txId, status, error? }` where `status` is `"success"`, `"pending"`, or `"failed"` (see [Broadcast Result](#broadcast-result)). | Parameter | Type | Description | | --------- | ---- | ----------- | | `to` | `string` | Recipient Tron address (base58) | | `amount` | `string \| number` | Amount of TRX to send | | `network` | `TronNetwork` | Optional network override | | `options` | `SignerOptions` | Optional — pass `{ signal }` to enable cancellation | ### `signer.sendTrc20(contractAddress, to, amount, decimals?, network?, options?): Promise` Sends TRC20 tokens. Opens a browser approval page. Returns `{ txId, status, error? }` — see [Broadcast Result](#broadcast-result). | Parameter | Type | Description | | --------- | ---- | ----------- | | `contractAddress` | `string` | TRC20 token contract address | | `to` | `string` | Recipient Tron address (base58) | | `amount` | `string` | Amount in human-readable units (e.g., `"1.5"` for 1.5 USDT). Decimals conversion is automatic. | | `decimals` | `number` | Optional. Token decimals. Omit to auto-detect via the contract's `decimals()` view (recommended — avoids 10^N magnitude errors on non-6dp tokens like USDD/SUN/JST). | | `network` | `TronNetwork` | Optional network override | | `options` | `SignerOptions` | Optional — pass `{ signal }` to enable cancellation | ### `signer.signMessage(message, network?, options?): Promise<{ signature: string }>` Signs a plain text message. ### `signer.signTypedData(typedData, network?, options?): Promise<{ signature: string }>` Signs EIP-712 typed data. ```ts const { signature } = await signer.signTypedData({ domain: { name: "MyDApp", version: "1", chainId: 728126428 }, types: { Greeting: [{ name: "contents", type: "string" }], }, primaryType: "Greeting", message: { contents: "Hello Tron!" }, }); ``` ### `signer.signTransaction(transaction, network?, broadcast?, options?): Promise<{ signedTransaction; txId?; status?; error? }>` Signs a raw transaction. When `broadcast` is `true`, the signed transaction is broadcast on-chain via TronLink and the SDK automatically polls for on-chain confirmation — see [Broadcast Result](#broadcast-result) for the possible `status` values. | Parameter | Type | Description | | --------- | ---- | ----------- | | `transaction` | `Record` | Raw transaction object to sign | | `network` | `TronNetwork` | Optional network override | | `broadcast` | `boolean` | Whether to broadcast after signing (default: `false`) | | `options` | `SignerOptions` | Optional — cancellation, confirmation control, broadcast callback | ```ts // Sign only const { signedTransaction } = await signer.signTransaction(tx); // Sign, broadcast, and wait for confirmation (default) const { signedTransaction, txId, status, error } = await signer.signTransaction(tx, "nile", true); // status === "success" — confirmed on-chain // status === "pending" — broadcast succeeded but not confirmed within timeout // status === "failed" — on-chain execution failed; `error` carries the reason // Broadcast without waiting for confirmation const result = await signer.signTransaction(tx, "nile", true, { confirm: false }); // Get notified as soon as the tx enters the mempool const result = await signer.signTransaction(tx, "nile", true, { onBroadcasted: ({ txId }) => console.log("Broadcast:", txId), }); ``` ### Broadcast Result Broadcasting methods (`sendTrx`, `sendTrc20`, and `signTransaction` with `broadcast: true`) return a `BroadcastResult`: ```ts interface BroadcastResult { txId: string; status: "success" | "pending" | "failed"; error?: string; // set when status === "failed" } ``` `"failed"` means the transaction reached the chain but execution failed (e.g. `OUT_OF_ENERGY`, Solidity revert). **It is not thrown** — inspect `status` instead. The SDK only throws when broadcast itself never happened (signature error, user rejection, network unreachable, etc.). ### `signer.waitForTransaction(txId, network?, options?): Promise<{ status; error? }>` Polls the network for transaction confirmation. Returns `{ status: "success" }` when confirmed, `{ status: "pending" }` on timeout, or `{ status: "failed", error }` when the transaction reverted / ran out of energy. | Parameter | Type | Description | | --------- | ---- | ----------- | | `txId` | `string` | Transaction ID to monitor | | `network` | `TronNetwork` | Optional network override | | `options` | `WaitForTransactionOptions` | Optional — `timeoutMs` (default: 30000), `signal` | ```ts const { status, error } = await signer.waitForTransaction(txId, "nile"); if (status === "success") console.log("Confirmed on-chain"); else if (status === "failed") console.warn("Execution failed:", error); ``` > **Note:** When using `signTransaction` with `broadcast: true`, confirmation polling is automatic — you don't need to call `waitForTransaction` separately unless you set `confirm: false`. ### `signer.getBalance(address, network?): Promise<{ balance: string; balanceSun: number }>` Gets TRX balance for an address. No browser approval needed. ### `signer.onBrowserDisconnect` Setter for a callback that fires when the approval page is closed or loses connection (heartbeat timeout). Useful for cleanup or re-prompting the user. ```ts signer.onBrowserDisconnect = () => { console.log("Browser approval page was closed"); }; ``` ### Cancellation & Options All signing methods accept an optional `SignerOptions`: | Option | Type | Description | | ------ | ---- | ----------- | | `signal` | `AbortSignal` | Cancels the pending request. If already aborted, the browser page is not opened. | | `confirm` | `boolean` | Wait for on-chain confirmation after broadcast (default: `true`). Only applies when `broadcast: true`. | | `confirmTimeoutMs` | `number` | Max time (ms) to wait for confirmation (default: `30000`). | | `onBroadcasted` | `(info) => void` | Fires after the tx enters the mempool (before confirmation). Callback errors are swallowed. | ```ts const controller = new AbortController(); // Cancel after 30 seconds setTimeout(() => controller.abort(), 30_000); try { const { txId } = await signer.sendTrx("TXxx...", 1, undefined, { signal: controller.signal, }); } catch (e) { // e.message === "CANCELLED_BY_CALLER" } ``` ## Safety & Side Effects | Side effect | Methods | | --- | --- | | **Read** (no approval, no state change) | `getBalance`, `waitForTransaction`, `getConfig` | | **Sign-only** (produces a signature, no on-chain effect) | `signMessage`, `signTypedData`, `signTransaction` with `broadcast: false` | | **Remote Write** (signs + broadcasts on-chain) | `sendTrx`, `sendTrc20`, `signTransaction` with `broadcast: true` | | **Connection** (local session) | `connectWallet`, `start`, `stop` | - **Human-in-the-loop:** `connectWallet` and every signing/Remote Write call require the user to approve on the TronLink approval page — the SDK throws on rejection. Private keys never leave TronLink. - **Retry policy:** - Read and Sign-only calls are safe to retry (idempotent, no chain effect). - Remote Write calls must **not** be blindly retried. The SDK throws only when broadcast never happened (signature error, user rejection, network unreachable) — those are safe to retry. But a returned `status: "pending"` means the broadcast may have landed; resending risks a double-spend. Reconcile with `waitForTransaction(txId)` before any retry. - **Failure ≠ throw:** an on-chain failure (`OUT_OF_ENERGY`, revert) is reported as `status: "failed"` on the `BroadcastResult`, not thrown — branch on `status`, not on try/catch. See [Broadcast Result](#broadcast-result). ## How It Works 1. Your code calls a signing method (e.g., `signMessage`) 2. A local HTTP server starts on port 3386 and a **single browser tab** opens the approval page 3. The approval page discovers the wallet via **TIP-6963** protocol (fallback to `window.tron`) 4. Auto-unlocks wallet and switches network if needed 5. For `connectWallet`, if the wallet is already connected, it auto-completes without user interaction 6. For signing/sending, the user reviews the transaction details and clicks Approve / Reject 7. The approval page parses transaction types (TRX transfer, TRC20, TRC721 NFT, stake, delegate, vote, etc.) into human-readable display 8. TronLink handles signing in the browser 9. The result is returned to your code — the page stays open and polls for the next request All subsequent operations reuse the same browser tab. Each server session has a unique ID — old browser tabs from previous sessions are automatically invalidated. The page detects server disconnection via heartbeat and shows a session expired message. The local server binds to `127.0.0.1` only. If port 3386 is in use, it auto-increments. Requests timeout after 5 minutes. The server gracefully shuts down on process exit (SIGINT/SIGTERM). ## Networks All signing methods accept an optional `network` parameter: | Network | Full Host | Explorer | | ------- | --------- | -------- | | `mainnet` (default) | `https://api.trongrid.io` | `https://tronscan.org` | | `nile` | `https://nile.trongrid.io` | `https://nile.tronscan.org` | | `shasta` | `https://api.shasta.trongrid.io` | `https://shasta.tronscan.org` | ## Environment Variables | Variable | Description | Default | | -------- | ----------- | ------- | | `TRON_NETWORK` | Default network | `mainnet` | | `TRON_HTTP_PORT` | Local HTTP server port | `3386` | | `TRON_API_KEY` | TronGrid API key (optional) | - | ## Types ```ts type TronNetwork = "mainnet" | "nile" | "shasta"; interface AppConfig { network: TronNetwork; httpPort: number; apiKey?: string; } interface NetworkConfig { name: string; fullHost: string; explorerUrl: string; } interface SignerOptions { signal?: AbortSignal; confirm?: boolean; confirmTimeoutMs?: number; onBroadcasted?: (info: { txId: string; signedTransaction: Record }) => void; } interface WaitForTransactionOptions { timeoutMs?: number; signal?: AbortSignal; } type BroadcastStatus = "success" | "pending" | "failed"; interface BroadcastResult { txId: string; status: BroadcastStatus; error?: string; } ``` ## Exports ```ts // Class export { TronSigner } from "./tron-signer.js"; // Config export { NETWORKS, DEFAULT_HTTP_PORT, REQUEST_TIMEOUT_MS, loadConfig } from "./config.js"; // Types export type { TronNetwork, NetworkConfig, AppConfig, PendingRequestType, PendingRequest, SendTrxData, SendTrc20Data, SignMessageData, SignTypedDataData, SignTransactionData, SignerOptions, WaitForTransactionOptions, BroadcastStatus, BroadcastResult, } from "./types.js"; ``` ## Version & License - **Package:** `tronlink-signer` v0.1.4 - **License:** MIT — `SPDX-License-Identifier: MIT` - **Changelog / releases:** [https://github.com/TronLink/mcp-tronlink-signer/releases](https://github.com/TronLink/mcp-tronlink-signer/releases) — shared with `mcp-tronlink-signer`; published releases: **v0.1.1, v0.1.2** (2026-04-15). v0.1.3 / v0.1.4 are npm-only at time of writing — see the [inline changelog in `mcp-tronlink-signer`](mcp-tronlink-signer.md#inline-changelog) for MCP-visible deltas; SDK-level changes follow the same wave. ### Compatibility & migration policy The SDK and the MCP wrapper share a version line and are released together. - **Semver.** Pre-1.0: a **minor** bump (0.x → 0.y) may introduce breaking changes; a **patch** (0.1.x → 0.1.y) will not change exported types, function signatures, or `error.code` values. Post-1.0: standard semver — major-only. - **Stable contracts** (won't change in a patch): - Exported function names (`connect`, `sendTrx`, `sendTrc20`, `signMessage`, `signTypedData`, `signTransaction`). - The `BroadcastResult` shape (`status: 'success' | 'pending'`, `txId`, `error?`). - `error.code` enum values and `error.retryable` semantics. - The HITL (browser approval) requirement — no programmatic bypass will ever be added in a patch. - **Volatile contracts** (may change at any time): - Internal helper exports under `./internal/*`, `./browser-bridge.ts`, etc. - Wording of approval-page UI, signing-notice text, and stderr log lines. - The exact layout of `error.message`; branch on `error.code`. - **Type-level breakage.** Because the SDK is TypeScript-first, a minor bump may **add** required properties to an internal options interface without changing the callable function signature — this surfaces as a `tsc` error in your code but not a runtime change. We treat that as **a minor change**, not major; pin TypeScript-strict consumers to a `~0.1` range or read the diff before bumping. - **Verifying after upgrade.** Re-import the public types and confirm your `BroadcastResult` branching still compiles; in MCP mode, re-call `list_tools` to verify wrapper tool names and `inputSchema`. --- # TronLink CLI **GitHub**: [https://github.com/TronLink/tronlink-cli](https://github.com/TronLink/tronlink-cli) CLI tool for TRON blockchain operations via TronLink wallet signing. All transactions are built locally and signed through the TronLink browser extension — private keys never leave TronLink. ## Requirements - Node.js >= 20 - TronLink browser extension installed - Browser running (write operations open TronLink for signing) **Bundled runtime (pinned by `@tronlink/tronlink-cli@1.0.1`):** - `tronweb` `6.2.2` — used by `transactionBuilder`, the ABI v2 trigger path, `TronWeb.isAddress()`, and local broadcast. TronWeb 6.x exposes the ethers-backed ABI v2 encoder that powers tuple / nested-array / arrays-of-tuples in `trigger`; earlier TronWeb majors do not. If you ever bump this dep, re-test every `trigger` example. - `tronlink-signer` `0.1.4` — the local signing bridge to the browser extension. Same SDK documented in [tronlink-signer](tronlink-signer.md). Effective as of 2026-05; source: each package's `package.json`. ## Installation ```bash # From npm npm install -g @tronlink/tronlink-cli # Local development npm install npm run build npm link ``` After installation, the `tronlink` command is available globally. ## Global Options | Option | Default | Description | | ------------------- | ------- | ---------------------------------------------------------- | | `--local-broadcast` | off | CLI broadcasts locally instead of letting signer broadcast | | `--json` | off | Output as JSON for scripts / AI agents | | `--api-key ` | - | TronGrid API key (or set `TRON_API_KEY` env) | | `--timeout ` | 300000 | Signing/connection timeout in milliseconds | | `--port ` | 3386 | TronLink Signer HTTP port | All option names are **case-insensitive** (e.g. `--toAddress`, `--TOADDRESS`, `--toaddress` are equivalent). ## Commands ### Query (Read) Read commands support two modes: - **With `--address`**: queries directly via TronGrid, no wallet connection needed. Defaults to mainnet if `--network` is omitted. - **Without `--address`**: prompts TronLink approval to read the current wallet address (defaults to mainnet if `--network` is omitted). ```bash # Query all token balances tronlink balance --address
[--network mainnet] tronlink balance # connects wallet # Query a specific TRC20 token balance (decimals auto-detected) tronlink balance --address
--token [--decimals 6] [--network mainnet] # Query a specific TRC10 token balance (decimals auto-detected) tronlink balance --address
--tokenId [--decimals 6] [--network mainnet] # Query energy & bandwidth tronlink resource --address
[--network nile] tronlink resource # connects wallet ``` **Mainnet tokens queried**: TRX, USDT, USDD, USDC, SUN, JST, BTT, WIN, WTRX **Nile tokens queried**: TRX, USDT **Shasta tokens queried**: TRX ### Transfer ```bash # TRX (amount in TRX, not sun) tronlink transfer --type trx --toAddress --amount [--network nile] # TRC10 (decimals auto-detected or specify --decimals) tronlink transfer --type trc10 --tokenId --toAddress --amount [--decimals 6] [--network nile] # TRC20 (decimals auto-detected; optional --fee-limit in TRX, default 100) tronlink transfer --type trc20 --contract --toAddress --amount [--decimals 6] [--fee-limit 150] [--network nile] # TRC721 NFT (optional --fee-limit in TRX, default 100) tronlink transfer --type trc721 --contract --toAddress --tokenId [--fee-limit 150] [--network nile] ``` Examples: ```bash tronlink transfer --type trx --toAddress TYqx5gm3p3wLDE9Bv8TBJAbK4ELNbSLfJV --amount 100 tronlink transfer --type trc20 --contract TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t --toAddress TYqx5gm3p3wLDE9Bv8TBJAbK4ELNbSLfJV --amount 50 tronlink transfer --type trc721 --contract TContractAddr --toAddress TRecipient --tokenId 12345 ``` Parameter validation per type: | Type | Required | Not allowed | | ------ | ---------------------------------------- | ----------------------------------------------------- | | trx | `--toAddress`, `--amount` | `--tokenId`, `--contract`, `--decimals`, `--fee-limit`| | trc10 | `--toAddress`, `--amount`, `--tokenId` | `--contract`, `--fee-limit` | | trc20 | `--toAddress`, `--amount`, `--contract` | `--tokenId` | | trc721 | `--toAddress`, `--contract`, `--tokenId` | `--amount`, `--decimals` | ### Trigger Smart Contract Call any smart contract method. Arguments are a JSON array aligned by position to the types in `--method`. ```bash # Writeable call (signed + broadcast) tronlink trigger \ --contract
\ --method 'transfer(address,uint256)' \ --args '["TRecipient...","1000000"]' \ [--call-value ] [--fee-limit ] [--network nile] # Constant (read-only) call — returns raw hex from constant_result tronlink trigger \ --contract
\ --method 'balanceOf(address)' \ --args '["TQuery..."]' \ --constant [--address ] [--network nile] ``` Examples: ```bash # TRC20 approve tronlink trigger --contract TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t \ --method 'approve(address,uint256)' \ --args '["TSpender...","1000000"]' # Batch swap with tuple array tronlink trigger --contract TDex... \ --method 'swap((address,uint256)[],uint256)' \ --args '[[["T...","100"],["T...","200"]],"999"]' --fee-limit 200 # Payable call (with --call-value) tronlink trigger --contract TPay... --method 'deposit()' --args '[]' --call-value 5 # Read-only query tronlink trigger --contract TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t \ --method 'balanceOf(address)' --args '["TQuery..."]' --constant --address TQuery... ``` Notes: - `--method` takes the Solidity function signature. Names on parameters are optional and ignored: `transfer(address to, uint256 amount)` works the same as `transfer(address,uint256)`. - Structured types are fully supported: tuples, nested arrays, arrays of tuples (e.g. `swap((address,uint256)[],uint256)`). Encoding goes through TronWeb's ABI v2 path (ethers under the hood). - `--args` must be a JSON array aligned by position to the method's top-level inputs. Tuples are nested JSON arrays (not objects); use strings for `uint256`/`int256` to avoid JS precision loss. - Writeable calls run a pre-flight simulation on chain (`triggerConstantContract`) before signing. Contract reverts, insufficient TRX for call-value + fee, and fee-limit overruns are caught before a transaction is ever submitted. - `--constant` skips signing and broadcast. Pass `--address` to avoid a wallet prompt when the owner address doesn't matter. Decoded output is not provided — use a Solidity ABI decoder on the returned hex. ### Staking (Stake 2.0) ```bash # Stake TRX for energy or bandwidth tronlink stake --amount --resource [--network nile] # Unlock staked TRX tronlink unstake --amount --resource [--network nile] # Withdraw unfrozen TRX (after 14-day unlock period) tronlink withdraw [--network nile] ``` ### Resource Delegation ```bash # Delegate energy or bandwidth (lock-period in days, supports decimals e.g. 1.5) tronlink delegate --toAddress --amount --resource [--lock-period ] [--network nile] # Reclaim delegated resources # Partial reclaim is allowed: if some delegations are past their lock period and # others are still locked, you can reclaim up to the unlocked total. The precheck # reports how much is unlocked and when the next batch unlocks if the request # exceeds the unlocked amount. tronlink reclaim --fromAddress --amount --resource [--network nile] ``` ### Voting ```bash # Vote for super representatives (format: address:count, supports multiple) tronlink vote --votes [--network nile] # Claim voting rewards tronlink reward [--network nile] ``` Examples: ```bash tronlink vote --votes TXxx:5 TYyy:3 TZzz:2 tronlink reward ``` ## Transaction Signing Write operations (transfer, stake, delegate, vote, etc.) require TronLink approval in the browser: 1. CLI builds the transaction locally and displays a preview 2. Browser opens the TronLink Signer approval page 3. User reviews and clicks Approve or Reject 4. Result is returned to the CLI The browser window is reused across multiple commands — only one browser tab is needed per session. Closing the browser tab ends the session. Multiple concurrent commands are supported. The browser UI shows each pending request as its own tab; approve them in any order. Cancelling a command (Ctrl+C) cancels only that transaction. Other queued transactions are unaffected. ## Transaction Preview All write operations display a preview before signing: ```text Transaction Preview ┌───────────┬────────────────────────────────────────┐ │ Action │ Transfer TRX │ │ Network │ nile │ │ From │ TXxx... │ │ To │ TYyy... │ │ Amount │ 100 TRX │ │ Broadcast │ Signer │ └───────────┴────────────────────────────────────────┘ Awaiting TronLink approval... ``` ## Broadcast By default, signed transactions are broadcast by the signer (TronLink). Use `--local-broadcast` to have the CLI broadcast locally via its own TronWeb instead: ```bash # Default: signer broadcasts after signing tronlink transfer --type trx --toAddress TYqx5gm3p3wLDE9Bv8TBJAbK4ELNbSLfJV --amount 100 # CLI broadcasts locally tronlink transfer --type trx --toAddress TYqx5gm3p3wLDE9Bv8TBJAbK4ELNbSLfJV --amount 100 --local-broadcast ``` **The two paths are mutually exclusive, not redundant.** Setting `--local-broadcast` tells the signer to return the signed transaction **without** broadcasting; the CLI then sends it once via its own TronWeb. The same signed payload is never submitted twice from this CLI in a single command. If a network race causes the CLI's local broadcast and a stale signer broadcast to both reach the network (e.g. flapping connectivity, two CLI invocations against the same nonce), the second submission is rejected by the node — TRON nodes deduplicate by transaction id, so you will see one block-inclusion plus one `DUP_TRANSACTION_ERROR`-class failure, not two on-chain effects. Treat any such error after a confirmed first inclusion as benign; treat it before confirmation as you would any `5` exit (network) — reconcile with an explorer before retrying. ## Input Validation All inputs are validated before connecting to TronLink: - **Amounts**: non-negative numbers only, no scientific notation, no multiple decimals - **TRX amounts**: must be > 0, within safe integer range after sun conversion - **TRC20/TRC10 amounts**: decimal places must not exceed the token's decimals. Decimals auto-detected from chain if `--decimals` is omitted - **Addresses**: valid TRON address format (verified via `TronWeb.isAddress()`) - **Contract existence**: verified on the specified network before querying decimals - **Vote counts**: positive integers, no duplicate SR addresses - **Network**: must be `mainnet`, `nile`, or `shasta` - **Decimals**: non-negative integer (0-77) - **Fee limit**: positive number in TRX (TRC20/TRC721/trigger) - **Call value** (trigger only): non-negative number in TRX; `0` is allowed and equivalent to omitting it - **Method signature** (trigger): must be `name(type1,type2,...)`; parameter names are optional and ignored - **Args** (trigger): valid JSON array whose length matches the method's top-level input count - **Transfer type validation**: missing required params or extra inapplicable params are rejected with clear errors Invalid inputs are rejected immediately with a clear error before any wallet interaction. ## Output Formats **Table (default):** Human-readable table output. **JSON (`--json`):** Machine-readable output for scripts and AI agents. Always pass `--json` for automation. A successful write command returns: ```json { "Status": "Success", "TxID": "0abc...", "Explorer": "https://tronscan.org/#/transaction/0abc..." } ``` Read commands return the queried data (balances, resources, etc.) under the same top-level object. Field names are stable within a major version. ## Exit Codes The CLI exits with one of these stable codes so an automation script can branch on failure class without parsing prose. In `--json` mode the same classification appears in the output as well. | Exit code | Class | Meaning | Retryable | | :---: | --- | --- | :---: | | `0` | Success | Query returned, or transaction signed and broadcast | n/a | | `1` | Invalid input | Validation failed before any wallet interaction (see [Input Validation](#input-validation)) | No — fix the input | | `2` | User rejected | User clicked Reject on the TronLink approval page | No — user declined | | `3` | Signing timeout | No approval within `--timeout ` (default 5 min) | Yes — but **not** for a broadcast that may already be in flight | | `4` | On-chain failure | Broadcast succeeded but execution failed (`OUT_OF_ENERGY`, `REVERT`, `FAILED`) | No — the tx is final; address the root cause | | `5` | Network error | TronGrid / RPC request failed (transient) | Yes — transient; for write commands, confirm the previous tx didn't land first | > **Retry policy.** Read commands (any `balance` / `resource` / `--constant trigger`) are always safe to retry. For write/signing commands (transfer, stake, delegate, vote, writeable trigger), do **not** auto-retry after a submitted-but-uncertain result — re-issuing re-opens the signing prompt and may double-submit. Re-issue only after confirming the previous tx did not land (via explorer or `balance`). ## Errors The error class an agent should branch on is given by the exit code above. The table below maps the conditions the CLI surfaces (in stderr and in `--json` output) to that class: | Condition | Exit code | | --- | :---: | | Argument parse / type / range failure | `1` | | User clicks Reject in TronLink | `2` | | `--timeout` elapsed without an approval | `3` | | `OUT_OF_ENERGY` returned by the node | `4` | | `REVERT` (Solidity revert) | `4` | | `FAILED` (other on-chain failure) | `4` | | TronGrid / RPC unreachable, 5xx, timeout | `5` | ## Safety & Side Effects | Side effect | Commands | | --- | --- | | **Read-only** (Network Read, no signing) | `balance`, `resource`, constant `trigger` (`--constant`) | | **Remote Write** (signs + broadcasts) | `transfer`, `stake`, `unstake`, `withdraw`, `delegate`, `reclaim`, `vote`, `reward`, writeable `trigger` | - **Human-in-the-loop:** every write command builds the transaction locally, shows a [Transaction Preview](#transaction-preview), and requires explicit approval on the TronLink browser page before signing. Private keys never leave TronLink. - **No auto-retry on writes:** see the retry policy above. - **Low-risk by default:** prefer testnets (`--network nile` / `shasta`); pass `--network mainnet` only for real funds. ## Supported Networks | Network | API Endpoint | Explorer | | ------- | -------------------------------- | ----------------------------- | | mainnet | `https://api.trongrid.io` | `https://tronscan.org` | | nile | `https://nile.trongrid.io` | `https://nile.tronscan.org` | | shasta | `https://api.shasta.trongrid.io` | `https://shasta.tronscan.org` | ## How It Works 1. CLI parses command and validates all inputs 2. For read operations with `--address`: queries TronGrid directly 3. For write/read without `--address`: connects to the TronLink browser extension for wallet info 4. Builds unsigned transaction using local TronWeb `transactionBuilder` 5. Pre-flight check: runs on all write commands (transfer, stake, unstake, delegate, reclaim, vote, trigger). Verifies TRX / token / staked / delegated balances, simulates contract calls to catch reverts, estimates energy & bandwidth burn, validates SR addresses (vote), NFT ownership (TRC721), and delegation unlock times (reclaim) before signing 6. Sends transaction to TronLink for signing (browser approval page) 7. Broadcasts: signer broadcasts by default, or CLI broadcasts locally with `--local-broadcast`. In both modes the CLI polls `getUnconfirmedTransactionInfo` until the tx is packed into a block (~3s), and surfaces OUT_OF_ENERGY / REVERT / FAILED as errors 8. Outputs result ## AI / Agent Usage TronLink CLI supports AI agent integration via `--json` output. All commands return structured JSON for easy parsing. ### Prerequisites - `tronlink` installed globally (`npm i -g @tronlink/tronlink-cli`) - TronLink browser extension installed and unlocked - Browser running (write operations open TronLink for signing) ### Rules 1. Always append `--json` to get machine-readable output 2. Write operations (transfer, stake, vote, etc.) will open the browser for user signing — wait for the command to return (default timeout: 5 minutes) 3. Read operations with `--address` don't need wallet connection, faster for lookups 4. Use `--network` to specify network; when omitted, commands default to mainnet 5. Cancelling a command (Ctrl+C) cancels only that transaction, not the signing session ### AI Command Reference #### Query (no signing needed) ```bash # Check all token balances tronlink balance --address
--network mainnet --json # Check a specific TRC20 token balance tronlink balance --address
--token --network mainnet --json # Check a specific TRC10 token balance tronlink balance --address
--tokenId --network mainnet --json # Check energy & bandwidth tronlink resource --address
--network mainnet --json ``` #### Transfer (requires signing) ```bash # TRX tronlink transfer --type trx --toAddress --amount --json # TRC20 (decimals auto-detected) tronlink transfer --type trc20 --contract --toAddress --amount --json # TRC10 (decimals auto-detected) tronlink transfer --type trc10 --tokenId --toAddress --amount --json # TRC721 NFT tronlink transfer --type trc721 --contract --toAddress --tokenId --json ``` #### Trigger Smart Contract ```bash # Writeable call (opens TronLink for signing) tronlink trigger --contract \ --method 'transfer(address,uint256)' \ --args '["",""]' --json # Constant (read-only) call — returns hex, decode with any Solidity decoder tronlink trigger --contract \ --method 'balanceOf(address)' \ --args '["
"]' --constant --address
--json ``` #### Staking ```bash tronlink stake --amount --resource energy --json tronlink unstake --amount --resource energy --json tronlink withdraw --json ``` #### Resource Delegation ```bash tronlink delegate --toAddress --amount --resource energy --json tronlink reclaim --fromAddress --amount --resource energy --json ``` #### Voting ```bash tronlink vote --votes --json tronlink reward --json ``` ### Common Token Contracts | Token | Network | Contract | | ----- | ------- | ----------------------------------- | | USDT | mainnet | TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t | | USDD | mainnet | TXDk8mbtRbXeYuMNS83CfKPaYYT8XWv9Hz | | USDC | mainnet | TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8 | ### Example: AI Transfer Flow ```bash # 1. Check balance first tronlink balance --address TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL --network mainnet --json # 2. Send 10 TRX (opens browser for signing, wait for return) tronlink transfer --type trx --toAddress TRecipientAddress --amount 10 --network mainnet --json # 3. Verify result — output includes txId and explorer URL # { "Status": "Success", "TxID": "abc...", "Explorer": "https://tronscan.org/#/transaction/abc..." } ``` ### Notes - All write commands block until the user approves/rejects in TronLink browser popup - If the user rejects or the signing times out (5 min), the command exits with an error - Cancelling a CLI command (Ctrl+C) cancels only that transaction — other queued transactions continue - Use `--timeout ` to adjust the signing timeout - Amounts use string-based math internally — no floating point precision issues ## Version & License - **Package:** `@tronlink/tronlink-cli` v1.0.1 - **License:** MIT — `SPDX-License-Identifier: MIT` - **Changelog / releases:** [https://github.com/TronLink/tronlink-cli/releases](https://github.com/TronLink/tronlink-cli/releases) — no GitHub-tagged releases yet for v1.0.x; track changes by commit until the first tag. ### Compatibility & migration policy The CLI is at **v1.0.x**, so standard semver applies — only **major** bumps may break the public surface that scripts depend on. - **Stable contracts** (won't change in a minor or patch): - Subcommand names and their required positional / flag arguments. - **Exit codes** — every code in the [Exit Codes](#exit-codes) table is part of the public surface. Adding a new code for a previously generic failure is allowed in a minor; reassigning an existing number is major. - **`--json` output keys** — top-level keys (`ok`, `error.code`, `error.retryable`, `txid`, etc.) and the shape under `error`. New optional fields can be added in a minor; renames / removals are major. - The `error.code` enum (shared SSOT with [TronLink MCP Core](tronlink-mcp-core.md#error-codes)). - **Volatile contracts** (may change at any time): - Human-readable stdout text without `--json`. - The exact wording of prompts, banner output, color codes. - Log line formats on stderr (parse `--json` instead). - **`--json` is the automation contract.** If you are scripting against this CLI, always pass `--json` and branch on structured fields. Plain-text output is for humans and will drift across minor releases. - **Deprecation window.** Deprecated subcommands / flags are kept for at least one minor cycle alongside their replacement; the CLI prints `[DEPRECATED]` to stderr when they are used. Removal lands no earlier than the next major. - **Verifying after upgrade.** Re-run `tronlink-cli --help` and any subcommand `--help` you depend on; spot-check the `--json` schema for one read and one preview-only write before resuming automation. --- # General Transfer ## Overview DApp requires users to initiate a TRX transfer. > **Prerequisite:** The DApp connection has been authorized via `eth_requestAccounts` (see [Start Developing](getting-started.md#request-authorization)). It takes 3 steps to initiate a transfer on the TRON network: 1. Create a transfer transaction 2. Sign the transaction 3. Broadcast the signed transaction In this process, Step 2 requires TronLink while both Step 1 and 3 happen on tronWeb. ## Specification ### Example ```javascript const tronweb = window.tron.tronWeb; const fromAddress = tronweb.defaultAddress.base58; const toAddress = "TAHQdDiZajMMP26STUnfsiRMNyXdxAJakZ"; const tx = await tronweb.transactionBuilder.sendTrx(toAddress, 10, fromAddress); // Step 1 try { const signedTx = await tronweb.trx.sign(tx); // Step 2 await tronweb.trx.sendRawTransaction(signedTx); // Step 3 } catch (e) { // error handling } ``` When “await tronweb.trx.sign(tx);” is executed, a pop-up window will show in the TronLink wallet asking the user to confirm, as shown below: ![TronLink transaction approval popup showing the recipient address, TRX amount, and Reject / Sign buttons](../images/plugin-wallet_sign_trx.jpg) If the user chooses on “Reject” in the pop-up window, an exception will be thrown, which the developer can catch for further processing. If the user chooses “Sign” in the pop-up window, the DApp receives and broadcasts the signed transaction. ## Errors The DApp provider surfaces errors as JavaScript exceptions with `.code` / `.message`. The codes you will encounter on this call: | Code | Meaning | Where it comes from | Retryable? | | :---: | --- | --- | :---: | | `4001` | User clicked **Reject** in the signing popup | `tronWeb.trx.sign(tx)` | No — user declined | | (thrown) | `Invalid address` / `Invalid amount` / etc. | `transactionBuilder.sendTrx(...)` before any wallet interaction | No — fix the input | | `REVERT` / `OUT_OF_ENERGY` / `FAILED` | Broadcast succeeded but execution failed on-chain | `sendRawTransaction` result or `tronWeb.trx.getTransactionInfo(txid)` | **No — the tx is final; never auto-retry** | | (network error) | TronGrid / RPC transient failure | `sendRawTransaction` HTTP layer | Yes — back off and retry | For cross-surface translation (DApp ↔ DeepLink ↔ MCP ↔ CLI) see the [Error Code Map](../reference/error-code-map.md). --- # Multi-Signature Transfer ## Overview For this section, you may refer to [General Transfer](transfer.en.md) > **Prerequisite:** The DApp connection has been authorized via `eth_requestAccounts` (see [Start Developing](getting-started.md#request-authorization)). ## Query active permissions `multiSign` requires a valid `permissionId` for an **active** permission on the signing account. Active permissions are returned by `tronWeb.trx.getAccount(address)` under the `active_permission[]` field; each entry exposes `id`, `permission_name`, `threshold`, and the `keys[]` array (address + weight). Owner permissions have a fixed `id` of `0` and are exposed under `owner_permission` separately. ```javascript const tronweb = window.tron.tronWeb; const account = await tronweb.trx.getAccount(tronweb.defaultAddress.base58); // account.active_permission is the array — pick the id whose keys[] includes // the address you intend to sign with, and whose weight + threshold allow the tx. const activePermissions = account.active_permission ?? []; console.log(activePermissions.map(p => ({ id: p.id, name: p.permission_name, threshold: p.threshold }))); ``` Use the resulting `id` as `permissionId` below. ## Specification ### Example ```javascript const tronweb = window.tron.tronWeb; const toAddress = "TRKb2nAnCBfwxnLxgoKJro6VbyA6QmsuXq"; const activePermissionId = 2; // obtained via tronWeb.trx.getAccount(...).active_permission const tx = await tronweb.transactionBuilder.sendTrx( toAddress, 10, { permissionId: activePermissionId } ); // Step 1 try { const signedTx = await tronweb.trx.multiSign(tx, undefined, activePermissionId); // Step 2 await tronweb.trx.sendRawTransaction(signedTx); // Step 3 } catch (e) {} ``` If the user chooses “Reject” in the pop-up window, an exception will be thrown, which the developer can catch for further processing. If the user chooses “Sign” in the pop-up window, the DApp receives and broadcasts the signed transaction. Note that the broadcast only collects one signature in this call — a fully signed multi-sig tx requires the threshold to be met across multiple `multiSign` calls (one per co-signer). Track the partially-signed transaction off-chain (or via the MCP `tl_multisig_*` tools) until the threshold is reached, then broadcast. ## Errors | Code | Meaning | Where it comes from | Retryable? | | :---: | --- | --- | :---: | | `4001` | User clicked **Reject** in the signing popup | `tronWeb.trx.multiSign(...)` | No — user declined | | (thrown) | `permission_id not active` / `permission_id not exist` | `transactionBuilder.sendTrx({permissionId})` — wrong or revoked permission | No — re-query `getAccount(...).active_permission` | | (thrown) | Signature weight below `threshold` after broadcast | `sendRawTransaction` | No — collect more signatures (one `multiSign` call per co-signer) | | `REVERT` / `OUT_OF_ENERGY` / `FAILED` | Broadcast succeeded but execution failed on-chain | `getTransactionInfo(txid)` | **No — the tx is final; never auto-retry** | For cross-surface translation see the [Error Code Map](../reference/error-code-map.md). --- # Message Signature ## Overview DApp requires users to sign a hex message. The signed message will be forwarded to the back-end to verify whether a user's login is legitimate. > **Prerequisite:** The DApp connection has been authorized via `eth_requestAccounts` (see [Start Developing](getting-started.md#request-authorization)). ## Specification ### Example ```javascript const tronweb = window.tron.tronWeb; try { const message = "0x1e"; // any hex string const signedString = await tronweb.trx.signMessageV2(message); } catch (e) {} ``` ### Parameters `window.tron.tronWeb.trx.signMessageV2` accepts a hexadecimal string as the parameter. The string represents the content to be signed. ### Returns If the user chooses to sign in the pop-up window, the DApp will get the signed hexadecimal string. For example: ```text 0xb0e0b150b9b10dc348f25c7f38fc87f16e18c0d230d23946aac519a5ad9e45937f656012d33c09e9d9dec00b03fbb304e797f8991bb823dce676ac91e03a55991b ``` If an error occurs, the following information will be returned: ```text Uncaught (in promise) Invalid transaction provided ``` ## Interaction When “tronweb.trx.signMessageV2(message);” is executed, a pop-up window will show in the TronLink wallet asking the user to confirm, as shown below. The message content will be in hex: ![TronLink message-signing approval popup showing the hex payload and Reject / Sign buttons](../images/plugin-wallet_sign_message.jpg) If the user chooses “Reject” in the pop-up window, an exception will be thrown, which the developer can catch for further processing. ## Errors | Code | Meaning | Where it comes from | Retryable? | | :---: | --- | --- | :---: | | `4001` | User clicked **Reject** in the signing popup | `tronWeb.trx.signMessageV2(message)` | No — user declined | | (thrown) | `Invalid transaction provided` / non-hex input | `signMessageV2(...)` argument validation | No — pass a valid hex string | | (thrown) | Provider not injected / wallet not authorized | `window.tron.tronWeb` undefined | No — connect first via `eth_requestAccounts` | For cross-surface translation (DApp ↔ DeepLink ↔ MCP ↔ CLI) see the [Error Code Map](../reference/error-code-map.md). --- # Stake2.0 > **Prerequisite:** The DApp connection has been authorized via `eth_requestAccounts` (see [Start Developing](getting-started.md#request-authorization)). When generating stake 2.0 transactions for DApps, for transactions of the `DelegateResourceContract` or `UnDelegateResourceContract` type, in order to display the estimated results during signature using the Tronlink extension, it is necessary to add the "__options" field to the transaction body. Inside "__options", there are two values: estimatedBandwidth and estimatedEnergy, which correspond to the estimated energy and bandwidth of the delegate and reclaim operations, respectively. When generating stake 2.0 transactions using a non-tronlink extension injected tronweb, in order to display the corresponding type of resource for `DelegateResourceContract` or `UnDelegateResourceContract` type transactions during signature, the "__resource" field needs to be added to the transaction body. (Adding “resource” is only necessary for tronWeb that is not injected by a tronlink extension. TronWeb that is injected by tronlink extension does not require.) The "__resource" field corresponds to the values "BANDWIDTH" and "ENERGY". ![Stake 2.0 delegate / undelegate signing UI annotated with the __resource ("BANDWIDTH" or "ENERGY") and __options.estimatedBandwidth / estimatedEnergy fields that TronLink reads to render the resource type at signing time](../images/dapp_stake2.0_img_0.jpg) For example: ```javascript const transaction = await tronWeb.transactionBuilder.delegateResource(10e6, 'receiverAddress', 'BANDWIDTH', 'ownerAddress', false); transaction.raw_data.contract[0].parameter.value.resource = "BANDWIDTH" transaction.__options = {"estimatedBandwidth": 1} ``` The specific calculation logic of estimatedEnergy and estimatedBandwidth can be found in the last chapter of the "[Stake 2.0 Adaptation FAQ](https://coredevs.medium.com/stake-2-0-adaption-faq-66bafdf53606)": "How to convert resource share to amount?" ## Errors | Code | Meaning | Where it comes from | Retryable? | | :---: | --- | --- | :---: | | `4001` | User clicked **Reject** in the signing popup | `tronWeb.trx.sign(tx)` for the delegate / undelegate tx | No — user declined | | (thrown) | Missing `__options` or `__resource` — signing UI cannot render the estimate | `tronWeb.trx.sign(tx)` | No — set both fields per the rules above | | (thrown) | Insufficient TRX staked / nothing to undelegate | `delegateResource` / `undelegateResource` builder | No — fix the on-chain state first | | `REVERT` / `OUT_OF_ENERGY` / `FAILED` | Broadcast succeeded but execution failed on-chain | `sendRawTransaction` result or `getTransactionInfo` | **No — the tx is final; never auto-retry** | For cross-surface translation see the [Error Code Map](../reference/error-code-map.md). --- # Networks & Addresses A single reference for the network parameters and common addresses used throughout this documentation. ## Networks | Network | `chainId` (hex) | EVM chainId (decimal) | RPC endpoint | Explorer | |---|---|---|---|---| | Mainnet | `0x2b6653dc` | `728126428` | `https://api.trongrid.io` | [tronscan.org](https://tronscan.org) | | Shasta testnet | `0x94a9059e` | `2494104990` | `https://api.shasta.trongrid.io` | [shasta.tronscan.org](https://shasta.tronscan.org) | | Nile testnet | `0xcd8690dc` | `3448148188` | `https://nile.trongrid.io` | [nile.tronscan.org](https://nile.tronscan.org) | - The `chainId` hex value is what `wallet_switchEthereumChain` (TIP-3326) and the `chainChanged` event use. **`chainId` values are case-sensitive.** - The EVM (decimal) chainId is what EIP-712 typed-data signing uses in the `domain.chainId` field. ## Testnet faucets - Shasta: request test TRX from the [Shasta faucet](https://www.trongrid.io/shasta). - Nile: request test TRX from the [Nile faucet](https://nileex.io/join/getJoinPage). ## Address formats TRON addresses have two interchangeable encodings: - **Base58** — starts with `T`, e.g. `TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t`. This is the user-facing form. - **Hex** — starts with `41`, e.g. `41...`. This is the on-chain/raw form (the `41` prefix is TRON's address byte). `tronWeb.address.toHex()` / `tronWeb.address.fromHex()` convert between them. ## Units - `1 TRX = 1,000,000 SUN`. Amounts in `tronWeb` transaction builders (e.g. `sendTrx`) are expressed in **SUN**. ## Common token contracts (Mainnet) | Token | Standard | Contract address | |---|---|---| | USDT | TRC-20 | `TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t` | For any other token, look up its contract address on [TronScan](https://tronscan.org/#/tokens). Always verify a token's contract address before integrating — addresses differ per network. --- # Glossary Definitions of terms, standards, and identifiers used across this documentation. ## TRON & tokens - **TRX** — the native coin of the TRON network. - **SUN** — the smallest TRX unit. `1 TRX = 1,000,000 SUN`. Transaction amounts are expressed in SUN. - **TRC-10** — TRON's native token standard, managed at the protocol level (identified by a numeric token id). - **TRC-20** — TRON's smart-contract fungible-token standard (analogous to Ethereum's ERC-20). - **TRC-721** — TRON's non-fungible-token (NFT) standard (analogous to ERC-721). - **Energy** — the resource consumed when executing smart-contract operations. Obtained by staking TRX (Stake 2.0) or burning TRX. - **Bandwidth** — the resource consumed by transaction size/byte count. Obtained by staking TRX or burning TRX; a small free daily amount is provided per account. - **Stake 2.0** — TRON's resource-staking model. Staking TRX yields Energy or Bandwidth; resources can be delegated to other accounts (`DelegateResourceContract` / `UnDelegateResourceContract`). See [Stake 2.0](../dapp/stake2.md). ## Wallet objects & SDK - **TronWeb** — the JavaScript SDK for building, signing, and broadcasting TRON transactions. See the [TronWeb docs](https://tronweb.network/docu/docs/intro). - **`window.tron`** — the provider object TronLink injects into every page. Exposes `request`, `tronWeb`, and `on` / `removeListener`. - **`tronWeb` (instance)** — the `TronWeb` SDK instance bound to the user's account/network, available as `window.tron.tronWeb` after authorization. `false` until authorized. - **`window.tronLink`** — the legacy provider object (deprecated in favor of `window.tron`). - **`iTron`** — an object injected only inside the TronLink mobile app's in-app DApp Explorer, exposing native app capabilities. See [DApp Support](../mobile/dapp-support.md). - **HD wallet** — a Hierarchical Deterministic wallet (BIP-32) deriving many keypairs from a single seed. **BIP-44** defines the derivation path structure; the seed is represented by a **mnemonic** phrase. See [HD Wallets](../hd-wallets.md). ## Standards & proposals - **TIP** — TRON Improvement Proposal. The full index is at [github.com/tronprotocol/tips](https://github.com/tronprotocol/tips). - **EIP** — Ethereum Improvement Proposal. TronLink reuses several EIP method names for compatibility. - **TIP-1102 / EIP-1102** — the wallet connection request (`eth_requestAccounts`). See [Proactively Request TronLink Plugin Features](../plugin-wallet/active-requests.md). - **TIP-1193 / EIP-1193** — the provider interface and its events (`connect`, `disconnect`, etc.), including the `ProviderRpcError` shape. - **TIP-3326 / EIP-3326** — the chain-switch request (`wallet_switchEthereumChain`). - **TIP-6963 / EIP-6963** — multi-wallet provider discovery via the `announceProvider` / `requestProvider` events, so a DApp can pick a specific wallet without competing for `window.tron`. ## Types & identifiers - **ABI** — Application Binary Interface: the JSON description of a contract's callable methods and events. If a token contract's ABI lacks a recognized method (e.g. `transfer`, `approve`), TronLink may not be able to offer that action. - **`chainId`** — the network identifier. TronLink uses the hex form (e.g. `0x2b6653dc`) for `wallet_switchEthereumChain` and `chainChanged`, and the decimal EVM form (e.g. `728126428`) for EIP-712 `domain.chainId`. See [Networks & Addresses](networks.md). - **`ProviderRpcError`** — the EIP-1193 error object emitted on `disconnect`, shaped `{ code: number; message: string; data?: unknown }`. - **permission id** — the active permission index used for multi-signature transactions (passed to `tronWeb.trx.multiSign`). See [Multi-Signature Transfer](../dapp/multi-sign-transfer.md). - **base58 / hex address** — the two TRON address encodings (`T...` vs `41...`). See [Networks & Addresses](networks.md#address-formats). ## AI tooling - **MCP** — Model Context Protocol, the standard TronLink's AI servers implement so agents can call wallet tools. See [AI Support](../ai-support/ai-llms.md). - **DeepLink** — the `tronlinkoutside://` URL scheme used to drive the TronLink mobile app from a DApp or H5 page. See [DeepLink](../mobile/deeplink.md). --- # FAQ Common questions when integrating a DApp with TronLink. ## `window.tron` is undefined / the wallet isn't detected The provider may not be injected yet, or TronLink isn't installed. Detect it via TIP-6963 instead of reading `window.tron` directly: listen for `TIP6963:announceProvider`, then dispatch `TIP6963:requestProvider`. If no provider announces, TronLink is not installed — prompt the user to install it. See [Start Developing](../dapp/getting-started.md#detect-tronlink-tip-6963). ## `eth_requestAccounts` was rejected (code `4001`) The user declined the connection (clicked **Reject**, closed the popup, or the request didn't come from the active tab). This is expected — catch the error and let the user retry. See the [error codes](../plugin-wallet/active-requests.md#error-codes). ## I get `-32000` when calling `eth_requestAccounts` The same origin issued another `eth_requestAccounts` within 20 seconds while the wallet was locked (rate-limited). Wait and retry, and avoid firing the request repeatedly. ## `tronWeb` is `false` / not ready `window.tron.tronWeb` is `false` until the user authorizes the DApp. Call `eth_requestAccounts` first, then read `tronWeb`. A safe helper checks `tronProvider.tronWeb?.ready`. See [Get the tronWeb Instance](../dapp/getting-started.md#get-the-tronweb-instance). ## Account or network changes aren't reflected in my DApp `eth_requestAccounts` only covers the initial authorization. For later changes — the user switching accounts, locking the wallet, or changing networks — subscribe to the passive `accountsChanged` and `chainChanged` events. See [Receive Messages from TronLink](../plugin-wallet/passive-messages.md). ## My DApp needs a specific network Request a switch with `wallet_switchEthereumChain` (TIP-3326), passing the target hex `chainId`. See the [chainId values](networks.md) and [Switch Network](../plugin-wallet/active-requests.md). Confirm the active network by reading `chainChanged`. ## A transaction fails for lack of resources On-chain operations consume **Energy** (contract execution) and **Bandwidth** (transaction size). Ensure the signing account has enough — obtained by staking TRX (Stake 2.0) or by letting TRX be burned. See the [Glossary](glossary.md) and [Stake 2.0](../dapp/stake2.md). ## Ledger signatures look different (the `v` byte) TronLink normalizes the trailing byte of Ledger signatures (`01`→`1c`, `00`→`1b`) so Ledger and regular account signatures match. See [Ledger v-field Compatibility](../plugin-wallet/ledger-signing-update.md). ## How do I integrate with the mobile app? Use the DeepLink (`tronlinkoutside://`) scheme to launch the TronLink app for login, transfer, and signing. See [DeepLink](../mobile/deeplink.md). ## How do I build an AI agent that uses TronLink? Use the MCP servers, the agent skill set, the signer SDK, or the CLI. Start at [AI Support](../ai-support/ai-llms.md).