# WDK Documentation *** *** Base URL: https://docs.wdk.tether.io *** Raw markdown is available by appending `.md` to any documentation page URL. *** *** ## Welcome to WDK URL: https://docs.wdk.tether.io/ Description: >- The **Wallet Development Kit _by Tether_ (WDK)** is Tether's open-source toolkit that empowers humans, machines and AI agents alike to build, deploy and use secure, multi-chain, self-custodial wallets that can be integrated anywhere from the smallest embedded device to any mobile, desktop and server operating system. WDK enables trillions of self-custodial wallets. WDK provides a set of core libraries that give you the highest level of control and a wide range of user-interface templates and widgets to maximize your development and deployment speed. *** ### Discover WDK Understand WDK core features and design principles Discover our philosophy and idea for the future wallets Learn foundational concepts and terminology *** ### Start Building Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Deploy WDK in lightweight environments Explore our React Native UI Kit with pre-built components *** ### Get Involved *** ## Agent Skills URL: https://docs.wdk.tether.io/ai/agent-skills Description: Give any AI agent self-custodial wallet capabilities with WDK agent skills WDK provides agent skills: structured instruction sets that teach AI agents how to create wallets, send transactions, swap tokens, bridge assets, and interact with DeFi protocols across 20+ blockchains. All operations are self-custodial. Keys stay on your machine, with no third-party custody dependency. **Skill vs MCP Toolkit**: Use an **agent skill** when your agent platform works with file-based instructions (e.g., OpenClaw, Cursor). Use the [MCP Toolkit](mcp-toolkit/) when your agent supports the Model Context Protocol natively (e.g., Claude, Cursor). Use both for maximum coverage. ## What Are Agent Skills? An agent skill is a structured set of instructions and reference documentation that teaches an AI agent to use a specific tool or SDK. Skills follow the [AgentSkills specification](https://agentskills.io/specification). Each skill is a `SKILL.md` file with frontmatter metadata and detailed instructions that any compatible agent can load and execute. WDK publishes a skill that covers the full SDK surface: wallet modules, swidge, swap, bridge, lending, fiat on/off-ramps, and the indexer. When an agent loads the skill, it learns WDK's APIs so you don't need blockchain expertise to get started. You can view the full skill file on [GitHub](https://github.com/tetherto/wdk-docs/blob/main/skills/wdk/SKILL.md). ## Capabilities Once an agent loads the WDK skill, it can: | Category | Operations | | --- | --- | | **Wallets** | Create and recover wallets across EVM chains, Bitcoin, Solana, Spark, TON, and Tron | | **Transactions** | Send native tokens and token transfers (ERC-20, SPL, Jetton, TRC-20) | | **Swaps** | DEX swaps via Velora (EVM) and StonFi (TON) | | **Bridges** | Cross-chain bridges with USDT0 via LayerZero | | **Lending** | Supply, borrow, repay, and withdraw via Aave V3 | | **Fiat** | Buy and sell crypto via MoonPay on/off-ramps | | **Gasless** | Fee-free transfers on TON (via paymaster) and Tron (via gas-free service), and ERC-4337 account abstraction on EVM | All write operations require explicit human confirmation. The skill instructs agents to estimate fees before sending and includes prompt injection protection guidance. ## How It Works 1. **Install the skill** by cloning the skill repository or installing from a skill registry like [ClawHub](https://clawhub.ai/HumanRupert/tether-wallet-development-kit) 2. **Agent loads the skill** and reads `SKILL.md` along with per-module reference files to learn WDK's API surface 3. **Agent executes operations** when you ask it to create a wallet or send a transaction, generating the correct WDK code 4. **You confirm** before any write operation (transactions, swaps, bridges) goes through The skill includes security guidance: pre-transaction validation checklists, prompt injection detection rules, and mandatory key cleanup patterns. ## Self-Custodial vs Hosted WDK's agent skills use a self-custodial model where your agent controls its own keys locally. This differs from hosted solutions where a third party manages your keys. | Feature | WDK | Coinbase Agentic Wallet | Privy Server Wallets | | --- | --- | --- | --- | | Custody model | Self-custodial | Coinbase-hosted | Privy-hosted (server) | | Multi-chain | Yes (EVM, Bitcoin, Solana, TON, Tron, Spark + more) | EVM + Solana | EVM + Solana + Bitcoin + more | | Open source | Yes (SDK + skills) | CLI/skills open, infra closed | Skills open, API closed | | MCP support | Yes ([MCP Toolkit](mcp-toolkit/)) | Via skills | Via skills | | OpenClaw support | Yes ([ClawHub skill](https://clawhub.ai/HumanRupert/tether-wallet-development-kit)) | Yes (npx skills) | Yes (ClawHub skill) | | x402 payments | Via [community extensions](#community-projects) | Yes (native) | No | | Key management | Local / self-managed | Coinbase infrastructure | Privy infrastructure | ## Use With Agent Platforms | Platform | How to Use | | --- | --- | | **OpenClaw** | Install from [ClawHub](openclaw) or clone to workspace. See [OpenClaw Integration](openclaw) | | **Claude** | Upload `SKILL.md` as project knowledge, or paste into conversation | | **Cursor / Windsurf** | Clone to `.cursor/skills/wdk` or `.windsurf/skills/wdk` | | **Any MCP-compatible agent** | Use the [MCP Toolkit](mcp-toolkit/) for structured tool calling | | **Any other agent** | Copy `SKILL.md` into system prompt or conversation context | ## Community Projects Community-built projects using WDK's agentic capabilities: | Project | Description | | --- | --- | | [wdk-wallet-evm-x402-facilitator](https://github.com/SemanticPay/wdk-wallet-evm-x402-facilitator) | Agent-to-agent payments using the x402 HTTP payment protocol | | [x402-usdt0](https://github.com/baghdadgherras/x402-usdt0) | Reference implementation of x402 on Plasma with USDT0 | | [Novanet zkML Guardrails](https://github.com/hshadab/tether) | Zero-knowledge ML safety checks for wallet operations | ## Resources - [WDK SKILL.md on GitHub](https://github.com/tetherto/wdk-docs/blob/main/skills/wdk/SKILL.md) - The full skill file agents consume - [WDK Skill on ClawHub](https://clawhub.ai/HumanRupert/tether-wallet-development-kit) - Install the skill - [AgentSkills Specification](https://agentskills.io/specification) - The skill format standard - [WDK MCP Toolkit](https://github.com/tetherto/wdk-mcp-toolkit) - MCP server for structured tool calling - [WDK Core](https://github.com/tetherto/wdk-core) - The core SDK *** ## Need Help? *** ## MCP Toolkit URL: https://docs.wdk.tether.io/ai/mcp-toolkit Description: Build MCP servers that give AI agents self-custodial WDK wallets The MCP Toolkit lets AI agents interact with self-custodial WDK wallets. It creates an [MCP server](https://modelcontextprotocol.io/) that exposes wallet operations (checking balances, sending transactions, swapping tokens, bridging assets, and more) as structured tools that any MCP-compatible AI client can call. Powered by [`@tetherto/wdk-mcp-toolkit`](https://github.com/tetherto/wdk-mcp-toolkit). **Beta** - This package is in active development (`v1.0.0-beta.1`). APIs may change between releases. ## Features - **MCP Server Extension** - Extends the official `@modelcontextprotocol/sdk` McpServer with WDK-specific capabilities - **Multi-Chain** - Support for 13 blockchains out of the box, including EVM chains, Bitcoin, Solana, Spark, TON, and Tron - **35 Built-in Tools** - Ready-to-use tools for wallets, pricing, indexer queries, swaps, bridges, lending, and fiat on/off-ramps - **Human Confirmation** - All write operations use MCP elicitations to require explicit user approval before broadcasting transactions - **Extensible** - Register custom tools alongside built-in ones using standard MCP SDK patterns - **Secure by Design** - Seed phrases stay local, `close()` wipes keys from memory, and read/write tool separation lets you control access ## Supported Chains | Chain | Identifier | | --- | --- | | Ethereum | `ethereum` | | Polygon | `polygon` | | Arbitrum | `arbitrum` | | Optimism | `optimism` | | Base | `base` | | Avalanche | `avalanche` | | BNB Chain | `bnb` | | Plasma | `plasma` | | Bitcoin | `bitcoin` | | Solana | `solana` | | Spark | `spark` | | TON | `ton` | | Tron | `tron` | You can register **any** blockchain name - the `CHAINS` constants are for convenience only. For custom chains, register tokens manually with `registerToken()`. Install and run your first MCP server in minutes Wallets, capabilities, tokens, protocols, and custom tools All 35 built-in MCP tools and the WdkMcpServer class Use WDK tools in LangChain agents via the serve CLI *** ## Need Help? *** ## API Reference URL: https://docs.wdk.tether.io/ai/mcp-toolkit/api-reference Description: WdkMcpServer class and all 35 built-in MCP tools ## WdkMcpServer The `WdkMcpServer` class extends `McpServer` from `@modelcontextprotocol/sdk` with WDK-specific wallet, pricing, indexer, and protocol capabilities. ### Constructor ```javascript const server = new WdkMcpServer(name: string, version: string) ``` | Parameter | Type | Description | | --- | --- | --- | | `name` | `string` | Server name (shown to AI clients) | | `version` | `string` | Server version string | ### Core Methods #### `useWdk(config)` Initializes the WDK wallet engine. Must be called before `registerWallet()` or `registerProtocol()`. ```typescript server.useWdk(config: WdkConfig): WdkMcpServer ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `config.seed` | `string` | Yes | BIP-39 mnemonic seed phrase | **Returns:** `WdkMcpServer` (for chaining) --- #### `registerWallet(blockchain, WalletManager, config)` Registers a wallet module for a specific blockchain. ```typescript server.registerWallet( blockchain: string, WalletManager: W, config: ConstructorParameters[1] ): WdkMcpServer ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `blockchain` | `string` | Yes | Chain name (e.g., `'ethereum'`, `'bitcoin'`) | | `WalletManager` | `class` | Yes | Wallet module class (e.g., `WalletManagerEvm`) | | `config` | `object` | Yes | Module-specific config (see each wallet module's docs) | **Requires:** `useWdk()` called first --- #### `registerProtocol(chain, label, Protocol, config?)` Registers a DeFi protocol (swap, bridge, lending, or fiat) for a chain. ```typescript server.registerProtocol

( chain: string, label: string, Protocol: P, config?: ConstructorParameters

[1] ): WdkMcpServer ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `string` | Yes | Chain name (must have a wallet registered) | | `label` | `string` | Yes | Protocol identifier (e.g., `'velora'`, `'aave'`) | | `Protocol` | `class` | Yes | Protocol module class | | `config` | `object` | No | Protocol-specific config | **Requires:** `useWdk()` called first --- #### `useIndexer(config)` Enables the WDK Indexer client for querying token balances and transfer history. ```typescript server.useIndexer(config: { apiKey: string }): WdkMcpServer ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `config.apiKey` | `string` | Yes | WDK Indexer API key | --- #### `usePricing()` Enables the Bitfinex pricing client for current and historical prices. ```typescript server.usePricing(): WdkMcpServer ``` --- #### `registerTools(tools)` Registers multiple MCP tools at once. ```typescript server.registerTools(tools: ToolFunction[]): WdkMcpServer ``` --- #### `registerToken(chain, symbol, token)` Registers a custom token for a chain. ```typescript server.registerToken(chain: string, symbol: string, token: TokenInfo): WdkMcpServer ``` | Parameter | Type | Description | | --- | --- | --- | | `chain` | `string` | Chain name | | `symbol` | `string` | Token symbol (e.g., `'USDC'`) | | `token.address` | `string` | Token contract address | | `token.decimals` | `number` | Token decimal places | --- #### `close()` Disposes the WDK instance and clears seed material from memory. Call this when shutting down the server. ```typescript server.close(): void ``` ### Query Methods | Method | Returns | Description | | --- | --- | --- | | `getChains()` | `string[]` | Registered blockchain names | | `getTokenInfo(chain, symbol)` | `TokenInfo \| undefined` | Token address and decimals | | `getRegisteredTokens(chain)` | `string[]` | Registered token symbols for a chain | | `getSwapChains()` | `string[]` | Chains with swap protocols | | `getSwapProtocols(chain)` | `string[]` | Swap protocol labels for a chain | | `getBridgeChains()` | `string[]` | Chains with bridge protocols | | `getBridgeProtocols(chain)` | `string[]` | Bridge protocol labels for a chain | | `getLendingChains()` | `string[]` | Chains with lending protocols | | `getLendingProtocols(chain)` | `string[]` | Lending protocol labels for a chain | | `getFiatChains()` | `string[]` | Chains with fiat protocols | | `getFiatProtocols(chain)` | `string[]` | Fiat protocol labels for a chain | ### Getters | Getter | Type | Description | | --- | --- | --- | | `server.wdk` | `WalletKit` | WDK instance (after `useWdk()`) | | `server.indexerClient` | `WdkIndexerClient` | Indexer client (after `useIndexer()`) | | `server.pricingClient` | `WdkPricingClient` | Pricing client (after `usePricing()`) | *** ## Types ```typescript type WdkConfig = { seed?: string } type TokenInfo = { address: string decimals: number } type ToolFunction = (server: WdkMcpServer) => void ``` *** ## Built-in MCP Tools All tools use [Zod](https://zod.dev/) for input/output validation and include [MCP tool annotations](https://modelcontextprotocol.io/docs/concepts/tools#tool-annotations) that describe their behavior to AI clients. ### MCP Annotations Every tool declares these annotations: | Annotation | Type | Meaning | | --- | --- | --- | | `readOnlyHint` | `boolean` | Tool does not modify state | | `destructiveHint` | `boolean` | Tool may spend funds or make irreversible changes | | `idempotentHint` | `boolean` | Calling multiple times produces the same result | | `openWorldHint` | `boolean` | Tool interacts with external systems | **Human Confirmation** - All tools where `destructiveHint: true` use MCP [elicitations](https://modelcontextprotocol.io/docs/concepts/elicitation) to show a confirmation dialog before broadcasting. The user must explicitly approve each transaction. *** ## Wallet Tools _Requires: `useWdk()` + `registerWallet()`_ ### `getAddress` Get the wallet address for a blockchain. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain to get the address for | **Output:** | Field | Type | Description | | --- | --- | --- | | `address` | `string` | The wallet address | --- ### `getBalance` Get the native token balance for a blockchain (ETH, BTC, SOL, etc.). **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain to query | **Output:** | Field | Type | Description | | --- | --- | --- | | `balance` | `string` | Balance in base units (wei, satoshis, etc.) | --- ### `getTokenBalance` Get the balance of a registered token (USDT, USDC, etc.). **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain to query | | `token` | `string` | Yes | Token symbol (e.g., `"USDT"`) | **Output:** | Field | Type | Description | | --- | --- | --- | | `balance` | `string` | Human-readable token balance | | `balanceBaseUnits` | `string` | Balance in smallest unit | --- ### `getFeeRates` Get current network fee rates. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain to query | **Output:** | Field | Type | Description | | --- | --- | --- | | `normal` | `string` | Normal fee rate (balanced speed) | | `fast` | `string` | Fast fee rate (higher cost, faster confirmation) | Fee units vary by chain: satoshis/byte (Bitcoin), wei (Ethereum), or chain-specific units. --- ### `getMaxSpendableBtc` Get the maximum spendable Bitcoin amount after fees. **Read-only.** Bitcoin-only. **Input:** None **Output:** | Field | Type | Description | | --- | --- | --- | | `amount` | `string` | Maximum spendable amount (satoshis) | | `fee` | `string` | Estimated transaction fee (satoshis) | | `changeValue` | `string` | Expected change output (satoshis) | --- ### `quoteSendTransaction` Estimate the fee for sending native currency. **Read-only.** Does not broadcast. **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain | | `to` | `string` | Yes | Recipient address | | `value` | `string` | Yes | Amount in **base units** (wei, satoshis) | **Output:** | Field | Type | Description | | --- | --- | --- | | `fee` | `string` | Estimated transaction fee in base units | --- ### `quoteTransfer` Estimate the fee for transferring a token. **Read-only.** Does not broadcast. **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain | | `token` | `string` | Yes | Token symbol (e.g., `"USDT"`) | | `recipient` | `string` | Yes | Recipient address | | `amount` | `string` | Yes | Amount in **human-readable** format (e.g., `"10"`) | **Output:** | Field | Type | Description | | --- | --- | --- | | `fee` | `string` | Estimated transaction fee in base units | --- ### `sendTransaction` Send native currency (ETH, BTC, etc.). **Destructive** - requires user confirmation. **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain | | `to` | `string` | Yes | Recipient address | | `value` | `string` | Yes | Amount in **base units** (wei, satoshis) | **Output:** | Field | Type | Description | | --- | --- | --- | | `hash` | `string` | Transaction hash | | `fee` | `string` | Actual fee paid | This tool shows a confirmation dialog with transaction details before broadcasting. The user must approve the transaction explicitly. --- ### `transfer` Transfer a registered token. **Destructive** - requires user confirmation. **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain | | `token` | `string` | Yes | Token symbol (e.g., `"USDT"`) | | `to` | `string` | Yes | Recipient address | | `amount` | `string` | Yes | Amount in **human-readable** format (e.g., `"100"`) | **Output:** | Field | Type | Description | | --- | --- | --- | | `hash` | `string` | Transaction hash | | `fee` | `string` | Actual fee paid | This tool shows a confirmation dialog with transaction details before broadcasting. The user must approve the transaction explicitly. --- ### `sign` Sign an arbitrary message with the wallet's private key. **Does not reveal the private key.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain | | `message` | `string` | Yes | Message to sign | **Output:** | Field | Type | Description | | --- | --- | --- | | `signature` | `string` | Cryptographic signature | --- ### `verify` Verify that a signature is valid for a given message. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | The blockchain | | `message` | `string` | Yes | Original message | | `signature` | `string` | Yes | Signature to verify | **Output:** | Field | Type | Description | | --- | --- | --- | | `valid` | `boolean` | Whether the signature is valid | *** ## Pricing Tools _Requires: `usePricing()`_ ### `getCurrentPrice` Get the current spot price from Bitfinex. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `base` | `string` | Yes | Base currency (e.g., `"BTC"`, `"ETH"`) | | `quote` | `string` | Yes | Quote currency (e.g., `"USD"`, `"USDT"`) | **Output:** | Field | Type | Description | | --- | --- | --- | | `base` | `string` | Base currency | | `quote` | `string` | Quote currency | | `price` | `number` | Current spot price | --- ### `getHistoricalPrice` Get historical price data (OHLCV candles) from Bitfinex. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `from` | `string` | Yes | Base currency (e.g., `"BTC"`) | | `to` | `string` | Yes | Quote currency (e.g., `"USD"`) | | `start` | `number` | No | Start timestamp (ms, unix epoch) | | `end` | `number` | No | End timestamp (ms, unix epoch) | **Output:** | Field | Type | Description | | --- | --- | --- | | `from` | `string` | Base currency | | `to` | `string` | Quote currency | | `start` | `number` | Start timestamp (if provided) | | `end` | `number` | End timestamp (if provided) | | `points` | `number[][]` | Array of `[timestamp, open, close, high, low, volume]` | Long time ranges are automatically downscaled to ≤100 data points. *** ## Indexer Tools _Requires: `useIndexer()`_ ### `getIndexerTokenBalance` Get token balance for **any** address via the WDK Indexer API. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `blockchain` | `enum` | Yes | Blockchain to query | | `token` | `enum` | Yes | Token (e.g., `"usdt"`, `"xaut"`, `"btc"`) | | `address` | `string` | Yes | Wallet address | **Output:** | Field | Type | Description | | --- | --- | --- | | `tokenBalance.blockchain` | `string` | Blockchain name | | `tokenBalance.token` | `string` | Token name | | `tokenBalance.amount` | `string` | Token balance | This queries the **indexed** balance, which may have slight delay compared to real-time blockchain state. For your own wallet's balance, use `getBalance` or `getTokenBalance` instead. --- ### `getTokenTransfers` Get token transfer history for an address. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `blockchain` | `enum` | Yes | Blockchain to query | | `token` | `enum` | Yes | Token (e.g., `"usdt"`, `"xaut"`, `"btc"`) | | `address` | `string` | Yes | Wallet address | | `limit` | `number` | No | Results per page (1–1000, default: 10) | | `fromTs` | `number` | No | Start timestamp (unix seconds) | | `toTs` | `number` | No | End timestamp (unix seconds) | | `sort` | `enum` | No | `"asc"` or `"desc"` (default: `"desc"`) | **Output:** | Field | Type | Description | | --- | --- | --- | | `transfers` | `object[]` | Array of transfer records | | `transfers[].blockchain` | `string` | Blockchain | | `transfers[].blockNumber` | `number` | Block number | | `transfers[].transactionHash` | `string` | Transaction hash | | `transfers[].token` | `string` | Token | | `transfers[].amount` | `string` | Transfer amount | | `transfers[].timestamp` | `number` | Unix timestamp | | `transfers[].from` | `string` | Sender address | | `transfers[].to` | `string` | Recipient address | *** ## Swap Tools _Requires: `registerProtocol()` with a swap protocol_ ### `quoteSwap` Get a swap quote without executing. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain with swap protocol | | `tokenIn` | `string` | Yes | Token to sell (e.g., `"USDT"`) | | `tokenOut` | `string` | Yes | Token to buy (e.g., `"USDC"`) | | `amount` | `string` | Yes | Amount in human-readable units | | `side` | `enum` | Yes | `"sell"` or `"buy"` | **Output:** | Field | Type | Description | | --- | --- | --- | | `protocol` | `string` | DEX protocol used | | `tokenIn` | `string` | Input token symbol | | `tokenOut` | `string` | Output token symbol | | `tokenInAmount` | `string` | Input amount (human-readable) | | `tokenOutAmount` | `string` | Output amount (human-readable) | | `fee` | `string` | Estimated fee | --- ### `swap` Execute a token swap. **Destructive** - requires user confirmation. **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain with swap protocol | | `tokenIn` | `string` | Yes | Token to sell | | `tokenOut` | `string` | Yes | Token to buy | | `amount` | `string` | Yes | Amount in human-readable units | | `side` | `enum` | Yes | `"sell"` or `"buy"` | | `to` | `string` | No | Recipient address (defaults to wallet) | **Output:** | Field | Type | Description | | --- | --- | --- | | `success` | `boolean` | Whether the swap succeeded | | `protocol` | `string` | DEX protocol used | | `hash` | `string` | Transaction hash | | `tokenIn` | `string` | Input token symbol | | `tokenOut` | `string` | Output token symbol | | `tokenInAmount` | `string` | Actual input amount | | `tokenOutAmount` | `string` | Actual output amount | | `fee` | `string` | Fee paid | This tool quotes the swap first, then shows a confirmation dialog before broadcasting. *** ## Bridge Tools _Requires: `registerProtocol()` with a bridge protocol_ ### `quoteBridge` Get a bridge quote without executing. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Source blockchain | | `targetChain` | `string` | Yes | Destination blockchain | | `token` | `string` | Yes | Token to bridge (e.g., `"USDT"`) | | `amount` | `string` | Yes | Amount in human-readable units | | `recipient` | `string` | No | Recipient on target chain (defaults to wallet) | **Output:** | Field | Type | Description | | --- | --- | --- | | `protocol` | `string` | Bridge protocol used | | `sourceChain` | `string` | Source blockchain | | `targetChain` | `string` | Destination blockchain | | `token` | `string` | Token symbol | | `amount` | `string` | Amount to bridge | | `fee` | `string` | Estimated gas fee | | `bridgeFee` | `string` | Bridge protocol fee | --- ### `bridge` Execute a cross-chain bridge. **Destructive** - requires user confirmation. **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Source blockchain | | `targetChain` | `string` | Yes | Destination blockchain | | `token` | `string` | Yes | Token to bridge | | `amount` | `string` | Yes | Amount in human-readable units | | `recipient` | `string` | No | Recipient on target chain (defaults to wallet) | **Output:** | Field | Type | Description | | --- | --- | --- | | `success` | `boolean` | Whether the bridge succeeded | | `protocol` | `string` | Bridge protocol used | | `hash` | `string` | Transaction hash | | `sourceChain` | `string` | Source blockchain | | `targetChain` | `string` | Destination blockchain | | `token` | `string` | Token symbol | | `amount` | `string` | Amount bridged | | `fee` | `string` | Gas fee paid | | `bridgeFee` | `string` | Bridge protocol fee paid | Bridge finality varies by target chain - tokens may take minutes to hours to arrive. *** ## Lending Tools _Requires: `registerProtocol()` with a lending protocol_ ### `quoteSupply` Get a fee estimate for supplying tokens to a lending pool. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain with lending protocol | | `token` | `string` | Yes | Token to supply | | `amount` | `string` | Yes | Amount in human-readable units | | `onBehalfOf` | `string` | No | Address to receive aTokens (defaults to wallet) | **Output:** | Field | Type | Description | | --- | --- | --- | | `protocol` | `string` | Lending protocol used | | `chain` | `string` | Blockchain | | `token` | `string` | Token symbol | | `amount` | `string` | Amount to supply | | `fee` | `string` | Estimated gas fee | --- ### `supply` Supply tokens to a lending pool. **Destructive** - requires user confirmation. Same input as `quoteSupply`. Output includes `success`, `protocol`, `hash`, `token`, `amount`, and `fee`. --- ### `quoteWithdraw` Estimate fee for withdrawing from a lending pool. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain | | `token` | `string` | Yes | Token to withdraw | | `amount` | `string` | Yes | Amount in human-readable units | **Output:** Same structure as `quoteSupply`. --- ### `withdraw` Withdraw tokens from a lending pool. **Destructive** - requires user confirmation. Same input as `quoteWithdraw`. Output includes `success`, `protocol`, `hash`, `token`, `amount`, and `fee`. --- ### `quoteBorrow` Estimate fee for borrowing from a lending pool. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain | | `token` | `string` | Yes | Token to borrow | | `amount` | `string` | Yes | Amount in human-readable units | **Output:** Same structure as `quoteSupply`. --- ### `borrow` Borrow tokens from a lending pool. **Destructive** - requires user confirmation. Same input as `quoteBorrow`. Output includes `success`, `protocol`, `hash`, `token`, `amount`, and `fee`. --- ### `quoteRepay` Estimate fee for repaying a loan. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain | | `token` | `string` | Yes | Token to repay | | `amount` | `string` | Yes | Amount in human-readable units | **Output:** Same structure as `quoteSupply`. --- ### `repay` Repay borrowed tokens. **Destructive** - requires user confirmation. Same input as `quoteRepay`. Output includes `success`, `protocol`, `hash`, `token`, `amount`, and `fee`. *** ## Fiat Tools _Requires: `registerProtocol()` with a fiat protocol_ ### `quoteBuy` Get a quote for purchasing crypto with fiat. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain for the fiat protocol | | `cryptoAsset` | `string` | Yes | Crypto asset code (e.g., `"eth"`, `"btc"`) | | `fiatCurrency` | `string` | Yes | Fiat currency code (e.g., `"USD"`, `"EUR"`) | | `amount` | `string` | Yes | Amount to quote | | `amountType` | `enum` | Yes | `"crypto"` or `"fiat"` | **Output:** | Field | Type | Description | | --- | --- | --- | | `protocol` | `string` | Fiat protocol used | | `cryptoAsset` | `string` | Crypto asset code | | `fiatCurrency` | `string` | Fiat currency code | | `cryptoAmount` | `string` | Crypto amount (base units) | | `fiatAmount` | `string` | Fiat amount (smallest units, e.g., cents) | | `fee` | `string` | Total fee (fiat smallest units) | | `rate` | `string` | Exchange rate | --- ### `buy` Execute a fiat-to-crypto purchase. **Destructive** - requires user confirmation. Same input as `quoteBuy`. Output includes `success`, `protocol`, redirect URL or transaction details. --- ### `quoteSell` Get a quote for selling crypto to fiat. **Read-only.** Same input structure as `quoteBuy`. Same output structure. --- ### `sell` Execute a crypto-to-fiat sale. **Destructive** - requires user confirmation. Same input as `quoteSell`. Output includes `success`, `protocol`, and transaction details. --- ### `getTransactionDetail` Get details of a fiat transaction by ID. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain | | `transactionId` | `string` | Yes | Transaction ID from the fiat provider | --- ### `getSupportedCryptoAssets` List crypto assets supported by the fiat provider. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain | --- ### `getSupportedFiatCurrencies` List fiat currencies supported by the fiat provider. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain | --- ### `getSupportedCountries` List countries supported by the fiat provider. **Read-only.** **Input:** | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `chain` | `enum` | Yes | Blockchain | *** ## Utility Exports Utility functions for converting between human-readable amounts and blockchain base units: ```javascript import { parseAmountToBaseUnits, formatBaseUnitsToAmount, AmountParseError, AMOUNT_ERROR_CODES } from '@tetherto/wdk-mcp-toolkit' ``` ### `parseAmountToBaseUnits(amount, decimals)` Converts a human-readable amount string to `BigInt` base units without floating-point errors. ```javascript parseAmountToBaseUnits('2.01', 6) // → 2010000n parseAmountToBaseUnits('100', 18) // → 100000000000000000000n parseAmountToBaseUnits('1,000.50', 6) // → 1000500000n ``` ### `formatBaseUnitsToAmount(baseUnits, decimals)` Converts `BigInt` base units to a human-readable string. ```javascript formatBaseUnitsToAmount(2010000n, 6) // → '2.01' formatBaseUnitsToAmount(100000000000000000000n, 18) // → '100' ``` ### `AmountParseError` Custom error class with a `code` property for programmatic handling: | Error Code | Description | | --- | --- | | `EMPTY_STRING` | Empty amount string | | `INVALID_FORMAT` | Not a valid number | | `NEGATIVE_AMOUNT` | Negative amounts not allowed | | `EXCESSIVE_PRECISION` | More decimal places than token supports | | `INVALID_DECIMALS` | Decimals value out of range | | `SCIENTIFIC_NOTATION_PRECISION` | Scientific notation exceeds precision | *** ## Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/ai/mcp-toolkit/configuration Description: Configure wallets, capabilities, tokens, protocols, and custom tools ## Server Setup Create a server with a name and version: ```javascript import { WdkMcpServer } from '@tetherto/wdk-mcp-toolkit' const server = new WdkMcpServer('my-server', '1.0.0') ``` The `WdkMcpServer` extends `McpServer` from the official [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk) with WDK-specific capabilities. All standard MCP server features are available. *** ## Wallet Configuration ### Enable WDK ```javascript server.useWdk({ seed: process.env.WDK_SEED }) ``` The `seed` is a BIP-39 mnemonic phrase used for key derivation across all registered blockchains. **Never hardcode seed phrases in source code.** Use environment variables or a secrets manager. The setup wizard generates a gitignored `.vscode/mcp.json` for local development. ### Register Wallets Register a wallet module for each blockchain you want to support: ```javascript import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerBtc from '@tetherto/wdk-wallet-btc' import WalletManagerSolana from '@tetherto/wdk-wallet-solana' // EVM chains - one module handles all EVM networks server.registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth-mainnet.g.alchemy.com/v2/KEY' }) server.registerWallet('polygon', WalletManagerEvm, { provider: 'https://polygon-rpc.com' }) // Bitcoin server.registerWallet('bitcoin', WalletManagerBtc, { network: 'bitcoin', host: 'electrum.blockstream.info', port: 50001 }) // Solana server.registerWallet('solana', WalletManagerSolana, { provider: 'https://api.mainnet-beta.solana.com' }) ``` Each `registerWallet()` call registers the chain name and makes it available to all wallet tools. For configuration details of each wallet module, see the [Wallet Modules](/sdk/wallet-modules/) documentation. *** ## Capabilities Enable optional capabilities before registering their tools: | Capability | Method | Requirement | Unlocks | | --- | --- | --- | --- | | **Pricing** | `server.usePricing()` | None | `PRICING_TOOLS` (2 tools) | | **Indexer** | `server.useIndexer({ apiKey })` | [WDK API key](/tools/indexer-api/get-started/#request-api-key) | `INDEXER_TOOLS` (2 tools) | | **Swap** | `server.registerProtocol(chain, label, SwapProtocol)` | Swap module installed | `SWAP_TOOLS` (2 tools) | | **Bridge** | `server.registerProtocol(chain, label, BridgeProtocol)` | Bridge module installed | `BRIDGE_TOOLS` (2 tools) | | **Lending** | `server.registerProtocol(chain, label, LendingProtocol)` | Lending module installed | `LENDING_TOOLS` (8 tools) | | **Fiat** | `server.registerProtocol(chain, label, FiatProtocol, config)` | Fiat module installed | `FIAT_TOOLS` (8 tools) | ### Pricing Fetches live prices from Bitfinex. No API key needed. ```javascript server.usePricing() ``` ### Indexer Enables querying token balances and transfer history for **any** address. Requires an API key. ```javascript server.useIndexer({ apiKey: process.env.WDK_INDEXER_API_KEY }) ``` ### Protocols DeFi protocols are registered per-chain: ```javascript import VeloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' import AaveProtocolEvm from '@tetherto/wdk-protocol-lending-aave-evm' import MoonPayProtocol from '@tetherto/wdk-protocol-fiat-moonpay' server.registerProtocol('ethereum', 'velora', VeloraProtocolEvm) server.registerProtocol('ethereum', 'usdt0', Usdt0ProtocolEvm) server.registerProtocol('ethereum', 'aave', AaveProtocolEvm) server.registerProtocol('ethereum', 'moonpay', MoonPayProtocol, { apiKey: process.env.MOONPAY_API_KEY, secretKey: process.env.MOONPAY_SECRET_KEY }) ``` *** ## Token Management ### Default Tokens USDT is auto-registered for supported chains via `DEFAULT_TOKENS`. You can query what's available: ```javascript server.getRegisteredTokens('ethereum') // ['USDT'] ``` ### Custom Tokens Register additional tokens with `registerToken()`: ```javascript server.registerToken('ethereum', 'DAI', { address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', decimals: 18 }) ``` Registered tokens are available to all tools that accept a `token` parameter (`getTokenBalance`, `transfer`, `quoteTransfer`, `swap`, etc.). *** ## Tool Registration ### Built-in Tool Arrays Each category exports three arrays for fine-grained control: | Export | Contents | | --- | --- | | `WALLET_TOOLS` | All 11 wallet tools | | `WALLET_READ_TOOLS` | 7 read-only wallet tools | | `WALLET_WRITE_TOOLS` | 4 wallet tools that modify state | | `PRICING_TOOLS` | All 2 pricing tools | | `INDEXER_TOOLS` | All 2 indexer tools | | `SWAP_TOOLS` | All 2 swap tools | | `SWAP_READ_TOOLS` | 1 read-only swap tool | | `SWAP_WRITE_TOOLS` | 1 swap tool that modifies state | | `BRIDGE_TOOLS` | All 2 bridge tools | | `BRIDGE_READ_TOOLS` | 1 read-only bridge tool | | `BRIDGE_WRITE_TOOLS` | 1 bridge tool that modifies state | | `LENDING_TOOLS` | All 8 lending tools | | `LENDING_READ_TOOLS` | 4 read-only lending tools | | `LENDING_WRITE_TOOLS` | 4 lending tools that modify state | | `FIAT_TOOLS` | All 8 fiat tools | | `FIAT_READ_TOOLS` | 6 read-only fiat tools | | `FIAT_WRITE_TOOLS` | 2 fiat tools that modify state | ### Read-Only Mode To allow an AI agent to query data without the ability to make transactions: ```javascript import { WALLET_READ_TOOLS, PRICING_TOOLS, INDEXER_TOOLS, SWAP_READ_TOOLS } from '@tetherto/wdk-mcp-toolkit' server.registerTools([ ...WALLET_READ_TOOLS, ...PRICING_TOOLS, ...INDEXER_TOOLS, ...SWAP_READ_TOOLS ]) ``` ### Individual Tool Registration You can also import and register tools individually: ```javascript import { getAddress, getBalance, getCurrentPrice } from '@tetherto/wdk-mcp-toolkit' server.registerTools([getAddress, getBalance, getCurrentPrice]) ``` ### Custom Tools Add your own MCP tools alongside the built-in ones using the standard `registerTool()` method (inherited from `McpServer`). See the [MCP SDK tools documentation](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/server.md#tools) for full details. ```javascript server.registerTool( 'myCustomTool', { title: 'My Custom Tool', description: 'Description of what this tool does', inputSchema: z.object({ param: z.string().describe('A required parameter') }), outputSchema: z.object({ result: z.string() }), annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false } }, async ({ param }) => { return { content: [{ type: 'text', text: `Result: ${param}` }], structuredContent: { result: param } } } ) ``` *** ## Environment Variables | Variable | Required | Description | | --- | --- | --- | | `WDK_SEED` | Yes | BIP-39 mnemonic for wallet key derivation | | `WDK_INDEXER_API_KEY` | No | API key for WDK Indexer | | `MOONPAY_API_KEY` | No | API key for MoonPay fiat on/off-ramp | | `MOONPAY_SECRET_KEY` | No | Secret key for MoonPay | *** ## Security Checklist **Self-custodial wallets require careful key management.** Follow these guidelines to protect user funds. - [ ] **Use a dedicated development wallet** - Never use production wallets with real funds for testing - [ ] **Never hardcode seed phrases** - Always use environment variables or `.vscode/mcp.json` (gitignored) - [ ] **Use `WALLET_READ_TOOLS` for untrusted agents** - Only register write tools when user confirmation is available - [ ] **Call `server.close()` on shutdown** - This disposes the WDK instance and wipes keys from memory - [ ] **Use `stdio` transport** - The default transport communicates only with the local AI client process - [ ] **Review MCP annotations** - Tools declare `readOnlyHint` and `destructiveHint` so clients can warn users appropriately - [ ] **Keep `.vscode/mcp.json` gitignored** - The setup wizard handles this automatically *** ## Need Help? *** ## Get Started URL: https://docs.wdk.tether.io/ai/mcp-toolkit/get-started Description: Install the MCP Toolkit and run your first AI-powered wallet server **Building a LangChain agent?** The `serve` command provides zero-config MCP server startup -- no server script needed. See [LangChain Integration](langchain). ## Setup Wizard The fastest way to get running. Clone the repository and let the wizard configure everything: ```bash title="Terminal" git clone https://github.com/tetherto/wdk-mcp-toolkit.git cd wdk-mcp-toolkit npm install npm run setup ``` The wizard will: 1. Prompt for your seed phrase (required) 2. Ask for optional API keys (WDK Indexer, MoonPay) 3. Generate `.vscode/mcp.json` with your credentials 4. Install required dependencies automatically Once complete, open the project in VS Code, start the MCP server from `.vscode/mcp.json`, and open the chatbot with **Cmd + Shift + I** (or run **Chat: Open Agent** from the Command Palette on non-Mac). **Security** - Your seed phrase is stored locally in `.vscode/mcp.json`, which is gitignored. Always use a **dedicated development wallet** with limited funds. *** ## Manual Setup If you prefer to set things up yourself or want to integrate the toolkit into an existing project: #### Install the toolkit Install the MCP Toolkit and the wallet modules you need: ```bash title="Terminal" npm install @tetherto/wdk-mcp-toolkit @modelcontextprotocol/sdk # Wallet modules (add any combination) npm install @tetherto/wdk-wallet-evm # Ethereum, Polygon, Arbitrum, etc. npm install @tetherto/wdk-wallet-btc # Bitcoin ``` #### Create your MCP server Create `index.js` with a basic multi-chain server: ```javascript title="index.js" import { WdkMcpServer, CHAINS, WALLET_TOOLS, PRICING_TOOLS } from '@tetherto/wdk-mcp-toolkit' import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerBtc from '@tetherto/wdk-wallet-btc' const server = new WdkMcpServer('my-wallet-server', '1.0.0') // 1. Enable WDK with your seed phrase server.useWdk({ seed: process.env.WDK_SEED }) // 2. Register wallet modules server.registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth.drpc.org' }) server.registerWallet('bitcoin', WalletManagerBtc, { network: 'bitcoin', host: 'electrum.blockstream.info', port: 50001 }) // 3. Enable pricing server.usePricing() // 4. Register tools and start server.registerTools([...WALLET_TOOLS, ...PRICING_TOOLS]) ``` #### Connect your AI client Add the MCP server to your AI tool's configuration: **Config path:** `.vscode/mcp.json` (project-level) ```json title=".vscode/mcp.json" { "servers": { "wdk": { "type": "stdio", "command": "node", "args": ["index.js"], "env": { "WDK_SEED": "your twelve word seed phrase here" } } } } ``` Then in VS Code: 1. Open `.vscode/mcp.json` and click **Start** above the server config 2. Open GitHub Copilot Chat and select **Agent mode** 3. Click **Tools** to verify the MCP tools are available → [VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) **Config path:** `.cursor/mcp.json` (project-level) ```json { "mcpServers": { "wdk": { "command": "node", "args": ["index.js"], "env": { "WDK_SEED": "your twelve word seed phrase here" } } } } ``` → [Cursor MCP documentation](https://cursor.com/docs/context/mcp) Run this command from your project directory: ```bash claude mcp add wdk -- node index.js ``` Set the environment variable separately: ```bash export WDK_SEED="your twelve word seed phrase here" ``` → [Claude Code MCP documentation](https://docs.anthropic.com/en/docs/claude-code/tutorials#set-up-model-context-protocol-mcp) **Config path:** `~/.codeium/windsurf/mcp_config.json` ```json { "mcpServers": { "wdk": { "command": "node", "args": ["index.js"], "env": { "WDK_SEED": "your twelve word seed phrase here" } } } } ``` → [Windsurf MCP documentation](https://docs.windsurf.com/windsurf/cascade/mcp) Add via Cline's MCP settings panel in VS Code, or create the config file directly: ```json { "mcpServers": { "wdk": { "command": "node", "args": ["index.js"], "env": { "WDK_SEED": "your twelve word seed phrase here" } } } } ``` → [Cline MCP documentation](https://github.com/cline/cline#add-context) **Config path:** `~/.continue/config.yaml` Add to the `mcpServers` section with the command and arguments for your server: ``` command: node args: ["index.js"] env: WDK_SEED: "your twelve word seed phrase here" ``` → [Continue MCP documentation](https://docs.continue.dev/customize/mcp-tools) #### Try it out Ask your AI assistant: ``` What's my ethereum address? ``` ``` Check my BTC balance ``` ``` What's the current price of ETH in USD? ``` ``` Send 10 USDT to 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7 on ethereum ``` Write operations (sending, swapping, bridging) will show a **confirmation dialog** before executing. You must explicitly approve each transaction. *** ## Optional Capabilities Add more capabilities by installing additional packages and enabling them on the server: ```javascript title="Additional capabilities" import { INDEXER_TOOLS, SWAP_TOOLS, BRIDGE_TOOLS, LENDING_TOOLS, FIAT_TOOLS } from '@tetherto/wdk-mcp-toolkit' import VeloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' import AaveProtocolEvm from '@tetherto/wdk-protocol-lending-aave-evm' import MoonPayProtocol from '@tetherto/wdk-protocol-fiat-moonpay' // Indexer - transaction history server.useIndexer({ apiKey: process.env.WDK_INDEXER_API_KEY }) // DeFi protocols server.registerProtocol('ethereum', 'velora', VeloraProtocolEvm) server.registerProtocol('ethereum', 'usdt0', Usdt0ProtocolEvm) server.registerProtocol('ethereum', 'aave', AaveProtocolEvm) server.registerProtocol('ethereum', 'moonpay', MoonPayProtocol, { secretKey: process.env.MOONPAY_SECRET_KEY, apiKey: process.env.MOONPAY_API_KEY }) // Register the corresponding tools server.registerTools([ ...INDEXER_TOOLS, ...SWAP_TOOLS, ...BRIDGE_TOOLS, ...LENDING_TOOLS, ...FIAT_TOOLS ]) ``` *** ## Environment Variables | Variable | Required | Description | | --- | --- | --- | | `WDK_SEED` | Yes | BIP-39 seed phrase for wallet derivation | | `WDK_INDEXER_API_KEY` | No | Enables `INDEXER_TOOLS` - [get a key](/tools/indexer-api/get-started/#request-api-key) | | `MOONPAY_API_KEY` | No | Enables `FIAT_TOOLS` - [MoonPay Dashboard](https://dashboard.moonpay.com/) | | `MOONPAY_SECRET_KEY` | No | Required with `MOONPAY_API_KEY` | *** ## Next Steps * [**Configuration**](configuration) - Wallets, tokens, protocols, custom tools, and security * [**API Reference**](api-reference) - All 35 built-in MCP tools with parameters and schemas *** ## Need Help? *** ## LangChain Integration URL: https://docs.wdk.tether.io/ai/mcp-toolkit/langchain Description: Use WDK MCP tools in LangChain agents with zero-config server startup You can use the WDK MCP Toolkit as a tool provider for [LangChain](https://www.langchain.com/) agents in both Python and TypeScript. LangChain's `MultiServerMCPClient` spawns the MCP server as a subprocess and converts WDK tools into LangChain-compatible tools, giving your agent access to wallet operations, pricing, swaps, bridges, lending, and more. This integration uses the `serve` CLI command, which starts a fully configured MCP server on stdio with no server script required. This approach uses LangChain's MCP adapters to connect to the WDK MCP server. WDK does not ship a native LangChain integration, it leverages the standard MCP protocol that LangChain already supports. **Want more control?** The `serve` command is the fastest way to get running, but you can also [write your own MCP server](get-started#manual-setup) with the programmatic API for full control over wallets, tools, and protocols. Then point LangChain's `MultiServerMCPClient` at it using `node your-server.js` instead of the `serve` command. *** ## The `serve` Command The `serve` command provides zero-config MCP server startup so you don't need to write a server script: ```bash title="Terminal" npx @tetherto/wdk-mcp-toolkit serve ``` Pass `WDK_SEED` to enable wallet operations, or omit it to run with pricing tools only: ```bash title="Terminal" # With wallet operations WDK_SEED="your twelve word seed phrase here" npx @tetherto/wdk-mcp-toolkit serve # Pricing-only mode (no seed required) npx @tetherto/wdk-mcp-toolkit serve ``` ### Default Chains By default, `serve` enables **three chains**: Ethereum, Arbitrum, and Bitcoin. For each enabled chain it dynamically imports the required wallet package and skips any that aren't installed. You can change the enabled set with the `WDK_CHAINS` environment variable. ### Built-in Registry The command has built-in definitions for 13 chains and 4 protocol modules. When a chain is enabled and its package is installed, the wallet is registered automatically. Protocol modules are also auto-registered when their packages are installed and at least one of their target chains is enabled. | Module | Registers | Default | | --- | --- | --- | | [`@tetherto/wdk-wallet-evm`](/sdk/wallet-modules/wallet-evm) | Ethereum, Arbitrum, Polygon, Optimism, Base, Avalanche, BNB, Plasma, Spark | Ethereum + Arbitrum enabled | | [`@tetherto/wdk-wallet-btc`](/sdk/wallet-modules/wallet-btc) | Bitcoin | Enabled | | [`@tetherto/wdk-wallet-solana`](/sdk/wallet-modules/wallet-solana) | Solana | Not enabled by default | | [`@tetherto/wdk-wallet-ton`](/sdk/wallet-modules/wallet-ton) | TON | Not enabled by default | | [`@tetherto/wdk-wallet-tron`](/sdk/wallet-modules/wallet-tron) | Tron | Not enabled by default | | [`@tetherto/wdk-protocol-swap-velora-evm`](/sdk/swap-modules/swap-velora-evm) | Swap tools (Ethereum, Arbitrum) | -- | | [`@tetherto/wdk-protocol-bridge-usdt0-evm`](/sdk/bridge-modules/bridge-usdt0-evm) | Bridge tools (Ethereum, Arbitrum) | -- | | [`@tetherto/wdk-protocol-lending-aave-evm`](/sdk/lending-modules/lending-aave-evm) | Lending tools (Ethereum) | -- | | [`@tetherto/wdk-protocol-fiat-moonpay`](/sdk/fiat-modules/fiat-moonpay) | Fiat tools (Ethereum) | Requires `MOONPAY_*` env vars | Missing packages are silently skipped. Install only the modules you need and `serve` will pick them up. For chains or protocols **not** in the built-in registry, use a [custom config file](#custom-config-file). *** ## Quick Start #### Install dependencies ```bash title="Terminal" pip install langchain-mcp-adapters langgraph langchain-openai ``` #### Create your agent ```python title="agent.py" import asyncio from langchain_mcp_adapters.client import MultiServerMCPClient from langgraph.prebuilt import create_react_agent from langchain_openai import ChatOpenAI async def main(): client = MultiServerMCPClient({ "wdk": { "transport": "stdio", "command": "npx", "args": ["-y", "@tetherto/wdk-mcp-toolkit", "serve"], "env": { "WDK_SEED": "your twelve word seed phrase here", "WDK_MCP_ELICITATION": "false", }, } }) tools = await client.get_tools() agent = create_react_agent(ChatOpenAI(model="gpt-4o"), tools) result = await agent.ainvoke({ "messages": [{"role": "user", "content": "What is my Ethereum address?"}] }) print(result["messages"][-1].content) await client.close() asyncio.run(main()) ``` #### Run it ```bash title="Terminal" export OPENAI_API_KEY="sk-..." python agent.py ``` #### Install dependencies ```bash title="Terminal" npm install @langchain/mcp-adapters @langchain/langgraph @langchain/core @langchain/openai ``` #### Create your agent ```typescript title="agent.ts" import { MultiServerMCPClient } from "@langchain/mcp-adapters"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; import { ChatOpenAI } from "@langchain/openai"; const client = new MultiServerMCPClient({ wdk: { transport: "stdio", command: "npx", args: ["-y", "@tetherto/wdk-mcp-toolkit", "serve"], env: { WDK_SEED: "your twelve word seed phrase here", WDK_MCP_ELICITATION: "false", }, }, }); const tools = await client.getTools(); const agent = createReactAgent({ llm: new ChatOpenAI({ model: "gpt-4o" }), tools, }); const result = await agent.invoke({ messages: [ { role: "user", content: "What is the current price of Bitcoin?" }, ], }); console.log(result.messages[result.messages.length - 1].content); await client.close(); ``` #### Run it ```bash title="Terminal" export OPENAI_API_KEY="sk-..." npx tsx agent.ts ``` **Security** -- Always use a dedicated development wallet with limited funds. Set `WDK_MCP_ELICITATION` to `"false"` for programmatic agents since elicitation dialogs require a human in the loop. *** ## Configuration ### Environment Variables Control `serve` behavior through environment variables: | Variable | Required | Default | Description | | --- | --- | --- | --- | | `WDK_SEED` | No | -- | BIP-39 seed phrase. If omitted, only pricing tools are available | | `WDK_CHAINS` | No | `ethereum,arbitrum,bitcoin` | Comma-separated list of chains to enable | | `WDK_MCP_ELICITATION` | No | `true` | Set to `"false"` for programmatic agents that cannot handle confirmation dialogs | | `WDK_RPC_\` | No | Built-in defaults | Override the RPC endpoint for a chain (e.g. `WDK_RPC_ETHEREUM=https://my-rpc.com`) | | `WDK_CONFIG` | No | -- | Path to a `wdk.config.json` file for custom chains and protocols | | `WDK_INDEXER_API_KEY` | No | -- | Enables indexer tools for balance and transfer history queries | | `MOONPAY_API_KEY` | No | -- | Enables fiat on/off-ramp tools | | `MOONPAY_SECRET_KEY` | No | -- | Required with `MOONPAY_API_KEY` | ### Custom Config File For chains or protocols not in the built-in defaults, create a `wdk.config.json` and pass its path via `WDK_CONFIG`: ```bash title="Terminal" WDK_CONFIG=./wdk.config.json WDK_SEED="..." npx @tetherto/wdk-mcp-toolkit serve ``` ```json title="wdk.config.json" { "chains": { "zksync": { "module": "@myorg/wdk-wallet-zksync", "config": { "provider": "https://mainnet.era.zksync.io" } }, "ethereum": { "config": { "provider": "https://my-private-rpc.com" } } }, "protocols": [ { "module": "@myorg/wdk-protocol-swap-custom", "label": "custom-swap", "type": "swap", "chains": ["zksync"] } ], "enabledChains": ["ethereum", "zksync", "bitcoin"] } ``` | Field | Description | | --- | --- | | `chains` | Add new chains or override config for built-in ones. New chains require a `module` field; overrides for existing chains can omit it | | `protocols` | Add custom protocols. Each entry requires `module`, `label`, and `chains`. The `type` field (`swap`, `bridge`, `lending`, `fiat`) maps to the corresponding built-in tool set | | `enabledChains` | Overrides `WDK_CHAINS` env var. If omitted, `WDK_CHAINS` is used | *** ## LLM Provider Support Both the Python and TypeScript examples support OpenAI and Anthropic. Set the corresponding environment variable and install the matching package: | Provider | Environment Variable | Python Package | TypeScript Package | | --- | --- | --- | --- | | OpenAI | `OPENAI_API_KEY` | `langchain-openai` | `@langchain/openai` | | Anthropic | `ANTHROPIC_API_KEY` | `langchain-anthropic` | `@langchain/anthropic` | The examples auto-detect which provider to use based on which API key is set. If both are set, OpenAI takes priority. **Full examples** -- See the complete interactive agent examples with conversation loops on GitHub: [`mcp-toolkit/langchain/python/`](https://github.com/tetherto/wdk-examples/tree/main/mcp-toolkit/langchain/python) and [`mcp-toolkit/langchain/typescript/`](https://github.com/tetherto/wdk-examples/tree/main/mcp-toolkit/langchain/typescript). *** ## Need Help? *** ## OpenClaw (Community Skill) URL: https://docs.wdk.tether.io/ai/openclaw Description: Give your OpenClaw AI agent a self-custodial WDK wallet in minutes The WDK skill for OpenClaw is a community skill, developed and maintained independently by a third-party contributor. Tether and the WDK Team do not endorse or assume responsibility for its code, security, or maintenance. Use your own judgment and proceed at your own risk. Artificial intelligence has inherent risks and limitations. You assume full responsibility for any reliance and use of artificial intelligence and agree that any such reliance and use is entirely at your own risk. [OpenClaw](https://openclaw.ai) is an open-source AI agent platform. With the WDK community skill, your OpenClaw agent can create wallets, send transactions, swap tokens, bridge assets, and interact with DeFi protocols. Everything stays self-custodial. The WDK community skill follows the [AgentSkills specification](https://agentskills.io/specification), so it works with any compatible agent platform. This page covers the OpenClaw-specific setup. ## Install the WDK Community Skill Install from [ClawHub](https://clawhub.ai/HumanRupert/tether-wallet-development-kit): ```bash npx clawhub install tether-wallet-development-kit ``` This installs the skill into your workspace's `skills/` directory. OpenClaw picks it up automatically on the next session. You might see a VirusTotal warning during installation. It flags the skill as suspicious because it handles crypto keys and calls external APIs. This is normal for any wallet SDK skill, nevertheless review the skill's source code on [ClawHub](https://clawhub.ai/HumanRupert/tether-wallet-development-kit) before proceeding. We plan to publish the official WDK skill to its own GitHub repository. Once that's live, you'll also be able to install via `git clone`. ## Configuration The WDK community skill does not require environment variables. Your agent will ask for a seed phrase in conversation when it needs to create or recover a wallet. The skill passes the seed phrase as a constructor parameter in code rather than reading it from configuration. Your seed phrase controls real funds. Never share it, commit it to version control, or expose it in logs. The skill instructs agents to never log or expose seed phrases or private keys. ## Verify It Works Start a new OpenClaw session and try a simple prompt: ``` Create a multi-chain wallet with Ethereum and Bitcoin support, then show me the addresses. ``` The agent should use the WDK community skill to create wallet accounts and return the generated addresses. All write operations (transactions, swaps, bridges) require your explicit confirmation before executing. ![OpenClaw creating a multi-chain wallet using the WDK skill](/assets/openclaw-wallet-output.png) *Example output from the WDK skill creating a multi-chain wallet* ## What Your Agent Can Do Once the skill is loaded, your agent can: - **Create wallets** across 20+ blockchains (EVM, Bitcoin, Solana, TON, Tron, Spark) - **Send transactions** and token transfers - **Swap tokens** via DEX aggregators (Velora, StonFi) - **Bridge assets** cross-chain with USDT0 - **Lend and borrow** through Aave V3 - **Buy and sell crypto** via MoonPay fiat on/off-ramps For the full list of capabilities and how skills work, see [Agent Skills](agent-skills). ## Security Risks and Safety Precautions OpenClaw is powerful because it runs on your system and can take real actions like creating files, fetching data from the web, and executing transactions. That same power can become a security risk if you're not careful about how and where you run it. This isn't a flaw in OpenClaw. It's what happens when you give any AI agent direct system access. Knowing these risks lets you use OpenClaw safely. ### Why running OpenClaw locally requires caution When you run OpenClaw on your own computer or a virtual server, you're allowing a chat interface to trigger actions on that system. This is a concern if your bot: - Has access to sensitive directories - Runs with elevated privileges - Is connected to a publicly accessible chat - Receives poorly scoped instructions It can unintentionally modify files, overwrite data, or expose information you didn't intend to share. The risk isn't that OpenClaw is malicious. The risk is that it will do exactly what it's told, even when the instruction is vague or unsafe. ### How to use OpenClaw safely To reduce risk, here are some practical safety measures: - Run OpenClaw as a non-privileged user - Keep its working files in a dedicated directory - Avoid connecting it to public or shared chats initially - Be explicit when asking it to read or write files - Test new capabilities on a disposable system or VM Think of OpenClaw the same way you'd think about running scripts on your system: powerful and useful, but something you need to be careful with. ### Inherent Limitations of Artificial Intelligence OpenClaw makes use of artificial intelligence and machine learning technologies. While the use of artificial intelligence and machine learning enables capabilities, it also involves inherent limitations and risks. These include: 1. The potential for inaccurate, incomplete, unexpected or misleading outputs or actions (including so-called hallucinations) 2. The risk that outputs or actions may contain biases 3. The possibility of errors related to document quality or text recognition of inputs 4. The possibility that the outputs may suggest specific immediate or near term actions that should not be relied upon 5. The risk that OpenClaw may take unexpected actions (including the sending of assets) ## Next Steps - [Agent Skills](agent-skills) - Full capabilities, how skills work, and a comparison with other agentic wallet solutions - [MCP Toolkit](mcp-toolkit/) - Programmatic wallet access for MCP-compatible agents - [OpenClaw Skills Documentation](https://docs.openclaw.ai/tools/skills) - How OpenClaw discovers and loads skills *** ## Need Help? *** ## x402 URL: https://docs.wdk.tether.io/ai/x402 Description: Accept and make instant USD₮ payments over HTTP using WDK self-custodial wallets ## What Is x402? [x402](https://www.x402.org) is an open payment protocol, [originally developed by Coinbase](https://docs.x402.org/), that gives the long-reserved [HTTP 402 Payment Required](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/402) status code a concrete, blockchain-native meaning: if you want this resource, pay for it. No accounts, API keys, or checkout flows. Just plain HTTP. This matters for AI agents because they need to pay for resources programmatically. x402 makes payment a first-class part of the web stack, so an agent can discover a price, sign a payment, and receive a resource in a single request-response cycle. ### The Three Roles | Role | Description | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | **Client (Buyer)** | The entity requesting a paid resource. Can be a human application, an AI agent, or any service with a wallet. | | **Resource Server (Seller)** | The API or service providing the paid resource. Defines payment requirements and returns `402` for unpaid requests. | | **Facilitator** | An intermediary that verifies payment signatures and submits transactions on-chain. Never holds funds, only executes signed authorizations. | ### How the Protocol Works #### Client requests a resource A standard HTTP request. `GET`, `POST`, whatever your API expects. #### Server responds with 402 Payment Required The response body describes what to pay: amount, token, network, and recipient address. ```json { "x402Version": 1, "accepts": [{ "scheme": "exact", "network": "eip155:9745", "maxAmountRequired": "1000000", "asset": "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", "resource": "https://api.example.com/data", "payTo": "0x1234...abcd" }] } ``` #### Client signs a payment The client constructs an [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) `transferWithAuthorization` and signs it with their wallet. No tokens leave the wallet yet. It's a signed intent, not a transfer. #### Client retries with payment header The signed payload goes in the `X-PAYMENT` header on the same request. #### Facilitator verifies The server forwards the payload to the facilitator's `/verify` endpoint. The facilitator checks that the signature is valid, the amount is sufficient, and the payer has funds. No money moves yet. #### Server performs the work Inference, database query, generation, whatever the resource requires. This only happens after verification succeeds. #### Facilitator settles on-chain The server calls the facilitator's `/settle` endpoint. The facilitator submits the signed authorization on-chain, transferring tokens from buyer to seller. #### Server returns the resource `200 OK` with the result in the body and a settlement receipt in the `X-PAYMENT-RESPONSE` header. For the full protocol specification, see [x402.org](https://www.x402.org) and the [x402 GitHub repository](https://github.com/coinbase/x402). ## How to Use x402 With WDK WDK wallets work as drop-in signers for x402. `WalletAccountEvm` satisfies the client x402 signer interface directly. Self-custodial x402 payments on any EVM chain. This guide walks through three things: 1. **Client (Buyer)** - Pay for x402-protected resources using a WDK wallet 2. **Server with Hosted Facilitator** - Accept x402 payments by delegating verification and settlement to a third-party facilitator 3. **Server with Self-Hosted Facilitator** - Run verification and settlement in-process using a WDK wallet, with no external dependencies The x402 integration described on this page uses community-developed modules and third-party facilitator services. Tether does not endorse, operate, or assume legal or financial responsibility for any third-party facilitator. You are solely responsible for using any service. Artificial intelligence and blockchain transactions carry inherent risks and limitations. ### Recommended Chains x402 with WDK works on any EVM chain where USD₮0 is deployed (see full list at [docs.usdt0.to](https://docs.usdt0.to/technical-documentation/deployments)). However, we recommend **Plasma** and **Stable** for x402 payments. Both chains are purpose-built for USD₮ transfers with near-instant finality and near-zero fees. Agents only need to hold USD₮. | Chain | CAIP-2 | RPC | USD₮0 Contract | Explorer | | --- | --- | --- | --- | --- | | **Plasma** | `eip155:9745` | `https://rpc.plasma.to` | `0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb` | [plasmascan.to](https://plasmascan.to/address/0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb) | | **Stable** | `eip155:988` | `https://rpc.stable.xyz` | `0x779Ded0c9e1022225f8E0630b35a9b54bE713736` | [stablescan.xyz](https://stablescan.xyz/address/0x779Ded0c9e1022225f8E0630b35a9b54bE713736) | *** ## Client: Paying for Resources See the full working client example at [`x402/client.js`](https://github.com/SemanticPay/x402-usdt0-demo/blob/main/x402/client.js). ```bash npm install @tetherto/wdk-wallet-evm @x402/fetch @x402/evm ``` #### Create a wallet ```javascript import WalletManagerEvm from "@tetherto/wdk-wallet-evm"; const account = await new WalletManagerEvm(process.env.SEED_PHRASE, { provider: "https://rpc.plasma.to", // or "https://rpc.stable.xyz" }).getAccount(); ``` #### Register with x402 `WalletAccountEvm` satisfies the `ClientEvmSigner` interface directly. No adapter needed. ```javascript import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; import { registerExactEvmScheme } from "@x402/evm/exact/client"; const client = new x402Client(); registerExactEvmScheme(client, { signer: account }); const fetchWithPayment = wrapFetchWithPayment(fetch, client); ``` #### Make a paid request `fetchWithPayment` intercepts any `402` response, signs an EIP-3009 authorization with your WDK wallet, and retries automatically. ```javascript const response = await fetchWithPayment("https://api.example.com/weather", { method: "GET", }); const data = await response.json(); console.log("Response:", data); ``` Your seed phrase controls your funds. Never commit it to version control. Use environment variables or a secrets manager. ### Getting USD₮0 on Plasma or Stable Before you can make x402 payments, your wallet needs USD₮0 on the target chain. If you hold USD₮ on Ethereum (or any supported EVM chain), bridge it using `@tetherto/wdk-protocol-bridge-usdt0-evm`. The bridge uses [LayerZero](https://layerzero.network) for secure cross-chain transfers. USD₮ on Ethereum is automatically converted to USD₮0 on the destination chain. ```bash npm install @tetherto/wdk-wallet-evm @tetherto/wdk-protocol-bridge-usdt0-evm ``` #### Bridge USD₮ from Ethereum to Plasma / Stable #### Create wallet and bridge protocol ```javascript import WalletManagerEvm from "@tetherto/wdk-wallet-evm"; import Usdt0ProtocolEvm from "@tetherto/wdk-protocol-bridge-usdt0-evm"; const account = await new WalletManagerEvm(process.env.SEED_PHRASE, { provider: "https://eth.drpc.org", }).getAccount(); const bridge = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 100000000000000n, // Max 0.0001 ETH in bridge fees }); ``` #### Approve and get a quote (recommended) ```javascript const USDT_ETHEREUM = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; const USDT0_ETHEREUM_OFT = "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee"; const amount = 10000000n; // 10 USD₮ (6 decimals) await account.approve({ token: USDT_ETHEREUM, spender: USDT0_ETHEREUM_OFT, amount, }); const quote = await bridge.quoteBridge({ targetChain: "plasma", // or "stable" recipient: await account.getAddress(), token: USDT_ETHEREUM, amount, oftContractAddress: USDT0_ETHEREUM_OFT, }); console.log("Total cost:", Number(quote.fee + quote.bridgeFee) / 1e18, "ETH"); ``` #### Execute the bridge Use the same approved `USDT0_ETHEREUM_OFT` spender when executing the bridge. ```javascript const result = await bridge.bridge({ targetChain: "plasma", // or "stable" recipient: await account.getAddress(), token: USDT_ETHEREUM, amount, oftContractAddress: USDT0_ETHEREUM_OFT, }); console.log("Bridge tx:", result.hash); ``` USD₮0 arrives on the destination chain within a few minutes. You can bridge from any of 25+ supported EVM chains, not just Ethereum. Point your wallet at the source chain's RPC and use the [USD₮ token address](https://tether.to/es/supported-protocols/) on that chain. See the full [bridge module documentation](/sdk/bridge-modules/bridge-usdt0-evm). *** ## Server: Accepting Payments (Hosted Facilitator) Your server delegates verification and settlement to a hosted facilitator. You never interact with the chain directly. **About the Semantic facilitator:** [Semantic](https://docs.semanticpay.io) operates a public USD₮-enabled x402 facilitator at `https://x402.semanticpay.io`. This is a third-party service not operated, endorsed, or guaranteed by Tether. The x402 protocol is an open standard. Anyone can build and host their own facilitator. For the API reference, see the [Semantic facilitator docs](https://docs.semanticpay.io/endpoints). See the full working server example at [`x402/server.js`](https://github.com/SemanticPay/x402-usdt0-demo/blob/main/x402/server.js). ```bash npm install @tetherto/wdk-wallet-evm @x402/express @x402/evm @x402/core express dotenv ``` #### Derive your receiving address ```javascript import WalletManagerEvm from "@tetherto/wdk-wallet-evm"; const account = await new WalletManagerEvm(process.env.SEED_PHRASE, { provider: "https://rpc.plasma.to", // or "https://rpc.stable.xyz" }).getAccount(); const sellerAddress = await account.getAddress(); ``` #### Create the facilitator client ```javascript import { HTTPFacilitatorClient } from "@x402/core/server"; const facilitatorClient = new HTTPFacilitatorClient({ url: "https://x402.semanticpay.io/", }); ``` #### Configure payment middleware ```javascript import express from "express"; import { paymentMiddleware, x402ResourceServer } from "@x402/express"; import { ExactEvmScheme } from "@x402/evm/exact/server"; const PLASMA_NETWORK = "eip155:9745"; // or "eip155:988" for Stable const USDT0_PLASMA = "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb"; // or "0x779Ded0c9e1022225f8E0630b35a9b54bE713736" on Stable const app = express(); app.use( paymentMiddleware( { "GET /weather": { accepts: [ { scheme: "exact", network: PLASMA_NETWORK, price: { amount: "1000", // $0.001 (6 decimals) asset: USDT0_PLASMA, extra: { name: "USDT0", version: "1", decimals: 6 }, }, payTo: sellerAddress, }, ], description: "Weather data", mimeType: "application/json", }, }, new x402ResourceServer(facilitatorClient).register( PLASMA_NETWORK, new ExactEvmScheme(), ), ), ); ``` The `extra` fields are passed to the buyer for EIP-712 signature construction. `name` and `version` must match what the on-chain USD₮0 contract expects. #### Add your routes ```javascript // Gated - requires payment app.get("/weather", (req, res) => { res.json({ weather: "sunny", temperature: 70 }); }); // Not gated - no payment config app.get("/health", (req, res) => { res.json({ status: "ok" }); }); app.listen(4021); ``` Routes not listed in the middleware config behave like normal Express routes. ### Multi-Chain (Plasma + Stable) To accept payments on both chains, add both networks to the `accepts` array and register both with the resource server. The buyer's client picks whichever network it has funds on. ```javascript const NETWORKS = { plasma: { network: "eip155:9745", usdt0: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb" }, stable: { network: "eip155:988", usdt0: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736" }, }; const resourceServer = new x402ResourceServer(facilitatorClient) .register(NETWORKS.plasma.network, new ExactEvmScheme()) .register(NETWORKS.stable.network, new ExactEvmScheme()); // In paymentMiddleware config: // accepts: [ // { scheme: "exact", network: NETWORKS.plasma.network, price: priceOnChain("plasma"), payTo }, // { scheme: "exact", network: NETWORKS.stable.network, price: priceOnChain("stable"), payTo }, // ] ``` ### Lifecycle Events The Semantic facilitator supports an optional `X-Event-Callback` header. When provided, the facilitator POSTs real-time events to your callback URL during verification and settlement. | Type | When | Key Fields | | ------------------ | --------------------------------- | ----------------------------------- | | `verify_started` | Facilitator begins verifying | `details.network`, `details.checks` | | `verify_completed` | Verification finished | `details.isValid` | | `verify_failed` | Verification error | `details.error` | | `settle_started` | Broadcasting on-chain transaction | `details.network` | | `settle_completed` | Transaction confirmed | `details.transactionHash` | | `settle_failed` | Settlement error | `details.error` | ```javascript const facilitatorClient = new HTTPFacilitatorClient({ url: "https://x402.semanticpay.io/", fetch: (url, init) => fetch(url, { ...init, headers: { ...init?.headers, "X-Event-Callback": "http://localhost:4021/payment-events" }, }), }); ``` Events are fire-and-forget. If the callback URL is unreachable, events are silently dropped. *** ## Server: Self-Hosted Facilitator (In-Process) Instead of relying on a hosted facilitator, you can run verification and settlement in-process using the `@semanticio/wdk-wallet-evm-x402-facilitator` community module. This wraps a WDK wallet as an x402 `FacilitatorEvmSigner`. Your server handles the entire payment lifecycle locally. Unlike the hosted Semantic facilitator (Plasma and Stable only), a self-hosted facilitator works with **any EVM chain where USD₮0 is deployed**. See the full deployment list at [docs.usdt0.to](https://docs.usdt0.to/technical-documentation/deployments). `@semanticio/wdk-wallet-evm-x402-facilitator` is a community module developed and maintained by [Semantic Pay](https://www.semanticpay.io). Tether does not endorse, audit, or assume responsibility for this module. It is currently in beta. Test thoroughly before using in production. See the full working self-hosted server example at [`x402/server-inprocess.js`](https://github.com/SemanticPay/x402-usdt0-demo/blob/main/x402/server-inprocess.js). ```bash npm install @semanticio/wdk-wallet-evm-x402-facilitator @tetherto/wdk-wallet-evm @x402/core @x402/evm @x402/express express dotenv ``` #### Create the facilitator signer The facilitator wallet submits settlement transactions on-chain. It needs gas tokens on the target chain. ```javascript import WalletManagerEvm from "@tetherto/wdk-wallet-evm"; import WalletAccountEvmX402Facilitator from "@semanticio/wdk-wallet-evm-x402-facilitator"; const walletAccount = await new WalletManagerEvm(process.env.FACILITATOR_MNEMONIC, { provider: process.env.RPC_URL, // Any EVM chain with USD₮0 }).getAccount(); const evmSigner = new WalletAccountEvmX402Facilitator(walletAccount); ``` The facilitator wallet and the seller wallet can use different seed phrases. The facilitator pays gas; the seller receives USD₮. The facilitator wallet must have enough native token to pay gas. #### Initialize the facilitator ```javascript import { x402Facilitator } from "@x402/core/facilitator"; import { registerExactEvmScheme } from "@x402/evm/exact/facilitator"; const facilitator = new x402Facilitator() .onAfterVerify(async (ctx) => { console.log("[verify]", ctx.result?.isValid ? "valid" : "invalid"); }) .onAfterSettle(async (ctx) => { console.log("[settle] tx:", ctx.result?.transaction); }); registerExactEvmScheme(facilitator, { signer: evmSigner, networks: process.env.NETWORK_ID, // e.g. "eip155:9745" }); ``` Available hooks: `onBeforeVerify`, `onAfterVerify`, `onBeforeSettle`, `onAfterSettle`. All are `async` and receive a context object with the payment payload and result. #### Wire into Express Same `paymentMiddleware` pattern, but pass the in-process `facilitator` directly instead of an `HTTPFacilitatorClient`. ```javascript import { paymentMiddleware, x402ResourceServer } from "@x402/express"; import { ExactEvmScheme } from "@x402/evm/exact/server"; const NETWORK = process.env.NETWORK_ID || "eip155:9745"; // Stable: "eip155:988" const USDT0 = process.env.USDT0_ADDRESS || "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb"; // Stable: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736" const resourceServer = new x402ResourceServer(facilitator).register( NETWORK, new ExactEvmScheme(), ); app.use( paymentMiddleware( { "GET /weather": { accepts: [{ scheme: "exact", network: NETWORK, price: { amount: "1000", asset: USDT0, extra: { name: "USDT0", version: "1", decimals: 6 } }, payTo: process.env.PAY_TO_ADDRESS, }], description: "Weather data", mimeType: "application/json", }, }, resourceServer, ), ); ``` *** ## Summary | Role | Packages | Notes | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | **Buyer (Client)** | `@tetherto/wdk-wallet-evm`, `@x402/fetch`, `@x402/evm` | `WalletAccountEvm` satisfies `ClientEvmSigner` directly. | | **Seller (Hosted)** | `@tetherto/wdk-wallet-evm`, `@x402/express`, `@x402/evm`, `@x402/core` | Delegates to a hosted facilitator. Semantic supports Plasma and Stable. | | **Seller (Self-Hosted)** | `@tetherto/wdk-wallet-evm`, `@semanticio/wdk-wallet-evm-x402-facilitator`, `@x402/core`, `@x402/evm`, `@x402/express` | In-process facilitator. Any USD₮0 chain. | *** ## Resources - [x402 Protocol Spec](https://www.x402.org) - The open standard specification - [x402 GitHub](https://github.com/coinbase/x402) - Reference implementations and examples - [Semantic Facilitator Docs](https://docs.semanticpay.io) - API reference for the hosted facilitator - [Self-Hosted Facilitator Module](https://www.npmjs.com/package/@semanticio/wdk-wallet-evm-x402-facilitator) - Community in-process facilitator - [x402-usdt0 Demo](https://github.com/SemanticPay/x402-usdt0-demo) - Full working buyer + seller demo - [WDK EVM Wallet Module](/sdk/wallet-modules/wallet-evm) - WDK EVM wallet documentation - [USD₮0 Deployments](https://docs.usdt0.to/technical-documentation/deployments) - Contract addresses on all chains - [EIP-3009 Specification](https://eips.ethereum.org/EIPS/eip-3009) - The authorization standard enabling gasless USD₮ transfers *** ## React Native Starter (Alpha) URL: https://docs.wdk.tether.io/examples-and-starters/react-native-starter Description: Multi-chain wallet starter built with WDK, Expo, and React Native The React Native Starter Alpha is an Expo + React Native app showing how to build a multi-chain wallet using WDK via BareKit worklets and secure secret management. This starter includes wallet creation/import flows, balances, transactions, and a modular service layer. *** **Prerequisites:** Node.js 22+, and either Xcode (iOS) or Android SDK API 29+ (Android). See the [React Native Quickstart](/start-building/react-native-quickstart#prerequisites) for details. ### Quickstart Get your React Native wallet running in minutes with these simple steps: #### Clone and Install ```bash git clone https://github.com/tetherto/wdk-starter-react-native.git && cd wdk-starter-react-native && npm install ``` #### Configure Environment ```bash cp .env.example .env ``` Get your free WDK Indexer API key [here](/tools/indexer-api/get-started) and add it to your `.env` file: ```bash EXPO_PUBLIC_WDK_INDEXER_BASE_URL=https://wdk-api.tether.io EXPO_PUBLIC_WDK_INDEXER_API_KEY=your_actual_api_key_here # Optional: For Tron network support EXPO_PUBLIC_TRON_API_KEY=your_tron_api_key EXPO_PUBLIC_TRON_API_SECRET=your_tron_api_secret ``` #### Run Your App For first-time setup, generate native project files: ```bash npx expo prebuild ``` Then run the app: ```bash npm run ios # iOS Simulator npm run android # Android Emulator ``` *** **Need detailed instructions?** Check out the complete [React Native Quickstart](/start-building/react-native-quickstart) guide for step-by-step setup, configuration, and troubleshooting. ### Features **Multi-Token & Chain Support** * **BTC**: Native SegWit transfers on Bitcoin network * **USD₮**: Gasless transactions on EVM (Ethereum, Polygon, Arbitrum), native transfers on TON and Tron * **XAU₮**: Gasless transactions on Ethereum network **Wallet Management** * **Secure Seed Generation**: Cryptographically secure entropy generation * **Seed Import**: Import existing 12-word mnemonic phrases * **Encrypted Storage**: Secure key storage via [`@tetherto/wdk-secret-manager`](https://github.com/tetherto/wdk-secret-manager) * **Multi-Account Support**: Derive multiple accounts from single seed **Asset Management** * **Real-Time Balances**: Live balance updates via [WDK Indexer](/tools/indexer-api/) * **Transaction History**: Complete transaction tracking and history via [WDK Indexer](/tools/indexer-api/) * **Price Conversion**: Real-time fiat pricing via [Pricing Provider](/tools/price-rates/) **User Experience** * **QR Code Scanner**: Scan addresses and payment requests via camera * **Send/Receive Flows**: Intuitive transfer interfaces * **Network Selection**: Choose optimal network for each transaction * **Token Selection**: Multi-token transfer support * **Activity Feed**: Real-time transaction monitoring *** ### Project Structure The starter includes a modular architecture designed for scalability and maintainability: ```text title="Project Structure" src/ ├── app/ # Expo Router screens (file-based routing) │ ├── onboarding/ # First-time user flows │ ├── wallet-setup/ # Create/import wallet screens │ ├── send/ & receive/ # Transaction flows │ ├── settings.tsx # Configuration & preferences │ └── token-details.tsx # Individual asset views ├── components/ # Reusable UI components ├── config/ # Network, asset, and chain settings ├── services/ # Business logic (pricing integration) ├── hooks/ # Custom React hooks └── utils/ # Formatting & helper functions ``` Detailed project structure can be found in the [Github Repository](https://github.com/tetherto/wdk-starter-react-native/tree/main?tab=readme-ov-file#-project-structure). *** ### Available Scripts | Script | Description | | ------------------------ | --------------------------------------------- | | `npm start` | Start Expo development server with dev client | | `npm run android` | Run on Android emulator/device | | `npm run ios` | Run on iOS simulator | | `npm run web` | Start web development server | | `npm run prebuild` | Generate native project files | | `npm run prebuild:clean` | Clean and regenerate native project files | | `npm run lint` | Run ESLint | | `npm run lint:fix` | Fix ESLint errors | | `npm run format` | Format code with Prettier | | `npm run format:check` | Check code formatting | | `npm run typecheck` | Run TypeScript type checking | *** ### Technology Stack #### Core Technologies * **Expo**: \~54.0.8 with development client * **React Native**: 0.81.4 * **React**: 19.1.0 * **TypeScript**: \~5.9.2 * **Reanimated**: \~4.1.0 * **New Architecture**: Enabled #### Build Configuration * **Android**: minSdkVersion 29 * **iOS**: Latest Xcode toolchain * **Build Properties**: Configured via `expo-build-properties` *** ### Next Steps **Customizing the UI** This starter uses components from the [WDK React Native UI Kit](/ui-kits/react-native-ui-kit/). To customize the look and feel: * [**Theming Guide**](/ui-kits/react-native-ui-kit/theming) - Deep dive into theming capabilities * [**Component Reference**](/ui-kits/react-native-ui-kit/api-reference) - Complete component documentation **Add new functionality** This starter provides a solid foundation that you can extend with additional functionality: * **Add support for other tokens** using wallet modules in the [WDK SDK](/sdk/get-started) * **Add DeFi protocols** like swaps, bridges, and lending using [protocol modules](/sdk/get-started) **Or explore documentation** * [**WDK SDK Documentation**](/sdk/get-started) - Learn about the underlying SDK * [**UI Kit Documentation**](/ui-kits/react-native-ui-kit/get-started) - Customize the interface * [**WDK Indexer**](/tools/indexer-api/) - Understand data fetching * [**Secret Manager**](/tools/secret-manager/) - Learn about secure key management *** ## Need Help? *** ## About WDK URL: https://docs.wdk.tether.io/overview/about Description: Learn about the Wallet Development Kit and its capabilities The **Wallet Development Kit _by Tether_ (WDK)** is Tether's open-source toolkit that empowers humans, machines and AI agents alike to build, deploy and use secure, multi-chain, self-custodial wallets that can be integrated anywhere from the smallest embedded device to any mobile, desktop and server operating system. A developer-first framework designed for maximum flexibility and scalability, powering anything from consumer wallets to wallet-enabled apps, DeFi integrations (lending, swaps, ...), IoT use cases, and AI agents. Unlike closed solutions or SaaS-based wallet infrastructure providers, WDK offers zero lock-in and is designed for maximum flexibility and extensibility. It is modular, runs on Bare, Node.js and React-Native, thus can be embedded in a wide variety of environments. *** ## What Problems Does WDK Solve? The current blockchain ecosystem is highly fragmented, with each blockchain requiring different SDKs, APIs, and integration approaches. This fragmentation creates significant barriers for developers who want to build truly seamless user-experiences that span across any blockchain, environment and use-case. Traditional wallet development requires months of integration work. Developers must learn different standards, implement contrasting security practices, or rely on closed-source paid solutions which act as gatekeepers. ### **The Missing AI Foundation** As we move toward a world where humans, machines and AI Agents need to manage digital assets safely, existing solutions fall short. AI agents will require wallets to interact in the financial infrastructure, and WDK wants to lay secure foundation that works for human, AI and IoT use cases. WDK enables trillions of self-custodial wallets. *** ## Why WDK is Different Works with Node.js, Bare runtime, mobile (React Native), and future embedded environments Pick only the modules you need; extend functionality with custom modules Clear SDK design, strong TypeScript typing, extensive docs, and ready-to-use starters Stateless and self-custodial architecture ensures keys never leave user control Transparent, community-driven, and free to adopt with no vendor lock-in Maintained and supported by Tether with strong community involvement *** ## What WDK Provides WDK combines four core components to deliver a complete wallet development solution: Unified APIs for wallet and protocol operations across multiple blockchains Reliable blockchain data access for balances, transactions, and historical data Reusable React Native components for building wallet interfaces Production-ready wallet templates and reference implementations *** ## Supported Blockchains & Protocols WDK natively supports a broad set of blockchains and standards out of the box: | Blockchain/Module | Support | | --------------------------------------------------------------- | ------- | | [Bitcoin](/sdk/wallet-modules/wallet-btc/) | ✅ | | [Ethereum & EVM](/sdk/wallet-modules/wallet-evm/) | ✅ | | [Ethereum ERC-4337](/sdk/wallet-modules/wallet-evm-erc-4337/) | ✅ | | [TON](/sdk/wallet-modules/wallet-ton/) | ✅ | | [TON Gasless](/sdk/wallet-modules/wallet-ton-gasless/) | ✅ | | [TRON](/sdk/wallet-modules/wallet-tron/) | ✅ | | [TRON Gasfree](/sdk/wallet-modules/wallet-tron-gasfree/) | ✅ | | [Solana](/sdk/wallet-modules/wallet-solana/) | ✅ | | [Spark/Lightning](/sdk/wallet-modules/wallet-spark/) | ✅ | | Protocol/Module | Support | | -------------------------------------------------------------- | ------- | | [velora (EVM)](/sdk/swap-modules/swap-velora-evm/) | ✅ | | StonFi (TON) | In progress | | [USD₮0 Bridge (EVM)](/sdk/bridge-modules/bridge-usdt0-evm/) | ✅ | | [Aave Lending (EVM)](/sdk/lending-modules/lending-aave-evm/) | ✅ | The modular architecture allows new chains, tokens, or protocols to be added by implementing dedicated modules. Ready to start building? Explore our [getting started guide](/start-building/nodejs-bare-quickstart) or dive into our [SDK documentation](/sdk/get-started). *** ## Changelog URL: https://docs.wdk.tether.io/overview/changelog Description: Updates and improvements to the Wallet Development Kit (WDK) modules and tools. Stay up to date with the latest improvements, new features, and bug fixes across all WDK modules. --- ### June 02, 2026 **What's New** - **pricing-provider** ([v1.0.0-beta.4](https://github.com/tetherto/wdk-pricing-provider/releases/tag/v1.0.0-beta.4)): Add failover provider support — `client` now accepts an array of `PricingClient` instances with automatic connection-error failover, and a new optional `retries` field (default: 3) controls the number of retry attempts. - **wallet-ton-gasless** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-wallet-ton-gasless/releases/tag/v1.0.0-beta.6)): Added client failover: `tonClient` and `tonApiClient` now accept arrays of configs or instances with a new `retries` option (default 3). Migrated to `@ton/ton` v16. --- ### June 01, 2026 **What's New** - **wallet-evm-erc-4337** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.8)): Allow per-call UserOperation gas overrides — `sendTransaction`, `quoteSendTransaction`, and `signTransaction` now accept `callGasLimit`, `verificationGasLimit`, `preVerificationGas`, `maxFeePerGas`, and `maxPriorityFeePerGas` on the transaction object. **Fixes** - **wallet-spark** ([v1.0.0-beta.19](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.19)): Fix `keyPair` getter to return `null` for `privateKey` after `dispose()` is called; document that the returned byte arrays are a read-only view of internal keys. --- ### May 29, 2026 **Fixes** - **wallet-ton** ([v1.0.0-beta.9](https://github.com/tetherto/wdk-wallet-ton/releases/tag/v1.0.0-beta.9)): Fix `keyPair.privateKey` to return `null` (not `undefined`) after `dispose()` and document that key-pair arrays are a read-only view; migrate to `@ton/ton` v16. - **wallet-evm** ([v1.0.0-beta.13](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.13)): Fix `keyPair` getter so `privateKey` reliably returns `null` after `dispose()`, and document that the returned byte arrays must be treated as read-only. - **wallet-tron-gasfree** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-wallet-tron-gasfree/releases/tag/v1.0.0-beta.6)): Remove cached GasFree account state so nonce-sensitive transfers fetch fresh account data, and clarify that returned key-pair byte arrays are a read-only view of internal keys. --- ### May 27, 2026 **What's New** - **wallet-evm-erc-4337** ([v1.0.0-beta.7](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.7)): Migrate the smart-account engine to Candide AbstractionKit, add ordered provider failover (pass an array of providers plus an optional `retries` count), and add an optional `onChainIdentifier` for tagging UserOperations. The `entryPointAddress` config option is no longer required. --- ### May 25, 2026 **What's New** - **wallet-solana** ([v1.0.0-beta.9](https://github.com/tetherto/wdk-wallet-solana/releases/tag/v1.0.0-beta.9)): The `WalletAccountSolana` constructor is now public (the `WalletAccountSolana.at()` factory is deprecated — construct accounts directly), and `dispose()` now securely zeroes the private key so `keyPair.privateKey` returns `null` afterwards. - **wdk-wallet** ([v1.0.0-beta.9](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.9)): Add the `SwidgeProtocol` abstract base class and `ISwidgeProtocol` interface, a unified swap/bridge/route surface (`quoteSwidge()`, `swidge()`, `getSwidgeStatus()`, `getSupportedChains()`, `getSupportedTokens()`) exported from the `@tetherto/wdk-wallet/protocols` entrypoint for provider modules to implement. - **create-wdk-module** ([v1.0.0-beta.2](https://github.com/tetherto/create-wdk-module/releases/tag/v1.0.0-beta.2)): Add `swidge` as a new scaffoldable module type for cross-chain swap (swap + bridge combined) protocols, selectable via the interactive prompt or `[type]` CLI argument. --- ### May 19, 2026 **What's New** - **[Swidge Protocol Interface](../sdk/swidge-modules)**: New page covering WDK's preferred route interface for new swap, bridge, and combined route providers, including discovery, quote, execution, status, fee, and migration guidance for the existing standalone swap and bridge interfaces. --- ### May 18, 2026 **Changes** - **wdk-utils** ([v1.0.0-beta.4](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.4)): [Breaking] Remove `decodeLightningInvoice()` and its associated BOLT11 types. Callers that depended on this function must remove it; `validateLightningInvoice()` and all other Lightning validators remain available. --- ### May 15, 2026 **Changes** - **bridge-usdt0-evm** ([v1.0.0-beta.4](https://github.com/tetherto/wdk-protocol-bridge-usdt0-evm/releases/tag/v1.0.0-beta.4)): Clarify that `bridge()` and `quoteBridge()` require an ERC-20 approval of the source-chain bridge spender before they are called. - **wallet-ton-gasless** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-ton-gasless/releases/tag/v1.0.0-beta.5)): [Breaking] Aligned the default derivation path with `@tetherto/wdk-wallet-ton`: `getAccount(index)` now derives `m/44'/607'/{index}'` instead of `m/44'/607'/0'/0/{index}`, so the same seed produces different addresses after upgrading. Added `signTransaction(tx)` (unsupported on gasless; throws) and cached the read-only account instance returned by `toReadOnlyAccount()`. --- ### May 12, 2026 **What's New** - **wallet-ton** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-ton/releases/tag/v1.0.0-beta.8)): Add RPC endpoint failover (`tonClient` now accepts an array of configs/clients with a `retries` option) and offline transaction signing with `signTransaction()`. **Fixes** - **react-native-secure-storage** ([v1.0.0-beta.4](https://github.com/tetherto/wdk-react-native-secure-storage/releases/tag/v1.0.0-beta.4)): Fix secure storage on PIN-only Android devices by using `DEVICE_PASSCODE` access control when no biometrics are enrolled, preventing silent keychain failures. --- ### May 10, 2026 **What's New** - **wallet-tron-gasfree** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-tron-gasfree/releases/tag/v1.0.0-beta.5)): Make `gasFreeApiKey` and `gasFreeApiSecret` optional when the GasFree provider does not require signed API requests, expose `TronGasfreeAssetInfo` and `TronGasfreeAccountInfo`, include activation fees in transfer quotes, and add unsupported `signTransaction(tx)` for wallet-interface compatibility. --- ### May 08, 2026 **What's New** - **wdk-utils** ([v1.0.0-beta.3](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.3)): Add `validateTronAddress()`, `decodeLightningInvoice()`, and `decodeLnurl()` so wallet UIs can validate Tron addresses and inspect BOLT11 invoices or LNURL strings before starting payment flows. **Changes** - **react-native-core** ([v1.0.0-beta.10](https://github.com/tetherto/wdk-react-native-core/releases/tag/v1.0.0-beta.10)): Use individual token balance reads when a wallet module does not expose batch token balance fetching, improving multi-chain balance support without changing the hook API. --- ### May 05, 2026 **Changes** - **react-native-secure-storage** ([v1.0.0-beta.3](https://github.com/tetherto/wdk-react-native-secure-storage/releases/tag/v1.0.0-beta.3)): Refresh `expo-crypto` and `expo-local-authentication` dependencies to the current Expo SDK release line and keep dependency overrides limited to development tooling, with no public secure storage API changes. --- ### May 01, 2026 **What's New** - **wallet-solana** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-solana/releases/tag/v1.0.0-beta.8)): Add `signTransaction(tx)` for offline Solana transaction signing and `getTokenBalances(tokenAddresses)` for batch SPL balance reads; prefer `provider` over the deprecated `rpcUrl` config alias, optimize `getTokenBalance()` to use one RPC call, reuse the cached read-only account helper, and bump `@tetherto/wdk-failover-provider` to `1.0.0-beta.2`. --- ### April 30, 2026 **What's New** - **[React Native Secure Storage](/tools/react-native-secure-storage/)**: Docs added for `@tetherto/wdk-react-native-secure-storage`, covering keychain-backed wallet credential storage, biometric options, and typed errors. - **wallet-spark** ([v1.0.0-beta.18](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.18)): Add `signTransaction(tx)` to `WalletAccountSpark` for `IWalletAccount` compatibility, document that standalone signed payloads are unsupported on Spark, reuse the cached read-only account helper, and refresh `@buildonspark/spark-sdk` to `0.7.16` and spark bare SDK to `0.0.66`. --- ### April 29, 2026 **What's New** - **wallet-btc** ([v1.0.0-beta.9](https://www.npmjs.com/package/@tetherto/wdk-wallet-btc/v/1.0.0-beta.9)): Add offline Bitcoin transaction signing with `signTransaction()` and ordered client failover with `retries`; reuse the read-only account helper and clarify that returned key-pair byte arrays should be treated as read-only. - **wallet-evm** ([v1.0.0-beta.12](https://www.npmjs.com/package/@tetherto/wdk-wallet-evm/v/1.0.0-beta.12)): Add ordered provider failover with automatic fallback on connection errors, offline EVM transaction signing with `signTransaction()`, optional `chainId` config for provider setup, optional `chainId` on `EvmTransaction`, and read-only helper reuse. **Fixes** - **wdk-core** ([v1.0.0-beta.9](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.9)): Harden internal protocol and middleware registries to use null-prototype maps, reducing prototype-pollution false positives without changing the public WDK API, and refresh the package README. --- ### April 28, 2026 **What's New** - **wdk-wallet** ([v1.0.0-beta.8](https://www.npmjs.com/package/@tetherto/wdk-wallet/v/1.0.0-beta.8)): Add `signTransaction(tx)` to the base `IWalletAccount` interface so wallet modules can expose offline transaction signing without broadcasting. **Fixes** - **react-native-core** ([v1.0.0-beta.9](https://www.npmjs.com/package/@tetherto/wdk-react-native-core/v/1.0.0-beta.9)): Clean up balance fetch timeouts and prevent timed-out balance requests from updating state after they resolve. --- ### April 22, 2026 **Fixes** - **wdk-core** ([v1.0.0-beta.8](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.8)): Fix `WDK.getRandomSeedPhrase(wordCount?)` so client code can generate 24-word BIP-39 seed phrases instead of always receiving the default 12-word mnemonic. --- ### April 19, 2026 **Changes** - **lending-aave-evm** ([v1.0.0-beta.4](https://www.npmjs.com/package/@tetherto/wdk-protocol-lending-aave-evm/v/1.0.0-beta.4)): Expand per-operation ERC‑4337 config overrides from `paymasterToken`-only to the wallet module's paymaster-token, sponsorship-policy, and native-coin gas modes. **Fixes** - **failover-provider** ([v1.0.0-beta.2](https://www.npmjs.com/package/@tetherto/wdk-failover-provider/v/1.0.0-beta.2)): Remove unnecessary published type definitions without changing the runtime failover behavior. - **wallet-solana** ([v1.0.0-beta.7](https://www.npmjs.com/package/@tetherto/wdk-wallet-solana/v/1.0.0-beta.7)): Fix `SolanaWalletConfig.rpcUrl` typings to accept ordered `string[]` failover endpoints and align the published TypeScript definitions with the beta.6 runtime behavior. --- ### April 15, 2026 **Changes** - **wallet-solana** ([v1.0.0-beta.6](https://www.npmjs.com/package/@tetherto/wdk-wallet-solana/v/1.0.0-beta.6)): Add runtime RPC failover support for ordered `rpcUrl` lists plus `retries`, and tighten custom `TransactionMessage` and derivation-path validation for durable nonce lifetimes, fee payer matching, and hardened SLIP-0010 child paths. --- ### April 14, 2026 **What's New** - **failover-provider** ([v1.0.0-beta.1](https://github.com/tetherto/wdk-failover-provider/releases/tag/v1.0.0-beta.1)): Initial release of a generic `FailoverProvider` that chains provider candidates and retries sync or async failures with configurable `retries` and `shouldRetryOn(error)` logic. **Changes** - **fiat-moonpay** ([v1.0.0-beta.2](https://github.com/tetherto/wdk-protocol-fiat-moonpay/releases/tag/v1.0.0-beta.2)): [Breaking] Replace `secretKey` signing with optional backend `signUrl`, add `environment` selection for production or sandbox widget URLs, and return unsigned widget URLs when no signer is configured. --- ### April 13, 2026 **What's New** - **wdk-utils** ([v1.0.0-beta.2](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.2)): Add EIP-681 request parsing utilities for transfer deeplinks, including request detection and structured parse results. - **wdk-core** ([v1.0.0-beta.7](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.7)): Added `dispose(blockchains?)`, so you can dispose one or more registered wallets without tearing down every wallet in the WDK instance. - **pear-wrk-wdk** (v1.0.0-beta.8): Adds `resetWdkWallets({ config })` so custom Bare hosts can selectively dispose and re-register wallet modules from a new `networks` config. **Changes** - **wallet-spark** ([v1.0.0-beta.13](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.13)): Refresh `@buildonspark/bare` and `@buildonspark/spark-sdk` dependencies. - **wallet-spark** ([v1.0.0-beta.14](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.14)): Add SparkScan-backed balance polling for `getBalance()`. - **wallet-spark** ([v1.0.0-beta.15](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.15)): Refresh `@buildonspark/bare`, `@buildonspark/spark-sdk`, and `bare-node-runtime` dependencies. - **wallet-spark** ([v1.0.0-beta.16](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.16)): Add `syncAndRetry` and `syncWalletBalance()` for retrying failed `sendTransaction()` and `payLightningInvoice()` calls once after syncing wallet state. **Fixes** - **worklet-bundler** ([v1.0.0-beta.3](https://github.com/tetherto/wdk-worklet-bundler/releases/tag/v1.0.0-beta.3)): Generated worklet entrypoints now suspend and resume both HTTP and HTTPS global agents with Bare thread lifecycle events. - **wallet-btc** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-btc/releases/tag/v1.0.0-beta.8)): `getBalance()` now includes unconfirmed funds when present, and `sendTransaction()` accepts an optional `timeoutMs` to keep polling after broadcast until spent inputs disappear from unspent outputs. - **wallet-evm** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.11)): Pin string-backed RPC providers to a static network during EVM account setup. - **wallet-evm-erc-4337** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.6)): Reuse the internal EVM read-only helper during ERC-4337 method calls instead of recreating it on each call. --- ### April 3, 2026 **Changes** - **wallet-spark** ([v1.0.0-beta.12](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.12)): [`WalletAccountReadOnlySpark`](/sdk/wallet-modules/wallet-spark/api-reference#walletaccountreadonlyspark) gained [`getTransfers()`](/sdk/wallet-modules/wallet-spark/api-reference#gettransfersoptions), [`getUnusedDepositAddresses()`](/sdk/wallet-modules/wallet-spark/api-reference#getunuseddepositaddressesoptions) (paginated return type), [`getStaticDepositAddresses()`](/sdk/wallet-modules/wallet-spark/api-reference#getstaticdepositaddresses), [`getUtxosForDepositAddress()`](/sdk/wallet-modules/wallet-spark/api-reference#getutxosfordepositaddressoptions), and [`getSparkInvoices()`](/sdk/wallet-modules/wallet-spark/api-reference#getsparkinvoicesparams) (new parameter type). Removed `sparkScanApiKey` config option and `SparkTransactionReceipt` type after dropping the `@sparkscan/api-node-sdk-client` dependency. [`getTransactionReceipt()`](/sdk/wallet-modules/wallet-spark/api-reference#gettransactionreceipthash) now returns `SparkTransfer` instead. Added [`getAccountByPath()`](/sdk/wallet-modules/wallet-spark/api-reference#getaccountbypathpath) to [`WalletManagerSpark`](/sdk/wallet-modules/wallet-spark/api-reference#walletmanagerspark). SIGNET network support documented. Dependency upgrades: `@buildonspark/spark-sdk` 0.7.3, `@buildonspark/bare` 0.0.53. --- ### April 2, 2026 **Changes** - **react-native-core** ([v1.0.0-beta.7](https://www.npmjs.com/package/@tetherto/wdk-react-native-core/v/1.0.0-beta.7)): Added missing type exports: `WdkAppState`, `TransactionParams`, `TransactionResult`, `UseAccountResponse`, `AddressInfo`, `AddressInfoResult`, `BalanceQueryOptions`, `UseWdkAppResult`. Removed `indexer` as a top-level config prop. --- ### March 24, 2026 **What's New** - **[React Native Core](/tools/react-native-core/)**: Added documentation for `@tetherto/wdk-react-native-core` ([v1.0.0-beta.6](https://github.com/tetherto/wdk-core-react-native/releases/tag/v1.0.0-beta.6)), the hooks-based React Native integration layer for WDK. Includes [API Reference](/tools/react-native-core/api-reference) covering `WdkAppProvider`, `useWdkApp`, `useWalletManager`, `useAccount`, `useBalance`, and more. Updated [React Native Quickstart](/start-building/react-native-quickstart) with step-by-step integration guide. --- ### March 12, 2026 **Changes** - **wallet-btc** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-wallet-btc/releases/tag/v1.0.0-beta.6)): Added `dispose()` method to [`WalletAccountReadOnlyBtc`](/sdk/wallet-modules/wallet-btc/api-reference#walletaccountreadonlybtc) for closing internal Electrum connections. Security dependency updates. --- ### March 6, 2026 **Changes** - **wallet-tron**: Fixed case-sensitive address check in `verify`, upgraded TonWeb to v6.2.0 ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-tron/releases/tag/v1.0.0-beta.5)) - **lending-aave-evm**: Security dependency updates ([v1.0.0-beta.4](https://github.com/tetherto/wdk-protocol-lending-aave-evm/releases/tag/v1.0.0-beta.4)) - **wdk**: Security dependency updates ([v1.0.0-beta.6](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.6)) --- ### March 5, 2026 **What's New** - **create-wdk-module**: Added documentation for the [`create-wdk-module`](/tools/create-wdk-module) CLI scaffolding tool. Updated [Community Modules](/sdk/community-modules/) and [SDK Get Started](/sdk/get-started) pages with references to the new tool. --- ### February 26, 2026 **Changes** - **wdk-protocol-bridge-usdt0-evm** ([v1.0.0-beta.3](https://github.com/tetherto/wdk-protocol-bridge-usdt0-evm/releases/tag/v1.0.0-beta.3)): Added per-call `BridgeOptions` overrides (`oftContractAddress`, `dstEid`) and expanded routing from EVM source chains to EVM plus non-EVM destinations (Solana, TON, TRON). --- ### February 25, 2026 **Changes** - **wallet-evm** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.8)): Added [`getTokenBalances(tokenAddresses)`](/sdk/wallet-modules/wallet-evm/api-reference#gettokenbalancestokenaddresses) to [`WalletAccountReadOnlyEvm`](/sdk/wallet-modules/wallet-evm/api-reference#walletaccountreadonlyevm), also available on [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference#walletaccountevm) through inheritance. - **wallet-evm-erc-4337** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.5)): Added EIP-712 typed data methods [`signTypedData(typedData)`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#signtypeddatatypeddata) and [`verifyTypedData(typedData, signature)`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#verifytypeddatatypeddata-signature), plus multicall token balance method [`getTokenBalances(tokenAddresses)`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#gettokenbalancestokenaddresses). --- ### February 24, 2026 **Changes** - **wallet-spark** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.11)): Added Pear runtime entrypoint support (`pear.js`), removed static import causing runtime issues, and bumped spark bare SDK (`@buildonspark/bare`) to `0.0.47`. --- ### February 20, 2026 **What's New** - **[Showcase](/overview/showcase)**: More visibility for our showcase page, we value contributions! Added 4 featured community projects: [wdk-mcp](https://github.com/dieselftw/wdk-mcp), [wdk-starter-browser-extension](https://github.com/base58-io/wdk-starter-browser-extension), [wdk-wallet-evm-x402-facilitator](https://github.com/SemanticPay/wdk-wallet-evm-x402-facilitator), and [x402-usdt0](https://github.com/baghdadgherras/x402-usdt0). - **[Community Modules](/sdk/community-modules)**: Added [`@base58-io/wdk-wallet-cosmos`](https://github.com/base58-io/wdk-wallet-cosmos) — wallet module for Cosmos-compatible blockchains by [Base58](https://base58.io/). --- ### February 18, 2026 **What's New** - **[x402 Payments](/ai/x402)**: New guide for accepting and making instant USD₮ payments over HTTP using WDK self-custodial wallets. Covers the x402 protocol, buyer integration with `@tetherto/wdk-wallet-evm`, seller setup with hosted and self-hosted facilitators, and bridging USD₮ to Plasma and Stable chains. --- ### February 15, 2026 **Changes** - **wallet-spark**: Added [`getIdentityKey()`](/sdk/wallet-modules/wallet-spark/api-reference#getidentitykey) method to [`WalletAccountReadOnlySpark`](/sdk/wallet-modules/wallet-spark/api-reference#walletaccountreadonlyspark) for retrieving the account's identity public key ([v1.0.0-beta.10](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.10)) --- ### February 14, 2026 **Changes** - **wallet-spark**: Upgrade spark-sdk from `0.6.1` to `0.6.4` and spark bare SDK to `0.0.43` ([v1.0.0-beta.9](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.9)) --- ### February 12, 2026 **What's New** - **[Agent Skills](/ai/agent-skills)**: New page covering WDK's agent skill capabilities, self-custodial vs hosted comparison, and platform compatibility with OpenClaw, Claude, Cursor, and other agent platforms. - **[OpenClaw Integration](/ai/openclaw)**: New page for installing and configuring the WDK skill in OpenClaw via ClawHub, including security precautions for running agents locally. **Changes** - **wallet-evm** ([v1.0.0-beta.7](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.7)): Added [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data support: - Added [`signTypedData(typedData)`](/sdk/wallet-modules/wallet-evm/api-reference#signtypeddatatypeddata) method to [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference#walletaccountevm) for signing structured data - Added [`verifyTypedData(typedData, signature)`](/sdk/wallet-modules/wallet-evm/api-reference#verifytypeddatatypeddata-signature) method to [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference#walletaccountevm) and [`WalletAccountReadOnlyEvm`](/sdk/wallet-modules/wallet-evm/api-reference#walletaccountreadonlyevm) for verifying typed data signatures - **wallet-evm-erc-4337** ([v1.0.0-beta.4](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.4)): - Added 2 new gas payment modes: [Sponsorship Policy](/sdk/wallet-modules/wallet-evm-erc-4337/configuration#gas-payment-mode-flags) and [Native Coins](/sdk/wallet-modules/wallet-evm-erc-4337/configuration#gas-payment-mode-flags), alongside the existing Paymaster Token mode - Added per-call [config override](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#config-override) parameter to `sendTransaction`, `transfer`, `quoteSendTransaction`, and `quoteTransfer` - Added [`getUserOperationReceipt(hash)`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#getuseroperationreceipthash) method for retrieving ERC-4337 UserOperation receipts - Added [`ConfigurationError`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#configurationerror) error type for invalid configuration validation --- ### February 10, 2026 **What's New** - **[MCP Toolkit](/ai/mcp-toolkit)**: New documentation for `@tetherto/wdk-mcp-toolkit` (`v1.0.0-beta.1`). Covers the `WdkMcpServer` class, 35 built-in MCP tools across 7 categories (wallet, pricing, indexer, swap, bridge, lending, fiat), setup wizard, multi-tool configuration, and full API reference. --- ### February 08, 2026 **Changes** - **wallet-spark**: Fixed import causing wallet init failure. Upgrade spark-sdk from `0.5.7` to `0.6.1` ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.8)) --- ### February 02, 2026 **Changes** - **wallet-ton-gasless**: Added `verify` method to [`WalletAccountReadOnlyTonGasless`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#walletaccountreadonlytongasless) ([v1.0.0-beta.4](https://github.com/tetherto/wdk-wallet-ton-gasless/releases/tag/v1.0.0-beta.4)) - **wallet-tron-gasfree**: Added `verify` method to [`WalletAccountReadOnlyTronGasfree`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#walletaccountreadonlytrongasfree) ([v1.0.0-beta.4](https://github.com/tetherto/wdk-wallet-tron-gasfree/releases/tag/v1.0.0-beta.4)) --- ### January 29, 2026 **What's New** - **wdk-indexer** - Updated Ethereum indexer supported tokens list to add USA₮. **Changes** - **wdk-indexer docs** - Fixed the USD₮, XAU₮ token names. --- ### January 26, 2026 **Changes** - **wallet-btc** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-btc/releases/tag/v1.0.0-beta.5)): - Added `verify` method to [`WalletAccountReadOnlyBtc`](/sdk/wallet-modules/wallet-btc/api-reference#walletaccountreadonlybtc) - Added Pluggable Transport classes: [`ElectrumTcp`](/sdk/wallet-modules/wallet-btc/api-reference#electrumtcp), [`ElectrumTls`](/sdk/wallet-modules/wallet-btc/api-reference#electrumtls), [`ElectrumSsl`](/sdk/wallet-modules/wallet-btc/api-reference#electrumssl), [`ElectrumWs`](/sdk/wallet-modules/wallet-btc/api-reference#electrumws) - **wallet-evm**: Added `verify` method to [`WalletAccountReadOnlyEvm`](/sdk/wallet-modules/wallet-evm/api-reference#walletaccountreadonlyevm) ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.5)) - **wallet-solana**: Added `verify` method to [`WalletAccountReadOnlySolana`](/sdk/wallet-modules/wallet-solana/api-reference#walletaccountreadonlysolana) ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-solana/releases/tag/v1.0.0-beta.5)) - **wallet-ton**: Added `verify` method to [`WalletAccountReadOnlyTon`](/sdk/wallet-modules/wallet-ton/api-reference#walletaccountreadonlyton) ([v1.0.0-beta.7](https://github.com/tetherto/wdk-wallet-ton/releases/tag/v1.0.0-beta.7)) - **wallet-tron**: Added `verify` method to [`WalletAccountReadOnlyTron`](/sdk/wallet-modules/wallet-tron/api-reference#walletaccountreadonlytron) ([v1.0.0-beta.4](https://github.com/tetherto/wdk-wallet-tron/releases/tag/v1.0.0-beta.4)) - **wallet-spark**: Added `verify` method to [`WalletAccountReadOnlySpark`](/sdk/wallet-modules/wallet-spark/api-reference#walletaccountreadonlyspark) ([v1.0.0-beta.7](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.7)) --- ### January 23, 2026 **What's New** - **wdk-core docs**: Added comprehensive [Core Module Guides](/sdk/core-module/guides/getting-started) covering: - [Getting Started](/sdk/core-module/guides/getting-started) - Installation and instantiation - [Wallet Registration](/sdk/core-module/guides/wallet-registration) - Registering wallet modules for different blockchains - [Account Management](/sdk/core-module/guides/account-management) - Working with accounts and addresses - [Transactions](/sdk/core-module/guides/transactions) - Sending native tokens - [Protocol Integration](/sdk/core-module/guides/protocol-integration) - Using swaps, bridges, and lending protocols - [Middleware](/sdk/core-module/guides/middleware) - Configuring logging and failover protection - [Error Handling](/sdk/core-module/guides/error-handling) - Best practices and memory management - **wdk-core**: Added support for 24-word seed phrases via `WDK.getRandomSeedPhrase(24)` - **indexer-api**: - Added new `/api/v1/chains` endpoint to list supported blockchains and tokens - Added XAU₮ support for Plasma network **Changes** - **wallet-btc docs**: - Updated documentation with BIP-84 (Native SegWit) and BIP-44 (Legacy) support - Improved API reference and configuration documentation - **wallet-spark docs**: - Removed testnet support (now only mainnet and regtest) - Added [Lightspark Regtest Faucet](https://app.lightspark.com/regtest-faucet) link for test funds - **wallet-tron-gasfree docs**: - Updated testnet from Shasta to Nile - Updated GasFree service URLs and configuration examples - **wallet-evm-erc-4337 docs**: Added paymaster token configuration documentation - **docs**: - Updated token symbols to USD₮ and XAU₮ throughout documentation - Various documentation improvements with better cross-linking and examples **Fixes** - **wallet-tron-gasfree docs**: Fixed typo "Gras-Free" to "Gas-Free" - Fixed GitBook callout syntax and formatting issues across documentation --- ### December 23, 2025 **What's New** - Added [MoonPay Fiat Module](/sdk/fiat-modules/fiat-moonpay/) for on-ramp and off-ramp functionality - Added [Community Modules](/sdk/community-modules/) section to highlight community-built modules **Changes** - Added this changelog page in the docs! - **wallet-spark**: Updated Spark SDK to latest version ([v1.0.0-beta.6](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.6)) - Introduced [All Modules](/sdk/all-modules) page in docs for comprehensive module listings - Reorganized documentation structure for better navigation --- ### December 17, 2025 **What's New** - **wdk-core**: Added fiat protocol support for on-ramp integrations ([v1.0.0-beta.5](https://github.com/tetherto/wdk-core/releases/tag/v1.0.0-beta.5)) - **wdk-wallet**: Added fiat protocol integration ([v1.0.0-beta.6](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.6)) --- ### December 3, 2025 **What's New** - **wallet-ton**: Added integration tests ([v1.0.0-beta.6](https://github.com/tetherto/wdk-wallet-ton/releases/tag/v1.0.0-beta.6)) - **wallet-btc**: Added support for custom `feeRate` and `confirmationTarget` parameters ([v1.0.0-beta.4](https://github.com/tetherto/wdk-wallet-btc/releases/tag/v1.0.0-beta.4)) **Changes** - **wallet-ton**: Updated default derivation path, fixed transaction receipt LT and from address - **wallet-solana**: Updated default derivation path for better compatibility ([v1.0.0-beta.4](https://github.com/tetherto/wdk-wallet-solana/releases/tag/v1.0.0-beta.4)) - **wallet-btc**: Multiple improvements: - Automatic dust limit inference based on wallet type - Performance improvements with bounded concurrency and caching for `getTransfers` - Switched to `bitcoinjs-message` for standard message signing - Updated default BIP to 84 (Native SegWit) - Fixed testnet derivation path (now uses `1'`) --- ### November 14, 2025 **Changes** - **wdk-wallet**: Runtime updates and dependency synchronization ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.5)) --- ### November 12, 2025 **What's New** - **wallet-solana**: Added `sendTransaction` support with unit tests ([v1.0.0-beta.3](https://github.com/tetherto/wdk-wallet-solana/releases/tag/v1.0.0-beta.3)) **Changes** - **wallet-solana**: Fixed `punycode` module resolution issue - **lending-aave-evm**: Runtime compatibility updates ([v1.0.0-beta.3](https://github.com/tetherto/wdk-protocol-lending-aave-evm/releases/tag/v1.0.0-beta.3)) --- ### November 11, 2025 **Changes** - **swap-velora-evm**: Runtime compatibility updates ([v1.0.0-beta.4](https://github.com/tetherto/wdk-protocol-swap-velora-evm/releases/tag/v1.0.0-beta.4)) --- ### November 9-10, 2025 **What's New** - **wallet-ton-gasless**: Added unit tests ([v1.0.0-beta.3](https://github.com/tetherto/wdk-wallet-ton-gasless/releases/tag/v1.0.0-beta.3)) - **pear-wrk-wdk**: Added seed buffer support in `workletStart` (v1.0.0-beta.5) **Changes** - **wallet-tron-gasfree**: Fixed bug interacting with Gasfree API ([v1.0.0-beta.3](https://github.com/tetherto/wdk-wallet-tron-gasfree/releases/tag/v1.0.0-beta.3)) - **wallet-ton-gasless**: Updated TON query-id and transaction hash handling - **wallet-evm**: Runtime updates ([v1.0.0-beta.4](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.4)) - **wallet-tron**: Dependency and runtime updates ([v1.0.0-beta.3](https://github.com/tetherto/wdk-wallet-tron/releases/tag/v1.0.0-beta.3)) --- ### November 8, 2025 **Changes** - **wdk-core**: Updated `bare-node-runtime` for improved compatibility ([v1.0.0-beta.4](https://github.com/tetherto/wdk-core/releases/tag/v1.0.0-beta.4)) - **wallet-spark**: Updated Spark dependencies and improved `dispose` method ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.5)) --- ### November 7, 2025 **Changes** - **wallet-evm-erc-4337**: Fixed destructuring of user operation in `getTransactionReceipt()` ([v1.0.0-beta.3](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.3)) - **wallet-ton**: Replaced UUID-based message body with seqno/queryId for TON transfers, downgraded `@ton/ton` to 15.1.0 for stability ([v1.0.0-beta.5](https://github.com/tetherto/wdk-wallet-ton/releases/tag/v1.0.0-beta.5)) --- ## How to Stay Updated - Check this page for the latest updates - Join our [Discord community](https://discord.gg/arYXDhHB2w) for real-time announcements - Star and follow the [GitHub repositories](https://github.com/orgs/tetherto/repositories?q=wdk) for detailed release notes *** ## Partner with WDK URL: https://docs.wdk.tether.io/overview/partner-program Description: Build with WDK alongside Tether through our partnership tracks Build with WDK alongside Tether. Whether you're integrating WDK into your product or extending the ecosystem with new capabilities, we have a partnership track designed for you. WDK is built to be open and extensible, but we know that building great products often takes more than just great documentation. We offer a selected group of partners a direct connection with Tether's engineering and product teams so you can ship faster, with confidence. We offer 3 partnership tracks depending on how you plan to work with WDK. - [Project Partners](#project-partners) - [WDK Tech Contributors](#wdk-tech-contributors) - [Consulting & Implementation Partners (Alpha)](#consulting--implementation-partners-alpha) *** ## Project Partners Integrate WDK more confidently, with direct access to Tether's engineering team and WDK solutions architects. Project Partners approved for Tether-supported implementations will benefit from: - Access to a WDK Solutions Architect to discuss product-specific implementation strategies - Custom integration assistance - Privileged support channel - WDK roadmap visibility - Early access to APIs and SDKs - Direct access to WDK product and engineering core team ### Is it for you? Project Partners are companies and teams building end-user products powered by WDK. You're a good fit for this track if you are: - A **fintech or neobank** building a wallet, payments app, or asset management platform and looking to leverage WDK as your underlying wallet infrastructure. - An **exchange or trading platform** adding self-custodial wallet features for your users. - A **messaging or social platform** integrating peer-to-peer payments or tipping functionality. - A **remittance or cross-border payments provider** looking to use stablecoins and multi-chain support to serve your customers. - An **enterprise or institutional player** that needs WDK integrated into internal treasury, compliance, or operations tooling. - Any team that plans to **ship a product to end users** where WDK handles key management, transaction signing, or blockchain interactions under the hood. As a Project Partner, you get hands-on integration support from the team that builds WDK. We'll help you navigate architecture decisions, troubleshoot implementation challenges, and make sure your product launches on solid foundations. [Become a Project Partner](https://forms.monday.com/forms/6d484c4b34949e3a238988c47bf0a1b6?r=euc1) *** ## WDK Tech Contributors Tap into the network of WDK adopters by developing modules and extensions for the WDK ecosystem. Technology partners approved as WDK Tech Contributors will benefit from: - Build and publish WDK modules - Visibility across WDK community and in WDK documentation - Co-marketing opportunities - Early access to APIs and SDKs - Technical documentation collaboration ### Is it for you? Tech Contributors are protocol teams, service providers, and developer organizations building modules, plugins, or integrations that extend what WDK can do. You're a good fit for this track if you are: - A **swap or DEX protocol** looking to provide liquidity and trading capabilities to WDK-powered wallets. - A **bridge protocol** enabling cross-chain asset transfers that WDK wallets can access natively. - An **on/off-ramp provider** connecting fiat currencies to the WDK ecosystem. - A **lending or DeFi protocol** looking to make your services available directly within WDK wallets. - A **hardware wallet or signing solution provider** building signer integrations for WDK. - A **blockchain or L2 network** that wants first-class WDK wallet support for your chain. - An **open-source developer or team** contributing new wallet modules, protocol integrations, or developer tooling to the WDK ecosystem. As a Tech Contributor, you'll work closely with our SDK team to build, test, and publish modules that reach every WDK-powered wallet. You'll get early access to unreleased APIs, architecture guidance, co-marketing exposure through our documentation and community channels, and the opportunity to shape how your protocol integrates across the ecosystem. [Become a Technology Partner](https://forms.monday.com/forms/8672578dbc8e26fdf4766cc073270769?r=euc1) *** ## Consulting & Implementation Partners (Alpha) Consulting companies, agencies, and systems integrators building wallet solutions with WDK for their clients. Approved Consulting & Implementation Partners will benefit from: - **Being part of Tether's partner ecosystem** - Access to a WDK Solutions Architect to discuss product-specific implementation strategies for your clients - Custom integration assistance - Direct access to WDK product and engineering core team - Privileged support channel - WDK roadmap visibility - Early access to APIs and SDKs - Co-marketing support ### Is it for you? Consulting & Implementation Partners are agencies, system integrators, and software houses that would like to deliver WDK-powered solutions on behalf of their clients. You're a good fit for this track if you are: - A **system integrator** helping enterprise clients adopt blockchain and digital asset infrastructure. - A **software development agency** building custom wallet or payment applications. - A **blockchain consultancy** advising companies on self-custodial wallet strategy and architecture. - A **digital transformation firm** integrating stablecoin payments into existing client platforms. - A **managed services provider** offering ongoing support and maintenance for WDK-based deployments. As a Consulting & Implementation Partner, you'll gain access to Tether's referral network, dedicated technical support for your client engagements. We'll equip your team with the training, documentation, and direct engineering access needed to deliver successful WDK implementations at scale. **Alpha Program** - This partnership track is currently in alpha. We're onboarding a limited number of partners as we shape the program and cannot guarantee acceptance, specific benefits, or program terms at this stage. Apply to express your interest and help shape the program as it evolves. [Become a Consulting & Implementation Partner](https://forms.monday.com/forms/cb64806c634b815bc637d8fb46badfa1?r=euc1) *** ## Showcase URL: https://docs.wdk.tether.io/overview/showcase Description: Explore real-world products and community projects built with the Wallet Development Kit. Explore products and community projects built with WDK. These examples show how WDK can power self-custodial wallets, payments, AI tooling, and protocol integrations in real-world products. ## Built With WDK ## Community Projects Community showcase projects are developed and maintained independently by third-party contributors. Tether and the WDK Team do not endorse or assume responsibility for their code, security, or maintenance. Use your own judgment and proceed at your own risk. Looking for community-built WDK modules you can install and use in your project? Check out the [Community Modules](/sdk/community-modules/) page instead. --- ### wdk-starter-browser-extension > Self-custodial browser extension wallet starter built on WDK. **Author:** Base58 ([Website](https://base58.io/), [GitHub](https://github.com/base58-io)) / alexszolowicz ([GitHub](https://github.com/alexszolowicz-blockether)) **Repository:** [github.com/base58-io/wdk-starter-browser-extension](https://github.com/base58-io/wdk-starter-browser-extension) A browser extension starter kit that demonstrates how to build a self-custodial wallet using WDK. Provides a ready-made template for creating Chrome-compatible extension wallets with secure key management and transaction signing. ![wdk-starter-browser-extension demo](/assets/wdk-starter-browser-extension.gif) --- ### wdk-wallet-evm-x402-facilitator > x402 payment facilitator adapter for WDK EVM wallets. **Author:** SemanticPay ([Website](https://www.semanticpay.io/), [GitHub](https://github.com/SemanticPay)) **Repository:** [github.com/SemanticPay/wdk-wallet-evm-x402-facilitator](https://github.com/SemanticPay/wdk-wallet-evm-x402-facilitator) An adapter that enables WDK EVM wallets to act as x402 payment facilitators. Bridges the WDK wallet interface with the x402 HTTP payment protocol, allowing servers to charge for API access using on-chain payments. --- ### x402-usdt0 > End-to-end x402 reference implementation on Plasma with USDT0 and WDK. **Author:** baghdadgherras ([GitHub](https://github.com/baghdadgherras)) **Repository:** [github.com/baghdadgherras/x402-usdt0](https://github.com/baghdadgherras/x402-usdt0) A complete reference implementation demonstrating the x402 HTTP payment protocol using USDT0 on the Plasma network. Includes both client and server components, showcasing how WDK wallets can facilitate machine-to-machine payments in a real-world setup. --- ### wdk-mcp > AI-powered blockchain operations via Model Context Protocol. **Author:** Seven ([GitHub](https://github.com/rezerov)) **Repository:** [github.com/rezerov/wdk-mcp](https://github.com/rezerov/wdk-mcp) Integrates WDK capabilities within the MCP (Model Context Protocol) ecosystem, allowing AI Agents to perform blockchain operations such as signing, transactions, and wallet interactions securely and locally. This project expands the reach of WDK to autonomous systems and AI-driven workflows. --- ## Submit Your Project If you've built something using WDK, we'd love to showcase it. Projects listed here should: - Use one or more WDK modules or SDKs - Be open source or publicly accessible - Include a clear README and installation instructions Your work may be featured in future updates, social posts, or documentation spotlights. Share it with us through the community form or the showcase channel below. *** ## Get Support URL: https://docs.wdk.tether.io/overview/support Description: Need help with WDK? We've got you covered We're here to help you succeed with WDK. Don't hesitate to reach out. *** ## Our Vision URL: https://docs.wdk.tether.io/overview/vision Description: >- Imagine a world where humans, machines, and AI agents have the freedom to control their own finances. WDK is a fully open-source, self-custodial toolkit designed to be modular, independent, resilient and infinitely scalable, enabling trillions of wallets. *** ### **Universal Unstoppable Access** Anyone should be able to build, deploy or use a wallet and manage assets without friction or gatekeepers. Whether you're an independent developer, a startup, a corporation, an AI, or even a nation-state, WDK provides the open technology to create hyper-secure self-custodial wallets without barriers. ### **Ubiquitous Deployment** Wallets need to run everywhere. Through Bare runtime compatibility, WDK can live and evolve on any embedded device, mobile apps, desktop applications, IoT devices, servers, and even autonomous systems. From smartphones to smart fridges, from trading bots to spaceships — WDK enables financial sovereignty across all environments. ### **AI-Native Architecture** In a world where AI agents and robots are becoming autonomous and will permeate every single part of our lives, the machines need to have access and self-manage their own resources. WDK is the preferred choice for the digital entities of tomorrow, ensuring direct custody of funds, highly scalable transactions, and empowering the infinite AI economy of the future. *** ## A world of opportunities WDK enables a future with millions of wallets built on top of it, each tailored to specific needs and use cases WDK enables trillions of AI agents to have their own wallet, managing resources autonomously in the digital economy Any developer, company, organization, or country can build their own white-label wallet and manage their assets independently From IoT devices to autonomous vehicles, every connected device can have its own wallet and financial identity *** ## Let's build this future together WDK is more than a development kit—it's the foundation for a new era of financial sovereignty. By making wallet technology accessible, ubiquitous, and AI-native, we're enabling a world where: * **Developers** can focus on innovation rather than infrastructure * **Users** maintain complete control over their digital assets * **AI Agents** can operate autonomously in the digital economy * **Organizations** can build custom financial solutions without compromise * **Society** benefits from more secure, efficient, and accessible financial infrastructure Join us in building this future. The tools are open-source, the vision is clear, and the possibilities are limitless. *** Ready to start building? Explore our [getting started guide](/start-building/nodejs-bare-quickstart) or dive into our [SDK documentation](/sdk/get-started). *** ## Concepts & Definitions URL: https://docs.wdk.tether.io/resources/concepts Description: Key concepts and definitions used throughout the Wallet Development Kit ## Account Abstraction Account Abstraction is a blockchain technology that separates the concept of a user account from the mechanism of transaction validation and fee payment. In traditional blockchain systems, users must pay transaction fees in the native token of the blockchain (like ETH on Ethereum). Account Abstraction allows users to pay fees in other tokens or have fees sponsored by third parties, enabling gasless transactions and enhanced user experiences. ### WDK Implementation WDK provides Account Abstraction support through specialized wallet modules: - `@tetherto/wdk-wallet-evm-erc4337` - EVM chains with ERC-4337 standard - `@tetherto/wdk-wallet-ton-gasless` - TON blockchain with gasless transactions - `@tetherto/wdk-wallet-tron-gasfree` - TRON blockchain with gas-free transactions These modules allow developers to implement gasless transaction flows where users can pay fees in tokens like USD₮ or XAU₮ instead of native blockchain tokens. ## ERC-4337 ERC-4337 is an Ethereum standard that enables Account Abstraction without requiring changes to the Ethereum protocol itself. It introduces a new transaction type called "UserOperation" that allows smart contract wallets to handle transaction validation and fee payment logic through components like EntryPoint contracts, Bundlers, and Paymasters. ## Gasless Transactions Gasless transactions allow users to perform blockchain operations without holding native tokens for gas fees. Instead, transaction fees are paid by third-party services or in alternative tokens, enabling new user onboarding, cross-chain operations, and corporate applications where companies can sponsor employee transactions. ## Paymaster Services Paymaster services are third-party providers that sponsor transaction fees on behalf of users. They accept payment in various tokens and handle the conversion and payment of gas fees to the blockchain network, providing fee estimation, gas optimization, and high transaction success rates. ## Safe Accounts Safe Accounts are smart contract wallets built on the Safe protocol that provide enhanced security features and multi-signature capabilities. In the context of ERC-4337, Safe Accounts can be used as the underlying wallet implementation, combining the security benefits of multi-signature with the flexibility of Account Abstraction for enterprise, family, and institutional use cases. ## BIP Standards BIP (Bitcoin Improvement Proposal) standards define common practices for Bitcoin and other blockchain wallets. WDK modules implement several key BIP standards for consistent wallet behavior across different blockchains. ### BIP-39 (Mnemonic Seed Phrases) BIP-39 defines a standard for generating mnemonic seed phrases from random entropy. These phrases are human-readable and can be used to recover wallet private keys. WDK modules use BIP-39 for secure seed phrase generation and validation. ### BIP-44 (Multi-Account Hierarchy) BIP-44 defines a hierarchical deterministic wallet structure that allows creating multiple accounts from a single seed phrase. The derivation path format is `m/purpose'/coin_type'/account'/change/address_index`, where each module uses its specific coin type (e.g., 60 for Ethereum, 998 for Spark). ### BIP-84 (Native SegWit) BIP-84 defines the derivation path for native SegWit addresses (P2WPKH) in Bitcoin wallets. This standard provides better security and lower transaction fees compared to legacy Bitcoin addresses. ## Lightning Network The Lightning Network is a second-layer payment protocol built on top of Bitcoin that enables instant, low-fee transactions. It works by creating payment channels between parties, allowing them to transact without broadcasting every transaction to the Bitcoin blockchain. ### Key Features - **Instant Payments**: Transactions settle immediately within payment channels - **Low Fees**: Minimal fees compared to on-chain Bitcoin transactions - **Scalability**: Can handle millions of transactions per second - **BOLT11 Invoices**: Standard format for Lightning payment requests ### WDK Integration The Spark wallet module integrates Lightning Network functionality, allowing users to create and pay Lightning invoices directly from their Spark wallets. ## Layer 2 Solutions Layer 2 solutions are protocols built on top of existing blockchains to improve scalability, reduce fees, and enhance transaction speed. They process transactions off the main blockchain and periodically settle to the base layer. ### Types of Layer 2 - **Rollups**: Bundle multiple transactions and submit them as a single transaction to the main chain - **State Channels**: Allow parties to transact off-chain and settle periodically - **Sidechains**: Independent blockchains that connect to the main chain via bridges ### WDK Support WDK modules support various Layer 2 solutions: - **Spark**: Bitcoin Layer 2 with Lightning Network integration - **EVM Rollups**: Support for Arbitrum, Optimism, and other EVM-compatible rollups ## EVM (Ethereum Virtual Machine) The Ethereum Virtual Machine is a runtime environment that executes smart contracts on Ethereum and other EVM-compatible blockchains. It provides a standardized way to run decentralized applications across different networks. ### EVM-Compatible Chains Many blockchains are EVM-compatible, meaning they can run the same smart contracts and use the same tools as Ethereum: - **Polygon**: Layer 2 scaling solution for Ethereum - **BSC**: Binance Smart Chain - **Arbitrum**: Optimistic rollup for Ethereum - **Optimism**: Layer 2 scaling solution ### WDK EVM Support The `@tetherto/wdk-wallet-evm` module works with any EVM-compatible blockchain, providing unified access to multiple networks through a single API. ## UTXO (Unspent Transaction Output) UTXO is a fundamental concept in Bitcoin and other UTXO-based blockchains. Each transaction consumes previous UTXOs and creates new ones, forming a chain of ownership. ### How UTXOs Work 1. **Inputs**: References to previous UTXOs that are being spent 2. **Outputs**: New UTXOs created by the transaction 3. **Change**: Remaining value returned to the sender as a new UTXO ### WDK UTXO Management The Bitcoin wallet module automatically handles UTXO selection and change address management, ensuring optimal transaction construction and fee calculation. ## Seed Phrases and Private Keys Seed phrases and private keys are the foundation of wallet security in blockchain systems. ### Seed Phrases (BIP-39) - **12-24 words**: Human-readable representation of wallet entropy - **Deterministic**: Same seed phrase always generates the same keys - **Recovery**: Can recover entire wallet from seed phrase - **Security**: Must be kept secure and never shared ### Private Keys - **256-bit numbers**: Cryptographic keys that control wallet funds - **Derived from seed**: Generated deterministically from seed phrase - **Signing**: Used to sign transactions and prove ownership - **Memory safety**: WDK modules handle private keys securely with automatic cleanup ## Network Types Blockchain networks come in different types for different use cases. ### Mainnet Production networks where real value is transacted: - **Ethereum Mainnet**: Production Ethereum network - **Bitcoin Mainnet**: Production Bitcoin network - **Spark Mainnet**: Production Spark network ### Testnet Development networks for testing without real value: - **Goerli/Sepolia**: Ethereum test networks - **Bitcoin Testnet**: Bitcoin test network - **Spark Testnet**: Spark test network ### Regtest Local networks for development and testing: - **Local Ethereum**: Private Ethereum network - **Bitcoin Regtest**: Local Bitcoin network - **Spark Regtest**: Local Spark network ### Testnet Funds & Faucets To test transactions without spending real assets, developers use "Testnets"—networks that mimic the main blockchain but use tokens with no monetary value. You can obtain these tokens for free from different publicly available "Faucets". Links to common "Faucets" are below. The below faucets are for testnets. The USD₮ tokens and other tokens available at the links below are not real and do not entitle the holder to anything. In particular, they cannot be redeemed with Tether International, S.A. de C.V. ("Tether International") and are not Tether Tokens as described in [Tether International's Terms of Service](https://tether.to/en/legal). The USD₮ tokens available at the links below on various testnets are intended for testing WDK on the applicable testnet. The links below are links to third-party websites and are Third-Party Information as described in Tether Operations, S.A. de [C.V.'s Website Terms](https://tether.io/terms/). #### Common Faucets * **USD₮ Test Tokens (Sepolia)**: [Pimlico Faucet](https://dashboard.pimlico.io/test-erc20-faucet) * **USD₮ Test Tokens (Sepolia)**: [Candide Faucet](https://dashboard.candide.dev/faucet) * **Ethereum (Sepolia)**: [Google Cloud Web3 Faucet](https://cloud.google.com/application/web3/faucet/ethereum/sepolia) * **Aave Test Tokens (Sepolia)**: [Aave Faucet](https://app.aave.com/faucet/) — get test USD₮, DAI and other tokens for DeFi testing * **TON Testnet**: [Testgiver Bot](https://t.me/testgiver_ton_bot) * **Bitcoin Testnet**: [CoinFaucet](https://coinfaucet.eu/en/btc-testnet/) *** ## All Modules URL: https://docs.wdk.tether.io/sdk/all-modules Description: Complete list of all available WDK modules including wallet, swidge, swap, bridge, lending, and fiat modules. A comprehensive list of all available WDK modules. Each module is designed to be modular and can be used independently or combined with others. ## Core Module The orchestrator that manages all WDK modules. | Module | Description | Documentation | |--------|-------------|---------------| | [`@tetherto/wdk-core`](https://github.com/tetherto/wdk-core) | Central orchestrator for all WDK modules | [Docs](/sdk/core-module/) | ## Wallet Modules Wallet modules provide blockchain-specific wallet functionality for managing addresses, balances, and transactions. | Module | Blockchain | Description | Documentation | |--------|------------|-------------|---------------| | [`@tetherto/wdk-wallet-btc`](https://github.com/tetherto/wdk-wallet-btc) | Bitcoin | Bitcoin SegWit wallet with BIP-39/BIP-44 support | [Docs](/sdk/wallet-modules/wallet-btc/) | | [`@tetherto/wdk-wallet-evm`](https://github.com/tetherto/wdk-wallet-evm) | EVM | Ethereum and EVM-compatible chains wallet | [Docs](/sdk/wallet-modules/wallet-evm/) | | [`@tetherto/wdk-wallet-evm-erc4337`](https://github.com/tetherto/wdk-wallet-evm-erc-4337) | EVM | ERC-4337 Account Abstraction for EVM chains | [Docs](/sdk/wallet-modules/wallet-evm-erc-4337/) | | [`@tetherto/wdk-wallet-ton`](https://github.com/tetherto/wdk-wallet-ton) | TON | TON blockchain wallet | [Docs](/sdk/wallet-modules/wallet-ton/) | | [`@tetherto/wdk-wallet-ton-gasless`](https://github.com/tetherto/wdk-wallet-ton-gasless) | TON | Gasless transactions on TON | [Docs](/sdk/wallet-modules/wallet-ton-gasless/) | | [`@tetherto/wdk-wallet-tron`](https://github.com/tetherto/wdk-wallet-tron) | TRON | TRON blockchain wallet | [Docs](/sdk/wallet-modules/wallet-tron/) | | [`@tetherto/wdk-wallet-tron-gasfree`](https://github.com/tetherto/wdk-wallet-tron-gasfree) | TRON | Gas-free transactions on TRON | [Docs](/sdk/wallet-modules/wallet-tron-gasfree/) | | [`@tetherto/wdk-wallet-solana`](https://github.com/tetherto/wdk-wallet-solana) | Solana | Solana blockchain wallet | [Docs](/sdk/wallet-modules/wallet-solana/) | | [`@tetherto/wdk-wallet-spark`](https://github.com/tetherto/wdk-wallet-spark) | Spark | Spark/Lightning Bitcoin L2 wallet | [Docs](/sdk/wallet-modules/wallet-spark/) | ## Swidge Interface Swidge is the preferred interface for new protocol providers that can quote and execute asset routes. A route can be swap-only, bridge-only, or a combined swap and bridge route. No released swidge provider module is listed yet. See the [swidge protocol interface](./swidge-modules/) for the shared API shape that new provider modules should implement. ## Swap Modules DEX swap functionality for token exchanges. | Module | Blockchain | Description | Documentation | |--------|------------|-------------|---------------| | [`@tetherto/wdk-protocol-swap-velora-evm`](https://github.com/tetherto/wdk-protocol-swap-velora-evm) | EVM | DEX aggregator swap on EVM chains | [Docs](/sdk/swap-modules/swap-velora-evm/) | ## Bridge Modules Cross-chain bridge functionality for token transfers between blockchains. | Module | Route | Description | Documentation | |--------|-------|-------------|---------------| | [`@tetherto/wdk-protocol-bridge-usdt0-evm`](https://github.com/tetherto/wdk-protocol-bridge-usdt0-evm) | EVM → EVM + Non-EVM | USD₮0 bridging from EVM source chains to EVM and non-EVM destinations | [Docs](/sdk/bridge-modules/bridge-usdt0-evm/) | ## Lending Modules DeFi lending and borrowing functionality. | Module | Blockchain | Status | Description | Documentation | |--------|------------|--------|-------------|---------------| | [`@tetherto/wdk-protocol-lending-aave-evm`](https://github.com/tetherto/wdk-protocol-lending-aave-evm) | EVM | Ready | Aave protocol integration for EVM | [Docs](/sdk/lending-modules/lending-aave-evm/) | | [`@morpho-org/wdk-protocol-lending-morpho-evm`](https://www.npmjs.com/package/@morpho-org/wdk-protocol-lending-morpho-evm) | EVM | Community | Morpho Vault V2 and Morpho Blue lending integration for EVM | [Docs](/sdk/lending-modules/lending-morpho-evm/) | ## Fiat Modules On-ramp and off-ramp functionality for fiat currency integration. | Module | Provider | Description | Documentation | |--------|----------|-------------|---------------| | [`@tetherto/wdk-protocol-fiat-moonpay`](https://github.com/tetherto/wdk-protocol-fiat-moonpay) | MoonPay | MoonPay integration for fiat on-ramp | [Docs](/sdk/fiat-modules/fiat-moonpay/) | ## Community Modules Modules built by the WDK community. See the [Community Modules](/sdk/community-modules/) page for more details. | Module | Blockchain | Description | Documentation | |--------|------------|-------------|---------------| | [`@utexo/wdk-wallet-rgb`](https://github.com/UTEXO-Protocol/wdk-wallet-rgb) | Bitcoin (RGB) | RGB protocol wallet integration | [GitHub](https://github.com/UTEXO-Protocol/wdk-wallet-rgb) | *** ## Bridge Modules Overview URL: https://docs.wdk.tether.io/sdk/bridge-modules Description: Explore WDK bridge modules for moving assets across supported chains. The Wallet Development Kit (WDK) provides a set of modules that support bridging between different blockchain networks. All modules share a common interface, ensuring consistent behavior across different blockchain implementations. WDK is introducing the [swidge interface](/sdk/swidge-modules) as the preferred interface for new swap, bridge, and combined route providers. Existing bridge modules remain supported, but new protocol integrations should prefer swidge because the standalone bridge interface is expected to be deprecated in a future release once swidge provider coverage is available. ## Bridge Protocol Modules Cross-chain bridge functionality for token transfers between blockchains: | Module | Route | Status | Documentation | |--------|-------|--------|---------------| | [`@tetherto/wdk-protocol-bridge-usdt0-evm`](https://github.com/tetherto/wdk-protocol-bridge-usdt0-evm) | EVM → EVM + Non-EVM | ✅ Ready | [Documentation](/sdk/bridge-modules/bridge-usdt0-evm/) | {/* | [`@tetherto/wdk-protocol-bridge-usdt0-ton`](https://github.com/tetherto/wdk-protocol-bridge-usdt0-ton) | TON ↔ EVM | In progress | [Documentation](/sdk/bridge-modules/bridge-usdt0-ton/) | */} ## Next steps To get started with WDK modules, follow these steps: 1. Get up and running quickly with our [Quickstart Guide](/start-building/nodejs-bare-quickstart) 2. Choose the modules that best fit your needs from the tables above 3. Check specific documentation for modules you wish to use You can also: - Learn about key concepts like [Account Abstraction](/resources/concepts#account-abstraction) and other important definitions - Use one of our ready-to-use examples to be production ready *** ## Bridge USD₮0 EVM Overview URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm Description: Overview of the @tetherto/wdk-protocol-bridge-usdt0-evm module A simple package that lets EVM wallet accounts bridge USD₮0 tokens across different blockchains. This package provides a clean API for moving tokens between chains using the LayerZero protocol and USD₮0 bridge system. ## Features - **Cross-Chain Bridge**: Move USD₮0 tokens between supported blockchains - **LayerZero Integration**: Uses LayerZero protocol for secure cross-chain transfers - **Expanded Multi-Chain Support**: Bridge across 25+ networks including all major EVM chains - **Non-EVM Destinations**: Bridge to Solana, TON, and TRON from any supported EVM chain - **Account Abstraction**: Works with both standard EVM wallets and ERC-4337 smart accounts - **Fee Management**: Built-in fee calculation and bridge cost estimation - **Token Support**: Supports USD₮0 and XAU₮0 ecosystem tokens - **Route Overrides**: Custom OFT contract addresses and destination endpoint IDs - **TypeScript Support**: Full TypeScript definitions included - **Memory Safety**: Secure transaction handling with proper error management - **Provider Flexibility**: Works with JSON-RPC URLs and EIP-1193 browser providers ## Supported Networks ### Source Chains (EVM) | Chain | Chain ID | |-------|----------| | Ethereum | 1 | | Arbitrum | 42161 | | Optimism | 10 | | Polygon | 137 | | Berachain | 80094 | | Ink | 57073 | | Plasma | 9745 | | Conflux eSpace | 1030 | | Corn | 21000000 | | Avalanche | 43114 | | Celo | 42220 | | Flare | 14 | | HyperEVM | 999 | | Mantle | 5000 | | MegaETH | 4326 | | Monad | 143 | | Morph | 2818 | | Rootstock | 30 | | Sei | 1329 | | Stable | 988 | | Unichain | 130 | | XLayer | 196 | ### Destination Chains All source chains above, plus: | Chain | Endpoint ID (EID) | |-------|-------------------| | Solana | 30168 | | TON | 30343 | | TRON | 30420 | Token support is determined by the contracts deployed on each chain. The protocol checks for `oftContract`, `legacyMeshContract`, and `xautOftContract` to determine available tokens. ## Next Steps Get started with WDK in a Node.js environment Configure the Bridge USD₮0 EVM Protocol Complete API documentation for the bridge protocol Installation, quick start, and usage examples --- ## Need Help? *** ## Bridge USD₮0 EVM API Reference URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm/api-reference Description: Complete API documentation for @tetherto/wdk-protocol-bridge-usdt0-evm ## Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [Usdt0ProtocolEvm](#usdt0protocolevm) | Main class for bridging USD₮0 tokens across blockchains. Extends `BridgeProtocol` from `@tetherto/wdk-wallet/protocols`. | [Constructor](#constructor), [Methods](#methods) | ## Usdt0ProtocolEvm The main class for bridging USD₮0 tokens across different blockchains using the LayerZero protocol. Extends `BridgeProtocol` from `@tetherto/wdk-wallet/protocols`. ### Constructor ```javascript new Usdt0ProtocolEvm(account, config?) ``` **Parameters:** - `account` (WalletAccountEvm | WalletAccountEvmErc4337 | WalletAccountReadOnlyEvm | WalletAccountReadOnlyEvmErc4337): The wallet account to use for bridge operations - `config` (BridgeProtocolConfig, optional): Configuration object - `bridgeMaxFee` (number | bigint, optional): Maximum total bridge cost in wei **Example:** ```javascript import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://eth.drpc.org' }) const bridgeProtocol = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 1000000000000000n // Maximum bridge fee in wei }) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `bridge(options, config?)` | Bridges tokens to another blockchain | `Promise` | If no provider or fee exceeds max | | `quoteBridge(options, config?)` | Estimates the cost of a bridge operation | `Promise>` | If no provider | #### `bridge(options, config?)` Bridges tokens to a different blockchain using the USD₮0 protocol. Before calling `bridge()`, approve the token allowance for the source-chain bridge spender. If you pass `oftContractAddress`, use the same address as the approval `spender`. **Parameters:** - `options` (BridgeOptions): Bridge operation options - `targetChain` (string): Destination chain name - `recipient` (string): Address that will receive the bridged tokens (EVM hex address, Solana base58 address, TON address, or TRON address) - `token` (string): Token contract address on source chain - `amount` (number | bigint): Amount to bridge in token base units - `oftContractAddress` (string, optional): Custom OFT contract address to use instead of auto-resolving from the source chain - `dstEid` (number, optional): Custom LayerZero destination endpoint ID override - `config` (`Pick & Pick`, optional): Override configuration for ERC-4337 accounts - `paymasterToken` (`{ address: string }`, optional): Paymaster token configuration for gas fees - `bridgeMaxFee` (number | bigint, optional): Override maximum bridge fee **Returns:** `Promise` - Bridge operation result **Throws:** - Error if account is read-only - Error if no provider is configured - Error if bridge fee exceeds maximum allowed **Example:** ```javascript // Standard EVM account await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x...', // Recipient address token: '0x...', // ₮ contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }) console.log('Bridge hash:', result.hash) console.log('Total fee:', result.fee) console.log('Bridge fee:', result.bridgeFee) // ERC-4337 account await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result2 = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }, { paymasterToken: { address: '0x...' }, // Paymaster token configuration bridgeMaxFee: 1000000000000000n }) console.log('Bridge hash:', result2.hash) // Single hash for bundled operations console.log('Total fee:', result2.fee) console.log('Bridge fee:', result2.bridgeFee) ``` #### `quoteBridge(options, config?)` Estimates the cost of a bridge operation without executing it. Some providers estimate the same bridge transaction that `bridge()` sends. If that estimate fails because token allowance is missing, approve the source-chain bridge spender before calling `quoteBridge()`. **Parameters:** - `options` (BridgeOptions): Bridge operation options (same as bridge method) - `config` (`Pick`, optional): Override configuration for ERC-4337 accounts - `paymasterToken` (`{ address: string }`, optional): Paymaster token configuration for gas fees **Returns:** `Promise>` - Bridge cost estimate **Throws:** Error if no provider is configured **Example:** ```javascript const quote = await bridgeProtocol.quoteBridge({ targetChain: 'polygon', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n }) console.log('Estimated fee:', quote.fee, 'wei') console.log('Bridge fee:', quote.bridgeFee, 'wei') // Check if fees are acceptable if (quote.fee + quote.bridgeFee > 1000000000000000n) { console.log('Bridge fees too high') } else { // Proceed with bridge await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'polygon', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }) } ``` ## Types ### BridgeOptions ```typescript interface BridgeOptions { targetChain: string; // Destination chain name recipient: string; // Address that will receive bridged tokens token: string; // Token contract address on source chain amount: number | bigint; // Amount to bridge in token base units oftContractAddress?: string; // Optional custom OFT contract address dstEid?: number; // Optional destination endpoint ID override } ``` ### BridgeResult ```typescript interface BridgeResult { hash: string; // Main bridge transaction hash fee: bigint; // Total gas cost in wei bridgeFee: bigint; // Bridge protocol fee in wei } ``` ### BridgeProtocolConfig ```typescript interface BridgeProtocolConfig { bridgeMaxFee?: number | bigint; // Maximum total bridge cost in wei } ``` ### EvmErc4337WalletConfig ```typescript interface EvmErc4337WalletConfig { paymasterToken?: { // Token to use for paying gas fees address: string; }; } ``` ### Supported Chains The bridge protocol supports the following chains: **Source Chains (EVM):** - `'ethereum'` (Chain ID: 1) - `'arbitrum'` (Chain ID: 42161) - ERC-4337 support - `'optimism'` (Chain ID: 10) - `'polygon'` (Chain ID: 137) - `'berachain'` (Chain ID: 80094) - `'ink'` (Chain ID: 57073) - `'plasma'` (Chain ID: 9745) - `'conflux'` (Chain ID: 1030) - `'corn'` (Chain ID: 21000000) - `'avalanche'` (Chain ID: 43114) - `'celo'` (Chain ID: 42220) - `'flare'` (Chain ID: 14) - `'hyperevm'` (Chain ID: 999) - `'mantle'` (Chain ID: 5000) - `'megaeth'` (Chain ID: 4326) - `'monad'` (Chain ID: 143) - `'morph'` (Chain ID: 2818) - `'rootstock'` (Chain ID: 30) - `'sei'` (Chain ID: 1329) - `'stable'` (Chain ID: 988) - `'unichain'` (Chain ID: 130) - `'xlayer'` (Chain ID: 196) **Destination Chains:** - **EVM destinations**: same as source-chain set above - `'solana'` (EID: 30168) - `'ton'` (EID: 30343) - `'tron'` (EID: 30420) ## Error Handling The bridge protocol throws specific errors for different failure cases: ```javascript try { await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }) } catch (error) { if (error.message.includes('not supported')) { console.error('Chain or token not supported') } if (error.message.includes('Exceeded maximum fee')) { console.error('Bridge fee too high') } if (error.message.includes('must be connected to a provider')) { console.error('Wallet not connected to blockchain') } if (error.message.includes('requires the protocol to be initialized with a non read-only account')) { console.error('Cannot bridge with read-only account') } if (error.message.includes('cannot be equal to the source chain')) { console.error('Cannot bridge to the same chain') } } ``` ## Usage Examples ### Basic Bridge Operation ```javascript import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' async function bridgeTokens() { // Create wallet account const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://eth.drpc.org' }) // Create bridge protocol const bridgeProtocol = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 1000000000000000n }) // Get quote first const quote = await bridgeProtocol.quoteBridge({ targetChain: 'arbitrum', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n }) console.log('Bridge quote:', quote) // Execute bridge await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }) console.log('Bridge result:', result) return result } ``` ### Multi-Chain Bridge ```javascript async function bridgeToMultipleChains(account, bridgeProtocol) { const chains = ['arbitrum', 'polygon', 'ethereum'] const token = '0x...' // USDT contract address const amount = 1000000n const recipient = '0x...' // Recipient address const results = [] for (const chain of chains) { try { // Get quote const quote = await bridgeProtocol.quoteBridge({ targetChain: chain, recipient, token, amount }) console.log(`Bridge to ${chain}:`, quote) // Execute bridge await account.approve({ token, spender: '0x...', // OFT or bridge spender address for the source route amount }) const result = await bridgeProtocol.bridge({ targetChain: chain, recipient, token, amount, oftContractAddress: '0x...' // Same address used as approval spender }) results.push({ chain, result }) console.log(`Bridge to ${chain} successful:`, result.hash) } catch (error) { console.error(`Bridge to ${chain} failed:`, error.message) } } return results } ``` ### ERC-4337 Gasless Bridge ```javascript import { WalletAccountEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337' async function gaslessBridge() { // Create ERC-4337 account const account = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", { chainId: 42161, provider: 'https://arb1.arbitrum.io/rpc', bundlerUrl: 'https://api.candide.dev/public/v3/arbitrum', safeModulesVersion: '0.3.0', paymasterUrl: 'https://api.candide.dev/public/v3/arbitrum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', paymasterToken: { address: '0x...' } // Paymaster token configuration }) // Create bridge protocol const bridgeProtocol = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 1000000000000000n }) // Bridge with gasless transactions await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'polygon', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }, { paymasterToken: { address: '0x...' } // Paymaster token configuration }) console.log('Gasless bridge result:', result) return result } ``` Get started with WDK in a Node.js environment Get started with WDK's Bridge USD₮0 EVM Protocol configuration Get started with WDK's Bridge USD₮0 EVM Protocol usage *** ### Need Help? *** ## Bridge USD₮0 EVM Configuration URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm/configuration Description: Configuration options and settings for @tetherto/wdk-protocol-bridge-usdt0-evm ## Bridge Protocol Configuration The `Usdt0ProtocolEvm` accepts a configuration object that defines how the bridge protocol works: ```javascript import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' // Create wallet account first const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://eth.drpc.org' }) // Create bridge protocol with configuration const bridgeProtocol = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 1000000000000000n // Optional: Maximum bridge fee in wei }) ``` ## Account Configuration The bridge protocol uses the wallet account's configuration for blockchain access: ```javascript import { WalletAccountEvm, WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm' // Full access account const account = new WalletAccountEvm( seedPhrase, "0'/0/0", // BIP-44 derivation path { provider: 'https://eth.drpc.org', transferMaxFee: 100000000000000 } ) // Read-only account const readOnlyAccount = new WalletAccountReadOnlyEvm( '0x...', // Ethereum address { provider: 'https://eth.drpc.org' } ) // Create bridge protocol const bridgeProtocol = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 1000000000000000n }) ``` ## Configuration Options ### Bridge Max Fee The `bridgeMaxFee` option sets a maximum limit for total bridge costs to prevent unexpectedly high fees. **Type:** `number | bigint` (optional) **Unit:** Wei (1 ETH = 1000000000000000000 Wei) **Examples:** ```javascript const config = { // Set maximum bridge fee to 0.001 ETH bridgeMaxFee: 1000000000000000n, } // Usage example try { await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }) } catch (error) { if (error.message.includes('Exceeded maximum fee')) { console.error('Bridge cancelled: Fee too high') } } ``` ### Provider The `provider` option comes from the wallet account configuration and specifies how to connect to the blockchain. **Type:** `string | Eip1193Provider` **Examples:** ```javascript // Option 1: Using RPC URL const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://eth.drpc.org' }) // Option 2: Using browser provider (e.g., MetaMask) const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: window.ethereum }) // Option 3: Using custom JsonRpcProvider import { JsonRpcProvider } from 'ethers' const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: new JsonRpcProvider('https://eth.drpc.org') }) ``` ## ERC-4337 Configuration When using ERC-4337 accounts, you can override configuration options during bridge operations: ```javascript // Bridge with ERC-4337 account await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }, { paymasterToken: { address: '0x...' }, // Paymaster token for gasless transactions bridgeMaxFee: 1000000000000000n // Override maximum bridge fee }) ``` ### Paymaster Token The `paymasterToken` option specifies which token to use for paying gas fees in ERC-4337 accounts. **Type:** `{ address: string }` (optional) **Format:** Object with token contract address **Example:** ```javascript await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }, { paymasterToken: { address: '0x...' // Paymaster token address } }) ``` ## Network Support The bridge protocol uses EVM wallet providers as source chains and supports both EVM and non-EVM destinations. Change the provider URL in the wallet account configuration: ```javascript // Ethereum Mainnet const ethereumAccount = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://eth.drpc.org' }) // Arbitrum const arbitrumAccount = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://arb1.arbitrum.io/rpc' }) // Polygon const polygonAccount = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://polygon-rpc.com' }) ``` ## Bridge Options When calling the bridge method, you need to provide bridge options: ```javascript const bridgeOptions = { targetChain: 'arbitrum', // Destination chain name recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, // Amount to bridge in base units oftContractAddress: '0x...', // Optional custom OFT contract address dstEid: 30110 // Optional LayerZero destination endpoint ID override } await account.approve({ token: bridgeOptions.token, spender: bridgeOptions.oftContractAddress, amount: bridgeOptions.amount }) const result = await bridgeProtocol.bridge(bridgeOptions) ``` ### Target Chain The `targetChain` option specifies which blockchain to bridge tokens to. **Type:** `string` **Supported values:** `'ethereum'`, `'arbitrum'`, `'optimism'`, `'polygon'`, `'berachain'`, `'ink'`, `'plasma'`, `'conflux'`, `'corn'`, `'avalanche'`, `'celo'`, `'flare'`, `'hyperevm'`, `'mantle'`, `'megaeth'`, `'monad'`, `'morph'`, `'rootstock'`, `'sei'`, `'stable'`, `'unichain'`, `'xlayer'`, `'solana'`, `'ton'`, `'tron'` ### Recipient The `recipient` option specifies the address that will receive the bridged tokens. **Type:** `string` **Format:** Valid address for the target chain ### Token The `token` option specifies which token contract to bridge. **Type:** `string` **Format:** Token contract address on the source chain ### Amount The `amount` option specifies how many tokens to bridge. **Type:** `number | bigint` **Unit:** Base units of the token (e.g., for USD₮: 1 USD₮ = 1000000n) ### OFT Contract Address The optional `oftContractAddress` option lets you override auto-discovery and force a specific OFT contract. **Type:** `string` (optional) **Format:** Valid EVM contract address on the source chain ### Destination EID Override The optional `dstEid` option lets you override the default LayerZero destination endpoint ID for the selected target chain. **Type:** `number` (optional) ## Error Handling The bridge protocol will throw errors for invalid configurations: ```javascript try { await account.approve({ token: '0x...', // USDT contract address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'invalid-chain', recipient: '0x...', // Recipient address token: '0x...', // USDT contract address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }) } catch (error) { if (error.message.includes('not supported')) { console.error('Chain or token not supported') } if (error.message.includes('Exceeded maximum fee')) { console.error('Bridge fee too high') } if (error.message.includes('must be connected to a provider')) { console.error('Wallet not connected to blockchain') } } ``` Get started with WDK in a Node.js environment Get started with WDK's Bridge USD₮0 EVM Protocol API Get started with WDK's Bridge USD₮0 EVM Protocol usage *** ### Need Help? *** ## Bridge Cross-Ecosystem URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-cross-ecosystem Description: Send USD₮0 from EVM toward Solana, TON, or TRON recipients. This guide covers [prerequisites](#prerequisites) and how to [bridge to Solana](#bridge-to-solana), [bridge to TON](#bridge-to-ton), and [bridge to TRON](#bridge-to-tron) using [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config). The same [`Usdt0ProtocolEvm`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#usdt0protocolevm) instance you use for EVM destinations applies; only `targetChain` and `recipient` formats change. ## Prerequisites A [`Usdt0ProtocolEvm`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#usdt0protocolevm) backed by a non-read-only EVM account, with enough USD₮ (and native gas for non-4337 accounts) on the source chain. Approve the source-chain bridge spender before calling [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config). Recipient strings must match each network’s address encoding. For `USDT_BRIDGE_SPENDER_ADDRESS`, use the source-chain OFT or bridge contract for the route. See [Bridge Tokens](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens#prerequisites) for address sources. ## Bridge to Solana You can set `targetChain` to `solana` and pass a base58 Solana address as `recipient` when calling [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config): ```javascript title="Bridge toward Solana" const USDT_TOKEN_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7' const USDT_BRIDGE_SPENDER_ADDRESS = process.env.USDT0_BRIDGE_SPENDER_ADDRESS const amount = 1000000n await account.approve({ token: USDT_TOKEN_ADDRESS, spender: USDT_BRIDGE_SPENDER_ADDRESS, amount }) const solanaResult = await bridgeProtocol.bridge({ targetChain: 'solana', recipient: 'HyXJcgYpURfDhgzuyRL7zxP4FhLg7LZQMeDrR4MXZcMN', token: USDT_TOKEN_ADDRESS, amount, oftContractAddress: USDT_BRIDGE_SPENDER_ADDRESS }) console.log('Solana bridge hash:', solanaResult.hash) console.log('Bridge fee:', solanaResult.bridgeFee) ``` Validate Solana addresses (length and base58 alphabet) before bridging. A malformed `recipient` fails the operation. ## Bridge to TON You can set `targetChain` to `ton` and supply a TON user-friendly or raw address string as `recipient` in [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config): ```javascript title="Bridge toward TON" const USDT_TOKEN_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7' const USDT_BRIDGE_SPENDER_ADDRESS = process.env.USDT0_BRIDGE_SPENDER_ADDRESS const amount = 1000000n await account.approve({ token: USDT_TOKEN_ADDRESS, spender: USDT_BRIDGE_SPENDER_ADDRESS, amount }) const tonResult = await bridgeProtocol.bridge({ targetChain: 'ton', recipient: 'EQAd31gAUhdO0d0NZsNb_cGl_Maa9PSuNhVLE9z8bBSjX6Gq', token: USDT_TOKEN_ADDRESS, amount, oftContractAddress: USDT_BRIDGE_SPENDER_ADDRESS }) console.log('TON bridge hash:', tonResult.hash) ``` ## Bridge to TRON You can set `targetChain` to `tron` and pass a base58Check TRON address (typically starting with `T`) to [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config): ```javascript title="Bridge toward TRON" const USDT_TOKEN_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7' const USDT_BRIDGE_SPENDER_ADDRESS = process.env.USDT0_BRIDGE_SPENDER_ADDRESS const amount = 1000000n await account.approve({ token: USDT_TOKEN_ADDRESS, spender: USDT_BRIDGE_SPENDER_ADDRESS, amount }) const tronResult = await bridgeProtocol.bridge({ targetChain: 'tron', recipient: 'TFG4wBaDQ8sHWWP1ACeSGnoNR6RRzevLPt', token: USDT_TOKEN_ADDRESS, amount, oftContractAddress: USDT_BRIDGE_SPENDER_ADDRESS }) console.log('TRON bridge hash:', tronResult.hash) ``` LayerZero endpoint IDs for these destinations are listed under [Supported chains](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#supported-chains) in the API reference. ## Next Steps Harden integrations with [Handle errors](/sdk/bridge-modules/bridge-usdt0-evm/guides/handle-errors), or return to [Bridge tokens](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens) for EVM-only flows and quotes. *** ## Bridge Tokens URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens Description: EVM-to-EVM bridging, quotes, fee caps, and optional OFT or endpoint overrides. This guide covers [prerequisites](#prerequisites), how to [run a standard EVM-to-EVM bridge](#run-a-standard-evm-to-evm-bridge), [quote bridge fees](#quote-bridge-fees), [override OFT routing](#override-oft-contract-and-destination-endpoint), and [set `bridgeMaxFee`](#cap-fees-with-bridgemaxfee) on the protocol. ## Prerequisites Complete [Get Started](/sdk/bridge-modules/bridge-usdt0-evm/guides/get-started): an account from [`new WalletAccountEvm(seed, path, config?)`](/sdk/wallet-modules/wallet-evm/api-reference#constructor-1) and a bridge from [`new Usdt0ProtocolEvm(account, config?)`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#constructor). The source chain RPC must match the account network. Before calling [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config), approve the source-chain bridge spender for the token and amount you want to bridge. If you pass `oftContractAddress`, use the same address as the approval `spender`. For placeholder values such as `USDT0_OFT_ADDRESS`, use the current token and bridge contract addresses from the [USDT0 deployments](https://docs.usdt0.to/technical-documentation/deployments). For the route mapping used by the WDK package, see the package [`src/config.js`](https://github.com/tetherto/wdk-protocol-bridge-usdt0-evm/blob/main/src/config.js), especially `oftContract`, `legacyMeshContract`, and `xautOftContract`. ## Run a standard EVM-to-EVM bridge You can move USD₮ on the source chain toward another EVM chain by calling [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) with `targetChain`, `recipient`, `token`, and `amount` (token base units). Amount `1000000n` is 1 USD₮ when the token uses 6 decimals. ```javascript title="Standard EVM bridge" const USDT_TOKEN_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7' const USDT0_OFT_ADDRESS = process.env.USDT0_OFT_ADDRESS const amount = 1000000n await account.approve({ token: USDT_TOKEN_ADDRESS, spender: USDT0_OFT_ADDRESS, amount }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', token: USDT_TOKEN_ADDRESS, amount, oftContractAddress: USDT0_OFT_ADDRESS }) console.log('Bridge transaction hash:', result.hash) console.log('Total fee:', result.fee, 'wei') console.log('Bridge fee:', result.bridgeFee, 'wei') ``` `bridge()` does not approve token allowance for you. Use a bounded approval for the bridge amount rather than an unlimited allowance. ## Quote bridge fees You can estimate gas and protocol fees without sending transactions using [`quoteBridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#quotebridgeoptions-config): ```javascript title="Quote before bridging" const quote = await bridgeProtocol.quoteBridge({ targetChain: 'polygon', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', token: '0xdac17f958d2ee523a2206206994597c13d831ec7', amount: 1000000n }) console.log('Estimated fee:', quote.fee, 'wei') console.log('Bridge fee:', quote.bridgeFee, 'wei') ``` Compare `quote.fee` and `quote.bridgeFee` to your risk limits before calling [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config). Some providers estimate the bridge transaction during [`quoteBridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#quotebridgeoptions-config). If the estimate fails because allowance is missing, approve the same `token`, `spender`, and `amount` before quoting. ## Override OFT contract and destination endpoint You can point [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) at a specific OFT contract and LayerZero destination endpoint ID when auto-resolution is not enough. Supply values from your deployment or integration configuration (environment variables shown for illustration): ```javascript title="Custom OFT and dstEid on bridge" const USDT_TOKEN_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7' const USDT0_OFT_ADDRESS = process.env.USDT0_OFT_ADDRESS const amount = 1000000n await account.approve({ token: USDT_TOKEN_ADDRESS, spender: USDT0_OFT_ADDRESS, amount }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', token: USDT_TOKEN_ADDRESS, amount, oftContractAddress: USDT0_OFT_ADDRESS, dstEid: Number(process.env.CUSTOM_DST_EID) }) console.log('Bridge transaction hash:', result.hash) ``` You can obtain matching fee estimates with [`quoteBridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#quotebridgeoptions-config) using the same `oftContractAddress` and `dstEid` fields: ```javascript title="Quote with OFT overrides" const customQuote = await bridgeProtocol.quoteBridge({ targetChain: 'arbitrum', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', token: '0xdac17f958d2ee523a2206206994597c13d831ec7', amount: 1000000n, oftContractAddress: process.env.USDT0_OFT_ADDRESS, dstEid: Number(process.env.CUSTOM_DST_EID) }) console.log('Custom route quote fee:', customQuote.fee, 'wei') ``` Invalid pairings of `oftContractAddress` and `dstEid` fail at execution time. Validate addresses and endpoint IDs against LayerZero and your token deployment. ## Cap fees with bridgeMaxFee You can pass `bridgeMaxFee` into the [`new Usdt0ProtocolEvm(account, config?)`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#constructor) constructor so [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) rejects operations whose cost exceeds the cap: ```javascript title="Protocol-level bridgeMaxFee" import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' const cappedBridge = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 1000000000000000n }) ``` If the quote exceeds `bridgeMaxFee`, [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) throws (for example when the message includes `Exceeded maximum fee`). ERC-4337 accounts can also pass `bridgeMaxFee` in the second argument to [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config); see [Bridge with ERC-4337](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337). ## Next Steps Bridge to Solana, TON, or TRON in [Bridge cross-ecosystem](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-cross-ecosystem), or switch to a smart account in [Bridge with ERC-4337](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337). *** ## Bridge with ERC-4337 URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337 Description: Gasless USD₮0 bridging using WalletAccountEvmErc4337 and paymaster options. This guide covers [prerequisites](#prerequisites), how to [create an ERC-4337 account](#create-a-walletaccountevmerc4337-account), and how to [call the bridge with paymaster configuration](#run-a-gasless-bridge-with-paymaster-options). ## Prerequisites * `@tetherto/wdk-wallet-evm-erc-4337` installed alongside [@tetherto/wdk-protocol-bridge-usdt0-evm](https://www.npmjs.com/package/@tetherto/wdk-protocol-bridge-usdt0-evm). * Bundler and paymaster endpoints for your chain (example uses Arbitrum public URLs from the API reference). ## Create a WalletAccountEvmErc4337 account You can construct an ERC-4337 signing account using [`new WalletAccountEvmErc4337(seed, path, config)`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#constructor-1) with chain, provider, bundler, and paymaster settings: ```javascript title="ERC-4337 account on Arbitrum" import { WalletAccountEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337' const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const account = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", { chainId: 42161, provider: 'https://arb1.arbitrum.io/rpc', bundlerUrl: 'https://api.candide.dev/public/v3/arbitrum', safeModulesVersion: '0.3.0', paymasterUrl: 'https://api.candide.dev/public/v3/arbitrum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', paymasterToken: { address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' } }) ``` You can wrap that account with the [`new Usdt0ProtocolEvm(account, config?)`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#constructor) constructor: ```javascript title="Usdt0ProtocolEvm with ERC-4337 account" import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' const bridgeProtocol = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 1000000000000000n }) ``` ## Run a gasless bridge with paymaster options You can execute [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) with a second argument that includes `paymasterToken` (and optional `bridgeMaxFee` override) as described in the [methods table](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#methods). Approve the source-chain bridge spender first, then call [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config). Each call returns one hash for the submitted user operation. ```javascript title="Gasless bridge with paymasterToken" const USDT_TOKEN_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7' const USDT0_OFT_ADDRESS = process.env.USDT0_OFT_ADDRESS const amount = 1000000n const paymasterToken = { address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' } await account.approve({ token: USDT_TOKEN_ADDRESS, spender: USDT0_OFT_ADDRESS, amount }) const result = await bridgeProtocol.bridge( { targetChain: 'polygon', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', token: USDT_TOKEN_ADDRESS, amount, oftContractAddress: USDT0_OFT_ADDRESS }, { paymasterToken, bridgeMaxFee: 1000000000000000n } ) console.log('Bridge hash:', result.hash) console.log('Total fee:', result.fee) console.log('Bridge fee:', result.bridgeFee) ``` The approval and bridge calls are separate user operations. Persist both hashes if your app needs to audit the complete flow. Paymaster policies, token addresses, and URLs are service-specific. Confirm supported tokens and networks with your bundler or paymaster provider before production use. ## Next Steps Bridge to non-EVM chains in [Bridge cross-ecosystem](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-cross-ecosystem). For failure modes and cleanup, read [Handle errors](/sdk/bridge-modules/bridge-usdt0-evm/guides/handle-errors). *** ## Get Started URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm/guides/get-started Description: Install the bridge package, wire WalletAccountEvm, and review supported chains. This guide shows how to [install the package](#install-the-package), [create an EVM account](#create-an-evm-account), [instantiate the bridge protocol](#instantiate-the-bridge-protocol), and review [supported chains](#supported-chains). ## Install the package ### Prerequisites * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually bundled with Node.js. You can add the published package to your project from npm: [@tetherto/wdk-protocol-bridge-usdt0-evm](https://www.npmjs.com/package/@tetherto/wdk-protocol-bridge-usdt0-evm). ```bash title="Install @tetherto/wdk-protocol-bridge-usdt0-evm" npm install @tetherto/wdk-protocol-bridge-usdt0-evm ``` ## Create an EVM account You can construct a signing account using [`new WalletAccountEvm(seed, path, config?)`](/sdk/wallet-modules/wallet-evm/api-reference) from `@tetherto/wdk-wallet-evm` with an RPC `provider`: ```javascript title="Create WalletAccountEvm" import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://eth.drpc.org' }) ``` **Seed phrase:** Store the mnemonic securely. Anyone with the phrase controls the funds on derived accounts. ## Instantiate the bridge protocol You can create a [`Usdt0ProtocolEvm`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference) instance with the [`new Usdt0ProtocolEvm(account, config?)`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference) constructor. Optional `bridgeMaxFee` caps the total bridge cost in wei (see [BridgeProtocolConfig](/sdk/bridge-modules/bridge-usdt0-evm/api-reference)): ```javascript title="Construct Usdt0ProtocolEvm" import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' const bridgeProtocol = new Usdt0ProtocolEvm(account, { bridgeMaxFee: 1000000000000000n }) ``` The account must not be read-only. Read-only accounts cannot call [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference). ## Supported chains Bridge operations use EVM source chains listed in the [API reference](/sdk/bridge-modules/bridge-usdt0-evm/api-reference). Destination routes include the same EVM set where USD₮0 contracts are deployed, plus Solana (EID 30168), TON (EID 30343), and TRON (EID 30420). **Source chains (EVM, `targetChain` keys)** | Chain | Key | Chain ID | | --- | --- | --- | | Ethereum | `ethereum` | 1 | | Arbitrum | `arbitrum` | 42161 | | Optimism | `optimism` | 10 | | Polygon | `polygon` | 137 | | Berachain | `berachain` | 80094 | | Ink | `ink` | 57073 | | Plasma | `plasma` | 9745 | | Conflux eSpace | `conflux` | 1030 | | Corn | `corn` | 21000000 | | Avalanche | `avalanche` | 43114 | | Celo | `celo` | 42220 | | Flare | `flare` | 14 | | HyperEVM | `hyperevm` | 999 | | Mantle | `mantle` | 5000 | | MegaETH | `megaeth` | 4326 | | Monad | `monad` | 143 | | Morph | `morph` | 2818 | | Rootstock | `rootstock` | 30 | | Sei | `sei` | 1329 | | Stable | `stable` | 988 | | Unichain | `unichain` | 130 | | XLayer | `xlayer` | 196 | Arbitrum supports ERC-4337 workflows (see [Bridge with ERC-4337](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337)). **Non-EVM destinations** | Network | `targetChain` | Endpoint ID | | --- | --- | --- | | Solana | `solana` | 30168 | | TON | `ton` | 30343 | | TRON | `tron` | 30420 | ## Next Steps Run a standard EVM-to-EVM transfer with [Bridge tokens](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens), or use [Bridge with ERC-4337](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337) for gasless flows on supported networks. *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm/guides/handle-errors Description: Catch bridge failures, interpret messages, and dispose of signing accounts safely. This guide describes [errors thrown by the bridge](#errors-from-the-bridge-protocol), [how to catch and branch on messages](#catch-and-branch-on-error-messages), and [best practices](#best-practices) for clearing sensitive material from memory. ## Errors from the bridge protocol Calls to [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) and [`quoteBridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#quotebridgeoptions-config) throw when the account is read-only, no provider is configured, the route is invalid, fees exceed [`bridgeMaxFee`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeprotocolconfig), or the destination matches the source chain. The [API reference](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#error-handling) lists representative `error.message` substrings. ## Catch and branch on error messages You can wrap [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) in `try/catch` and inspect `error.message` for stable substrings: ```javascript title="Handle bridge errors" try { await account.approve({ token: '0xdac17f958d2ee523a2206206994597c13d831ec7', spender: process.env.USDT0_OFT_ADDRESS, amount: 1000000n }) const result = await bridgeProtocol.bridge({ targetChain: 'arbitrum', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', token: '0xdac17f958d2ee523a2206206994597c13d831ec7', amount: 1000000n, oftContractAddress: process.env.USDT0_OFT_ADDRESS }) console.log('Bridge successful:', result.hash) } catch (error) { console.error('Bridge failed:', error.message) if (error.message.includes('not supported')) { console.error('Chain or token not supported') } if (error.message.includes('Exceeded maximum fee')) { console.error('Bridge fee above bridgeMaxFee') } if (error.message.includes('insufficient funds')) { console.error('Not enough tokens or gas') } if (error.message.includes('must be connected to a provider')) { console.error('Wallet not connected to blockchain') } if ( error.message.includes( 'requires the protocol to be initialized with a non read-only account' ) ) { console.error('Cannot bridge with read-only account') } if (error.message.includes('cannot be equal to the source chain')) { console.error('Source and destination chain must differ') } } ``` Prefer [`quoteBridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#quotebridgeoptions-config) before [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) when you want to fail early on fee or route issues without broadcasting. ## Best practices You can clear private key material from memory when a session ends by calling [`account.dispose()`](/sdk/wallet-modules/wallet-evm/api-reference#dispose-1) on [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference#walletaccountevm), or [`account.dispose()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#dispose-1) on [`WalletAccountEvmErc4337`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference#walletaccountevmerc4337): ```javascript title="Dispose EVM account after use" account.dispose() ``` Call dispose only when you no longer need signing for that account instance. Create a new account object for later sessions. Combine quoting, fee caps via [`new Usdt0ProtocolEvm(account, config?)`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#constructor), and user-facing validation of `targetChain` and `recipient` to reduce avoidable failures. ## Next Steps Review configuration defaults in [WDK Bridge USD₮0 EVM Protocol Configuration](/sdk/bridge-modules/bridge-usdt0-evm/configuration) or return to [Get Started](/sdk/bridge-modules/bridge-usdt0-evm/guides/get-started) for setup. *** ## Bridge USD₮0 EVM Usage URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm/usage Description: Task-focused guides for @tetherto/wdk-protocol-bridge-usdt0-evm # Usage The [@tetherto/wdk-protocol-bridge-usdt0-evm](https://www.npmjs.com/package/@tetherto/wdk-protocol-bridge-usdt0-evm) package bridges USD₮0 across EVM and selected non-EVM networks. Use the guides below for setup, standard and gasless bridging, cross-ecosystem recipients, and error handling. Install the package, attach WalletAccountEvm, and review supported chains. EVM-to-EVM bridges, quotes, fee caps, and optional OFT or endpoint overrides. Gasless bridging with WalletAccountEvmErc4337 and paymaster options. Send toward Solana, TON, or TRON recipients from EVM. Interpret bridge failures and dispose of signing material safely. Get started with WDK in a Node.js environment Configure RPC, fees, and protocol options for this bridge Constructor, methods, types, and error behavior *** *** ## Community Modules URL: https://docs.wdk.tether.io/sdk/community-modules Description: Explore WDK modules built by the community and learn how to create your own custom modules. The WDK ecosystem is enriched by modules developed by our community. These modules extend WDK's capabilities to support additional blockchains, protocols, and use cases. Community modules are developed and maintained independently by third-party contributors. Tether and the WDK Team do not endorse or assume responsibility for their code, security, or maintenance. Use your own judgment and proceed at your own risk. ## Available Community Modules | Module | Type | Description | Documentation | Author | |--------|------|-------------|---------------|--------| | [@utexo/wdk-wallet-rgb](https://www.npmjs.com/package/@utexo/wdk-wallet-rgb) ([GitHub](https://github.com/UTEXO-Protocol/wdk-wallet-rgb)) | Wallet Module | Wallet module for RGB, Bitcoin-based smart contracts | - | [UTEXO](https://github.com/UTEXO-Protocol) | | [@base58-io/wdk-wallet-cosmos](https://www.npmjs.com/package/@base58-io/wdk-wallet-cosmos) ([GitHub](https://github.com/base58-io/wdk-wallet-cosmos)) | Wallet Module | Wallet module for Cosmos-compatible blockchains | - | [Base58](https://base58.io/) | | [@morpho-org/wdk-protocol-lending-morpho-evm](https://www.npmjs.com/package/@morpho-org/wdk-protocol-lending-morpho-evm) ([GitHub](https://github.com/morpho-org/sdks/tree/main/packages/wdk-protocol-lending-morpho-evm)) | Lending Module | Morpho EVM lending module for vault deposits, collateral supply, borrowing, repayment, and position reads | [Docs](/sdk/lending-modules/lending-morpho-evm/) | [Morpho Association](https://morpho.org/) | --- ## Create Your Own Module Want to extend WDK with your own custom module? Use the `create-wdk-module` CLI to scaffold a fully configured project in seconds: ```bash title="Scaffold a new module" npx @tetherto/create-wdk-module@latest ``` The CLI generates source files, tests, TypeScript type definitions, and CI workflows for all five module types (wallet, swap, bridge, lending, fiat). See the [Create WDK Module documentation](/tools/create-wdk-module) for the full guide, CLI options, and generated project structure. You can also: 1. **Study existing modules** - Review the source code of official WDK modules on [GitHub](https://github.com/orgs/tetherto/repositories?q=wdk) to understand the patterns and interfaces 2. **Join the community** - Connect with other developers on our [Discord](https://discord.gg/arYXDhHB2w) to discuss your ideas 3. **Open an issue** - Have questions? Open an issue on the relevant repository --- ## Submit Your Module If you've built a WDK module, we'd love to feature it here! **To submit your module:** 1. Ensure your module follows WDK interface conventions 2. Include comprehensive documentation and a clear README 3. Make the repository publicly accessible 4. Submit through our [Community Form](https://forms.gle/wmNwc5epxaa85u8a9) or share on our **#wdk-showcase** Discord channel Your module may be featured in our documentation and community showcases. --- ## Guidelines for Community Modules Community modules should: - Implement the standard WDK module interface - Include TypeScript type definitions - Provide clear installation and usage instructions - Be open source or publicly accessible - Include appropriate tests and examples *** ## WDK Core URL: https://docs.wdk.tether.io/sdk/core-module Description: Overview of the @tetherto/wdk-core module This package serves as the main entry point and **orchestrator for all WDK wallet and protocol modules**, allowing you to register and manage different blockchain wallets through a single, unified interface. ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's configuration Get started with WDK's API Get started with WDK's usage *** ## Need Help? *** ## WDK Core API Reference URL: https://docs.wdk.tether.io/sdk/core-module/api-reference Description: Complete API documentation for @tetherto/wdk ## Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WDK](#wdk) | Main class for managing wallets across multiple blockchains. Orchestrates wallet managers and protocols. | [Constructor](#constructor), [Methods](#methods) | | [IWalletAccount](#iwalletaccount) | Base writable wallet account interface from `@tetherto/wdk-wallet`. | [Methods](#methods-1) | | [IWalletAccountWithProtocols](#iwalletaccountwithprotocols) | Extended wallet account interface that supports protocol registration and access. Extends `IWalletAccount`. | [Methods](#methods-2) | ## WDK The main class for managing wallets across multiple blockchains. This class serves as an orchestrator that allows you to register different wallet managers and protocols, providing a unified interface for multi-chain operations. ### Constructor ```javascript title="Constructor" new WDK(seed) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes **Example:** ```javascript title="Initialize WDK" import WDK from '@tetherto/wdk' // With seed phrase const wdk = new WDK('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about') // With seed bytes const seedBytes = new Uint8Array([...]) const wdk2 = new WDK(seedBytes) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `registerWallet(blockchain, wallet, config)` | Registers a new wallet manager for a blockchain | `WDK` | - | | `registerProtocol(blockchain, label, protocol, config)` | Registers a protocol globally for a blockchain | `WDK` | - | | `registerMiddleware(blockchain, middleware)` | Registers middleware for account decoration | `WDK` | - | | `getAccount(blockchain, index?)` | Returns a wallet account for a blockchain and index | `Promise` | If wallet not registered | | `getAccountByPath(blockchain, path)` | Returns a wallet account for a blockchain and derivation path | `Promise` | If wallet not registered | | `getFeeRates()` | Returns current fee rates | `Promise` | - | | `dispose(blockchains?)` | Disposes all registered wallets, or only the named blockchains, and clears their sensitive data | `void` | - | ##### `registerWallet(blockchain, wallet, config)` Registers a new wallet manager for a specific blockchain. **Type Parameters:** - `W`: `typeof WalletManager` - A class that extends the `@tetherto/wdk-wallet`'s `WalletManager` class **Parameters:** - `blockchain` (string): The name of the blockchain (e.g., "ethereum", "ton", "bitcoin") - `wallet` (W): The wallet manager class - `config` (`ConstructorParameters[1]`): The configuration object for the wallet **Returns:** `WDK` - The wdk manager instance (supports method chaining) **Example:** ```javascript title="Register Wallets" import WDK from '@tetherto/wdk' import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerTon from '@tetherto/wdk-wallet-ton' const wdk = new WDK(seedPhrase) // Register EVM wallet wdk.registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth.drpc.org' }) // Register TON wallet wdk.registerWallet('ton', WalletManagerTon, { tonApiKey: 'YOUR_TON_API_KEY', tonApiEndpoint: 'https://tonapi.io' }) // Method chaining const wdk2 = new WDK(seedPhrase) .registerWallet('ethereum', WalletManagerEvm, ethereumWalletConfig) .registerWallet('ton', WalletManagerTon, tonWalletConfig) ``` ##### `registerProtocol(blockchain, label, protocol, config)` Registers a protocol globally for all accounts of a specific blockchain. **Type Parameters:** - `P`: `typeof SwapProtocol | typeof BridgeProtocol | typeof LendingProtocol` - A class that extends one of the `@tetherto/wdk-wallet/protocol`'s classes **Parameters:** - `blockchain` (string): The name of the blockchain - `label` (string): Unique label for the protocol (must be unique per blockchain and protocol type) - `protocol` (P): The protocol class - `config` (`ConstructorParameters

[1]`): The protocol configuration **Returns:** `WDK` - The wdk manager instance (supports method chaining) **Example:** ```javascript title="Register Protocols" import veloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' // Register swap protocol for Ethereum wdk.registerProtocol('ethereum', 'velora', veloraProtocolEvm, { apiKey: 'YOUR_velora_API_KEY' }) // Register bridge protocol for Ethereum wdk.registerProtocol('ethereum', 'usdt0', Usdt0ProtocolEvm) // Method chaining const wdk2 = new WDK(seedPhrase) .registerWallet('ethereum', WalletManagerEvm, ethereumWalletConfig) .registerProtocol('ethereum', 'velora', veloraProtocolEvm, veloraProtocolConfig) ``` ##### `registerMiddleware(blockchain, middleware)` Registers middleware for account decoration and enhanced functionality. **Parameters:** - `blockchain` (string): The name of the blockchain - `middleware` (`(account: A) => Promise`): Middleware function called when deriving accounts **Returns:** `WDK` - The wdk manager instance (supports method chaining) **Example:** ```javascript title="Register Middleware" // Simple logging middleware wdk.registerMiddleware('ethereum', async (account) => { console.log('New account:', await account.getAddress()) }) // Failover cascade middleware import { getFailoverCascadeMiddleware } from '@tetherto/wdk-wrapper-failover-cascade' wdk.registerMiddleware('ethereum', getFailoverCascadeMiddleware({ fallbackOptions: { retries: 3, delay: 1000 } })) // Method chaining const wdk2 = new WDK(seedPhrase) .registerWallet('ethereum', WalletManagerEvm, ethereumWalletConfig) .registerMiddleware('ethereum', async (account) => { console.log('New account:', await account.getAddress()) }) ``` ##### `getAccount(blockchain, index?)` Returns a wallet account for a specific blockchain and index using BIP-44 derivation. **Parameters:** - `blockchain` (string): The name of the blockchain (e.g., "ethereum") - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise` - The wallet account with protocol support **Throws:** Error if no wallet has been registered for the given blockchain **Example:** ```javascript title="Get Account" // Get first account (index 0) const account = await wdk.getAccount('ethereum', 0) // Get second account (index 1) const account1 = await wdk.getAccount('ethereum', 1) // Default index (0) const defaultAccount = await wdk.getAccount('ethereum') // This will throw an error if no wallet registered for 'tron' try { const tronAccount = await wdk.getAccount('tron', 0) } catch (error) { console.error('No wallet registered for tron blockchain') } ``` ##### `getAccountByPath(blockchain, path)` Returns a wallet account for a specific blockchain and BIP-44 derivation path. **Parameters:** - `blockchain` (string): The name of the blockchain (e.g., "ethereum") - `path` (string): The derivation path (e.g., "0'/0/0") **Returns:** `Promise` - The wallet account with protocol support **Throws:** Error if no wallet has been registered for the given blockchain **Example:** ```javascript title="Get Account by Path" // Full path: m/44'/60'/0'/0/1 const account = await wdk.getAccountByPath('ethereum', "0'/0/1") // Different derivation path const customAccount = await wdk.getAccountByPath('ton', "1'/2/3") ``` ##### `getFeeRates()` Returns current fee rates for all registered blockchains. **Returns:** `Promise` - The fee rates in base units **Example:** ```javascript title="Get Fee Rates" const feeRates = await wdk.getFeeRates() console.log('Fee rates:', feeRates) ``` ##### `dispose(blockchains?)` Disposes all registered wallets when called without arguments, or only the wallets for the named blockchains when you pass a string array. **Parameters:** - `blockchains` (string[], optional): The blockchain identifiers to dispose. Omit this parameter to dispose every registered wallet. **Example:** ```javascript title="Dispose WDK" // Clean up all sensitive data wdk.dispose() // Dispose only one registered wallet wdk.dispose(['ethereum']) ``` ### Static Methods | Method | Description | Returns | |--------|-------------|---------| | `getRandomSeedPhrase(wordCount?)` | Returns a random BIP-39 seed phrase (12 or 24 words) | `string` | | `isValidSeedPhrase(seedPhrase)` | Checks if a seed phrase is valid | `boolean` | ##### `getRandomSeedPhrase(wordCount?)` Returns a random BIP-39 seed phrase. Supports both 12-word (128-bit entropy) and 24-word (256-bit entropy) seed phrases. **Parameters:** - `wordCount` (12 | 24, optional): The number of words in the seed phrase. Defaults to 12. **Returns:** `string` - The seed phrase **Example:** ```javascript title="Generate Random Seed" // Generate 12-word seed phrase (default) const seedPhrase12 = WDK.getRandomSeedPhrase() console.log('Generated 12-word seed:', seedPhrase12) // Output: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" // Generate 24-word seed phrase (higher security) const seedPhrase24 = WDK.getRandomSeedPhrase(24) console.log('Generated 24-word seed:', seedPhrase24) // Output: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" ``` ##### `isValidSeedPhrase(seedPhrase)` Checks if a seed phrase is valid according to BIP-39 standards. **Parameters:** - `seedPhrase` (string): The seed phrase to validate **Returns:** `boolean` - True if the seed phrase is valid **Example:** ```javascript title="Validate Seed Phrase" const isValid = WDK.isValidSeedPhrase('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about') console.log('Seed phrase valid:', isValid) // true const isInvalid = WDK.isValidSeedPhrase('invalid seed phrase') console.log('Seed phrase valid:', isInvalid) // false ``` ## IWalletAccount Base writable wallet account interface exposed by `@tetherto/wdk-wallet`. Blockchain modules implement this interface and may narrow the transaction type accepted by `signTransaction()` and `sendTransaction()`. ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getAddress()` | Returns the account address | `Promise` | - | | `sign(message)` | Signs a message with the account private key | `Promise` | - | | `signTransaction(tx)` | Signs a transaction without broadcasting it | `Promise` | If the transaction is invalid for the module | | `verify(message, signature)` | Verifies a message signature | `Promise` | - | | `sendTransaction(tx)` | Signs, broadcasts, and returns the transaction result | `Promise` | If provider access or broadcast fails | | `transfer(options)` | Transfers a token where supported by the module | `Promise` | If the module does not support token transfers | | `toReadOnlyAccount()` | Returns a read-only account copy | `Promise` | - | | `dispose()` | Clears sensitive account material from memory | `void` | - | ##### `signTransaction(tx)` Signs a transaction with the account private key and returns the signed transaction payload without broadcasting it. Use this when your app needs offline signing, external transaction submission, or a separate review step before broadcast. **Parameters:** - `tx` (Transaction): Module-specific transaction object. For example, EVM accounts accept `EvmTransaction`, and Bitcoin accounts accept `BtcTransaction`. **Returns:** `Promise` - The signed transaction payload. Wallet modules may narrow this return type, such as a hex string for EVM and Bitcoin transactions. **Example:** ```typescript title="Sign Without Broadcasting" const account = await wdk.getAccount('ethereum', 0) const signedTransaction = await account.signTransaction({ to: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', value: 1000000000000000n }) console.log('Signed transaction:', signedTransaction) ``` ## IWalletAccountWithProtocols Extended wallet account interface that supports protocol registration and access. Extends `IWalletAccount` from `@tetherto/wdk-wallet`. ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `registerProtocol(label, protocol, config)` | Registers a protocol for this specific account | `IWalletAccountWithProtocols` | - | | `getSwapProtocol(label)` | Returns the swap protocol with the given label | `ISwapProtocol` | If protocol not found | | `getBridgeProtocol(label)` | Returns the bridge protocol with the given label | `IBridgeProtocol` | If protocol not found | | `getLendingProtocol(label)` | Returns the lending protocol with the given label | `ILendingProtocol` | If protocol not found | ##### `registerProtocol(label, protocol, config)` Registers a new protocol for this specific account. **Type Parameters:** - `P`: `typeof SwapProtocol | typeof BridgeProtocol | typeof LendingProtocol` - A class that extends one of the `@tetherto/wdk-wallet/protocol`'s classes **Parameters:** - `label` (string): Unique label for the protocol (must be unique per account and protocol type) - `protocol` (P): The protocol class - `config` (`ConstructorParameters

[1]`): The protocol configuration **Returns:** `IWalletAccountWithProtocols` - The account instance (supports method chaining) **Example:** ```javascript title="Register Protocol for Account" import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' const account = await wdk.getAccount('ethereum', 0) // Register protocol for this specific account account.registerProtocol('usdt0', Usdt0ProtocolEvm, { apiKey: 'YOUR_API_KEY' }) // Method chaining const account2 = await wdk.getAccount('ethereum', 1) .registerProtocol('usdt0', Usdt0ProtocolEvm, usdt0ProtocolConfig) ``` ##### `getSwapProtocol(label)` Returns the swap protocol with the given label. **Parameters:** - `label` (string): The protocol label **Returns:** `ISwapProtocol` - The swap protocol instance **Throws:** Error if no swap protocol with the given label has been registered **Example:** ```javascript title="Get Swap Protocol" import veloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' // Register swap protocol account.registerProtocol('velora', veloraProtocolEvm, veloraProtocolConfig) // Get swap protocol const velora = account.getSwapProtocol('velora') // Use the protocol const swapResult = await velora.swap({ tokenIn: '0x...', tokenOut: '0x...', tokenInAmount: 1000000n }) // This will throw an error // try { // const uniswap = account.getSwapProtocol('uniswap') // } catch (error) { // console.error('No swap protocol with label "uniswap" found') // } ``` ##### `getBridgeProtocol(label)` Returns the bridge protocol with the given label. **Parameters:** - `label` (string): The protocol label **Returns:** `IBridgeProtocol` - The bridge protocol instance **Throws:** Error if no bridge protocol with the given label has been registered **Example:** ```javascript title="Get Bridge Protocol" import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' // Register bridge protocol account.registerProtocol('usdt0', Usdt0ProtocolEvm) // Get bridge protocol const usdt0 = account.getBridgeProtocol('usdt0') // Use the protocol await account.approve({ token: '0x...', spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const bridgeResult = await usdt0.bridge({ targetChain: 'arbitrum', recipient: '0x...', token: '0x...', amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }) ``` ##### `getLendingProtocol(label)` Returns the lending protocol with the given label. **Parameters:** - `label` (string): The protocol label **Returns:** `ILendingProtocol` - The lending protocol instance **Throws:** Error if no lending protocol with the given label has been registered **Example:** ```javascript title="Get Lending Protocol" import AaveProtocolEvm from '@tetherto/wdk-protocol-lending-aave-evm' // Register lending protocol account.registerProtocol('aave', AaveProtocolEvm, aaveProtocolConfig) // Get lending protocol const aave = account.getLendingProtocol('aave') // Use the protocol const supplyResult = await aave.supply({ token: '0x...', amount: 1000000n }) ``` ## Complete Example ```javascript title="Complete WDK Flow" import WDK from '@tetherto/wdk' import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerTon from '@tetherto/wdk-wallet-ton' import veloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' // Initialize WDK Manager const wdk = new WDK(seedPhrase) .registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth.drpc.org' }) .registerWallet('ton', WalletManagerTon, { tonApiKey: 'YOUR_TON_API_KEY', tonApiEndpoint: 'https://tonapi.io' }) .registerProtocol('ethereum', 'velora', veloraProtocolEvm, { apiKey: 'YOUR_velora_API_KEY' }) .registerProtocol('ethereum', 'usdt0', Usdt0ProtocolEvm) // Get accounts const accountEth = await wdk.getAccount('ethereum', 3) const accountTon = await wdk.getAccountByPath('ton', "1'/2/3") // Use wallet account methods const { hash, fee } = await accountEth.sendTransaction({ to: '0x...', value: 1000000000000000000n // 1 ETH }) // Use protocols const velora = accountEth.getSwapProtocol('velora') const swapResult = await velora.swap(swapOptions) const usdt0 = accountEth.getBridgeProtocol('usdt0') // bridgeOptions.oftContractAddress is the source-chain bridge spender. await accountEth.approve({ token: bridgeOptions.token, spender: bridgeOptions.oftContractAddress, amount: bridgeOptions.amount }) const bridgeResult = await usdt0.bridge(bridgeOptions) // Clean up wdk.dispose() ``` ## Types ### FeeRates ```typescript title="Type: FeeRates" interface FeeRates { [blockchain: string]: { normal: number; fast: number; }; } ``` ### Middleware Function ```typescript title="Type: MiddlewareFunction" type MiddlewareFunction = ( account: A ) => Promise; ``` ### Protocol Types ```typescript title="Types: Protocol Interfaces" // Swap Protocol interface ISwapProtocol { swap(options: SwapOptions): Promise; } // Bridge Protocol interface IBridgeProtocol { bridge(options: BridgeOptions): Promise; } // Lending Protocol interface ILendingProtocol { supply(options: LendingOptions): Promise; withdraw(options: LendingOptions): Promise; borrow(options: LendingOptions): Promise; repay(options: LendingOptions): Promise; } // Swidge Protocol (unified swap + bridge + route) interface ISwidgeProtocol extends ISwapProtocol, IBridgeProtocol { quoteSwidge(options: SwidgeOptions): Promise; swidge(options: SwidgeOptions, config?: SwidgeProtocolConfig): Promise; getSwidgeStatus(id: string, options?: SwidgeStatusOptions): Promise; getSupportedChains(): Promise; getSupportedTokens(options?: SwidgeSupportedTokensOptions): Promise; } ``` ### Swidge Protocol `SwidgeProtocol` is an abstract base class exported from `@tetherto/wdk-wallet/protocols` for provider packages that implement a single, route-aware surface for same-chain swaps and cross-chain bridges. It implements `ISwidgeProtocol`, which extends both `ISwapProtocol` and `IBridgeProtocol`, so the base class derives `swap()`, `quoteSwap()`, `bridge()`, and `quoteBridge()` by delegating to `swidge()` and `quoteSwidge()`. Provider subclasses implement the abstract methods below. | Method | Description | Returns | |--------|-------------|---------| | `quoteSwidge(options)` | Returns a non-binding quote for a swap/bridge operation | `Promise` | | `swidge(options, config?)` | Executes a swap/bridge operation | `Promise` | | `getSwidgeStatus(id, options?)` | Returns the current status of an in-flight operation | `Promise` | | `getSupportedChains()` | Returns the chains the provider supports | `Promise` | | `getSupportedTokens(options?)` | Returns the tokens the provider supports, optionally route-scoped | `Promise` | The `SwidgeOptions` input combines common fields (`fromToken`, `toToken`, optional `toChain`, `recipient`, `refundAddress`, `slippage`) with either an exact-in (`fromTokenAmount`) or exact-out (`toTokenAmount`) amount. The optional `SwidgeProtocolConfig` accepts `maxNetworkFeeBps` and `maxProtocolFeeBps` to cap acceptable fees. See the [Swidge Protocol Interface](/sdk/swidge-modules) page for the full discovery, quote, execution, status, fee, and result shapes. ```typescript title="Type: ISwidgeProtocol Options and Results" type SwidgeProtocolConfig = { maxNetworkFeeBps?: number | bigint; maxProtocolFeeBps?: number | bigint; }; type SwidgeOptions = { fromToken: string; toToken: string; toChain?: string | number; // defaults to the source chain (same-chain swap) recipient?: string; refundAddress?: string; slippage?: number; // decimal, e.g. 0.01 for 1% } & ( | { fromTokenAmount: number | bigint } // exact-in | { toTokenAmount: number | bigint } // exact-out ); type SwidgeStatus = | 'pending' | 'action-required' | 'completed' | 'failed' | 'refund-pending' | 'refunded' | 'cancelled' | 'expired' | 'partial'; ``` *** ## Next Steps Get started with WDK's configuration Get started with WDK's Usage Explore blockchain-specific wallet modules Cross-chain USD₮0 bridges *** ### Need Help? *** ## WDK Core Configuration URL: https://docs.wdk.tether.io/sdk/core-module/configuration Description: Configuration options and settings for @tetherto/wdk # Configuration ## WDK Manager Configuration ```javascript title="Create WDK Instance" import WDK from '@tetherto/wdk' const wdk = new WDK(seedPhrase) ``` The WDK Manager itself only requires a seed phrase for initialization. Configuration is done through the registration of wallets and protocols. ## Wallet Registration Configuration ```javascript title="Register WDK Wallet" import WDK from '@tetherto/wdk' import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerTon from '@tetherto/wdk-wallet-ton' const wdk = new WDK(seedPhrase) .registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth.drpc.org' }) .registerWallet('ton', WalletManagerTon, { tonApiKey: 'YOUR_TON_API_KEY', tonApiEndpoint: 'https://tonapi.io' }) ``` ## Protocol Registration Configuration ```javascript title="Register WDK Protocol" import veloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' const wdk = new WDK(seedPhrase) .registerProtocol('ethereum', 'velora', veloraProtocolEvm, { apiKey: 'YOUR_velora_API_KEY' }) ``` ## Configuration Options ### Wallet Configuration Each wallet manager requires its own configuration object when registered. The configuration depends on the specific wallet module being used. #### EVM Wallet Configuration ```javascript title="Ethereum WDK Wallet Configuration" const ethereumWalletConfig = { provider: 'https://eth.drpc.org', // RPC endpoint // Additional EVM-specific configuration options } wdk.registerWallet('ethereum', WalletManagerEvm, ethereumWalletConfig) ``` #### TON Wallet Configuration ```javascript title="TON WDK Wallet Configuration" const tonWalletConfig = { tonClient: { secretKey: 'YOUR_TON_API_KEY', url: 'https://toncenter.com/api/v2/jsonRPC' } } wdk.registerWallet('ton', WalletManagerTon, tonWalletConfig) ``` ### Protocol Configuration Protocols also require their own configuration objects when registered. #### Swap Protocol Configuration ```javascript title="Swap WDK Protocol Configuration" const veloraProtocolConfig = { apiKey: 'YOUR_velora_API_KEY', baseUrl: 'https://apiv5.velora.io' } wdk.registerProtocol('ethereum', 'velora', veloraProtocolEvm, veloraProtocolConfig) ``` ### Middleware Configuration Middleware functions can be registered to enhance account functionality. ```javascript title="Middleware WDK Protocol Configuration" // Simple logging middleware wdk.registerMiddleware('ethereum', async (account) => { console.log('New account created:', await account.getAddress()) }) ``` ## Environment Variables For production applications, consider using environment variables for sensitive configuration: ```javascript title="WDK environment variables Configuration" const wdk = new WDK(process.env.SEED_PHRASE) .registerWallet('ethereum', WalletManagerEvm, { provider: process.env.ETHEREUM_RPC_URL }) .registerProtocol('ethereum', 'velora', veloraProtocolEvm, { apiKey: process.env.velora_API_KEY }) ``` ## Configuration Validation The WDK Manager will validate configurations when wallets and protocols are registered: - **Wallet Registration**: Ensures the wallet class extends the required base class - **Protocol Registration**: Validates that protocol labels are unique per blockchain and protocol type - **Middleware Registration**: Validates that middleware functions have the correct signature ## Error Handling Configuration errors will be thrown during registration: ```javascript title="Configuration errors" try { wdk.registerWallet('ethereum', InvalidWalletClass, config) } catch (error) { console.error('Wallet registration failed:', error.message) } try { wdk.registerProtocol('ethereum', 'velora', veloraProtocolEvm, invalidConfig) } catch (error) { console.error('Protocol registration failed:', error.message) } ``` *** ## Next Steps Get started with WDK's usage Get started with WDK's API Explore blockchain-specific wallet modules Cross-chain USD₮0 bridges *** ### Need Help? *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/core-module/guides/account-management Description: Learn how to work with accounts and addresses. This guide explains how to access accounts from your registered wallets. An "Account" object in WDK is your interface for inspecting balances and sending transactions on a specific blockchain. ## Retrieve Accounts You can retrieve an account using a simple index or a custom derivation path. ### By Index (Recommended) The simplest way to get an account is by its index (starting at `0`). This uses the default derivation path for the specified blockchain. ```typescript title="Get Account by Index" // Get the first account (index 0) for Ethereum and TON const ethAccount = await wdk.getAccount('ethereum', 0) const tonAccount = await wdk.getAccount('ton', 0) ``` ### By Derivation Path (Advanced) If you need a specific hierarchy, you can request an account by its unique derivation path. ```typescript title="Get Account by Path" // Custom path for Ethereum const customEthAccount = await wdk.getAccountByPath('ethereum', "0'/0/1") ``` The WDK instance caches accounts. If you call `getAccount` twice using the same index, the function will return the same `Account` object instance. **Network Mismatch Warning** Ensure your WDK instance configuration matches your account environment. * If using **Testnet** keys, ensure you registered the wallet with a **Testnet RPC** (e.g., `https://sepolia.drpc.org` for ETH, `https://testnet.toncenter.com/api/v2/jsonRPC` for TON). * If using **Mainnet** keys, ensure you registered the wallet with a **Mainnet RPC** (e.g., `https://eth.drpc.org` for ETH, `https://toncenter.com/api/v2/jsonRPC` for TON). Using a Mainnet key on a Testnet RPC (or vice versa) will result in "Network not allowed" or zero balance errors. ## View Addresses Once you have an account object, you can retrieve its public blockchain address using the `getAddress` function. ```typescript title="Get Addresses" const ethAddress = await ethAccount.getAddress() console.log('Ethereum address:', ethAddress) ``` ## Check Balances You can check the native token balance of any account (e.g., ETH on Ethereum, TON on TON) by using the `getBalance()` function. ```typescript title="Get Balance" try { const balance = await ethAccount.getBalance() console.log('Balance:', balance) } catch (error) { console.error('Failed to fetch balance:', error) } ``` ### Multi-Chain Balance Check Because WDK offers a unified interface, you can easily iterate through multiple chains to fetch balances. The following example: 1. Iterates over an array of user defined chains. 2. Retrieves the first account using the respective chain's `getAccount(index)` function. 3. Retrieves the first account's balance using the `getBalance()` function. 4. Logs the balance to the console. ```typescript title="Check All Balances" const chains = ['ethereum', 'ton', 'bitcoin'] for (const chain of chains) { try { const account = await wdk.getAccount(chain, 0) const balance = await account.getBalance() console.log(`${chain} balance:`, balance) } catch (error) { console.log(`${chain}: Wallet not registered or unavailable`) } } ``` ## Next Steps Now that you can access your accounts, learn how to [send transactions](/sdk/core-module/guides/transactions). *** ## Error Handling URL: https://docs.wdk.tether.io/sdk/core-module/guides/error-handling Description: Learn about common errors and best practices. # Error Handling & Best Practices This guide covers recommended patterns for error handling and security when using the WDK. ## Handling Common Errors When interacting with multiple chains and protocols, various runtime issues may occur. ### Missing Registration The most common error is attempting to access a wallet or protocol that hasn't been registered. ```typescript title="Check Registration Pattern" try { // This will throw if 'tron' was never registered via .registerWallet() const tronAccount = await wdk.getAccount('tron', 0) } catch (error) { console.error('Tron wallet not available:', error.message) } ``` Always use `try/catch` blocks when initializing sessions or accessing dynamic features. ## Memory Management For security, clear sensitive data from memory when a session is complete. The WDK provides [`dispose()`](/sdk/core-module/api-reference) for this purpose. ### Disposing the Instance You can clear every registered wallet using [`dispose()`](/sdk/core-module/api-reference): ```typescript title="Dispose WDK" function endSession(wdk) { // 1. Clean up sensitive data wdk.dispose() // 2. Modify app state to reflect logged-out status // ... console.log('Session ended, wallet data cleared.') } ``` ### Disposing Specific Wallets You can dispose only the wallets you no longer need using [`dispose()`](/sdk/core-module/api-reference): ```typescript title="Dispose Specific Wallets" // Keep the TON wallet registered, but dispose the Ethereum wallet wdk.dispose(['ethereum']) ``` **After Disposal:** Once a wallet is disposed, any later call that depends on that wallet registration will fail until you register it again. If you call `wdk.dispose()` without arguments, you must instantiate a new WDK instance or register fresh wallets before resuming operations. ## Security Best Practices ### Environment Variables Never hardcode API keys or seed phrases in your source code. Use environment variables (e.g., `process.env.TON_API_KEY`). ### Secure Storage If you persist a session, never store the raw seed phrase in local storage. Use secure operating system storage (like Keychain on macOS or Keystore on Android). *** ## Getting Started URL: https://docs.wdk.tether.io/sdk/core-module/guides/getting-started Description: Install and instantiate the WDK Core module. This guide explains how to install the [`@tetherto/wdk`](https://www.npmjs.com/package/@tetherto/wdk) package and create a new instance to start managing your wallets. ## 1. Installation ### Prerequisites Before you begin, ensure you have the following installed: * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ### Install Package To install the WDK Core package, run the following command in your terminal: ```bash npm install @tetherto/wdk ``` This package allows you to manage different blockchain wallets and protocols through a single interface. ## 2. Instantiation To use WDK, you must create an instance of the `WDK` class. This instance acts as the central manager for all your wallets and protocols. ### Import the Module First, import the `WDK` class from the package: ```typescript title="Import WDK Core" import WDK from '@tetherto/wdk' ``` ### Initialize WDK You can initialize `WDK` in two ways: with a [new seed phrase](#generate-a-new-wallet) or an [existing one](#restore-an-existing-wallet). #### Generate a New Wallet If you are creating a fresh wallet for a user, use the static `getRandomSeedPhrase()` method to generate a secure mnemonic. ```typescript title="Create new WDK Instance" // 1. Generate a secure random seed phrase // Generate 24-word seed phrase for higher security const seedPhrase = WDK.getRandomSeedPhrase(24) // Or use 12-word seed phrase (default) // const seedPhrase = WDK.getRandomSeedPhrase() // 2. Initialize the WDK instance with the new seed const wdk = new WDK(seedPhrase) ``` **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. #### Restore an Existing Wallet If a user already has a seed phrase (e.g., from a previous session or another wallet), you can pass it directly to the constructor. ```typescript title="Restore WDK Instance" // Replace this string with the user's actual seed phrase const existingSeed = 'witch collapse practice feed shame open despair creek road again ice ...' const wdk = new WDK(existingSeed) ``` ## Next Steps With your WDK instance ready, you can now [register wallet modules](/sdk/core-module/guides/wallet-registration) to interact with specific blockchains like [Ethereum](/sdk/wallet-modules/wallet-evm/), [TON](/sdk/wallet-modules/wallet-ton/), or [Bitcoin](/sdk/wallet-modules/wallet-btc/). *** ## Configure Middleware URL: https://docs.wdk.tether.io/sdk/core-module/guides/middleware Description: Learn how to intercept and enhance wallet operations with middleware. Middleware allows you to intercept wallet operations. You can use this to add [logging](#logging), implement retry logic, or handle [failovers for RPC providers](#failover-protection-with-provider-failover). ## Register Middleware When registering middleware, you should reference a specific chain. The middleware function runs every time an account is instantiated or an operation is performed, depending on the implementation. ### Logging This simple middleware logs a message whenever a new account is accessed. ```typescript title="Logging Middleware" wdk.registerMiddleware('ethereum', async (account) => { const address = await account.getAddress() console.log('Accessed Ethereum account:', address) // You can also attach custom properties or wrap methods here }) ``` ## Failover Protection with Provider Failover The [`@tetherto/wdk-provider-failover`](https://www.npmjs.com/package/@tetherto/wdk-provider-failover) package provides a resilient wrapper for wallet instances. Unlike standard middleware, you wrap your wallet class instantiation directly. ### Install `@tetherto/wdk-provider-failover` You can install the `@tetherto/wdk-provider-failover` using npm with the following command: ```bash npm install @tetherto/wdk-provider-failover ``` ### Use `createFallbackWallet` You can import the `createFallbackWallet` function to ensure that if your primary RPC fails, the wallet automatically retries with the fallback providers. With this configuration, if `sendTransaction` fails due to a network error, the WDK will automatically retry using the fallback providers without throwing an error to your application. ```typescript title="Failover Wrapper Usage" import { createFallbackWallet } from '@tetherto/wdk-provider-failover' import { WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm' const wallet = createFallbackWallet( WalletAccountReadOnlyEvm, ['0x...'], // constructor args { primary: { provider: 'https://mainnet.infura.io/v3/YOUR_KEY' }, fallbacks: [ { provider: 'https://eth.llamarpc.com' }, { provider: 'https://ethereum.publicnode.com' } ] } ) // Use the wallet instance directly const balance = await wallet.getBalance() ``` ## Next Steps Learn about [error handling and best practices](/sdk/core-module/guides/error-handling) to ensure your application is robust and secure. *** ## Integrate Protocols URL: https://docs.wdk.tether.io/sdk/core-module/guides/protocol-integration Description: Learn how to use Swidge, Swap, Bridge, and Lending protocols. The WDK Core module supports registering external protocols. This allows you to extend the basic wallet functionality with advanced features like [swidge routes](/sdk/swidge-modules), [token swapping](#swapping-tokens), [cross-chain bridging](#bridging-assets), and lending. ## Register Protocols You can register protocols globally (for all new accounts). ### Global Registration (Recommended) Global registration ensures that every account you retrieve already has the protocol ready to use. You can do this by chaining a call to `.registerProtocol()` on the WDK instance. ### 1. Install Protocol Modules Install the [`@tetherto/wdk-protocol-swap-velora-evm`](https://www.npmjs.com/package/@tetherto/wdk-protocol-swap-velora-evm) and [`@tetherto/wdk-protocol-bridge-usdt0-evm`](https://www.npmjs.com/package/@tetherto/wdk-protocol-bridge-usdt0-evm) packages: ```bash npm install @tetherto/wdk-protocol-swap-velora-evm && npm install @tetherto/wdk-protocol-bridge-usdt0-evm ``` ### 2. Register in Code Now, import the protocol modules and register them with your WDK instance. This makes the protocol methods available to any account derived from that instance. First, import the necessary modules: ```typescript title="Import Protocols" import veloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' import usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm' ``` Then, register the protocols for the specific chains they support: ```typescript title="Register Protocols" // Register protocols for specific chains const wdk = new WDK(seedPhrase) .registerWallet('ethereum', WalletManagerEvm, ethConfig) // Register Velora Swap for Ethereum .registerProtocol('ethereum', 'velora', veloraProtocolEvm, { apiKey: 'YOUR_API_KEY' }) // Register USDT0 Bridge for Ethereum .registerProtocol('ethereum', 'usdt0', usdt0ProtocolEvm, { ethereumRpcUrl: 'https://eth.drpc.org' // Configuration depends on the module }) ``` ## Use Protocols Once [registered](#register-protocols), you can access the protocol instance using the specific getter methods, such as `getSwapProtocol`, `getBridgeProtocol`, `getLendingProtocol`, or `getFiatProtocol`. ### Swidge Routes Use a swidge provider module for new swap, bridge, or combined route integrations. The shared swidge interface discovers supported chains and tokens with `getSupportedChains()` and `getSupportedTokens()`, quotes with `quoteSwidge()`, executes with `swidge()`, and tracks asynchronous settlement with `getSwidgeStatus()`. The provider can decide whether the route is fulfilled as a same-chain swap, same-token bridge, combined route, intent, solver route, or aggregator route. The example below assumes `swidge` is an instance of a concrete provider module that implements the shared interface. ```typescript title="Swidge route flow" const chains = await swidge.getSupportedChains() const tokens = await swidge.getSupportedTokens({ fromChain: 'ethereum', toChain: 'arbitrum' }) const options = { fromToken: '0xSourceToken...', toToken: '0xDestinationToken...', toChain: 'arbitrum', recipient: '0xRecipient...', fromTokenAmount: 1000000n, slippage: 0.01 } const quote = await swidge.quoteSwidge(options) const result = await swidge.swidge(options, { maxNetworkFeeBps: 50, maxProtocolFeeBps: 25 }) const status = await swidge.getSwidgeStatus(result.id, { toChain: 'arbitrum' }) ``` Use discovery results to build token and chain selectors, but continue to show the quote details before execution. `swidge()` is the write step in the shared swidge flow. Existing swap and bridge modules keep their current accessors for released modules. Prefer the [swidge protocol interface](/sdk/swidge-modules) for new protocol integrations because the standalone swap and bridge interfaces are expected to be deprecated after swidge provider coverage is available. ### Swapping Tokens Use `getSwapProtocol` to access registered swap services on any wallet account. ```typescript title="Swap Tokens" const ethAccount = await wdk.getAccount('ethereum', 0) const velora = ethAccount.getSwapProtocol('velora') const result = await velora.swap({ tokenIn: '0x...', // Address of token to sell tokenOut: '0x...', // Address of token to buy tokenInAmount: 1000000n // Amount to swap }) ``` ### Bridging Assets 1. Use `getBridgeProtocol` to access cross-chain bridges. 2. Approve the source-chain bridge spender for the token and amount. 3. Call `bridge` from the bridge protocol to send tokens from one protocol to another. ```typescript title="Bridge Assets" const ethAccount = await wdk.getAccount('ethereum', 0) const usdt0 = ethAccount.getBridgeProtocol('usdt0') await ethAccount.approve({ token: '0x...', // ERC20 Token Address spender: '0x...', // OFT or bridge spender address amount: 1000000n }) const result = await usdt0.bridge({ targetChain: 'ton', recipient: 'UQBla...', // TON address token: '0x...', // ERC20 Token Address amount: 1000000n, oftContractAddress: '0x...' // Same address used as approval spender }) ``` **Protocol Availability:** If you try to access a protocol that hasn't been registered (e.g., `getSwapProtocol('uniswap')`), the SDK will throw an error. always ensure registration matches the ID you request. ## Next Steps Learn how to [configure middleware](/sdk/core-module/guides/middleware) to add logging or failover protection to your wallet interactions. *** ## Send Transactions URL: https://docs.wdk.tether.io/sdk/core-module/guides/transactions Description: Learn how to send native tokens on different blockchains. You can [send native tokens](#send-native-tokens), [sign a transaction without broadcasting it](#sign-without-broadcasting), [handle transaction responses](#handling-responses), and [orchestrate multi-chain payments](#multi-chain-transactions) from WDK wallet accounts. **Get Testnet Funds:** To test these transactions without spending real money, ensure you are on a testnet and have obtained funds. See [Testnet Funds & Faucets](/resources/concepts#testnet-funds--faucets) for a list of available faucets. **BigInt Usage:** Always use `BigInt` (the `n` suffix) for monetary values to avoid precision loss with large numbers. ## Send Native Tokens The `sendTransaction` method allows you to transfer value. It accepts a unified configuration object, though specific parameters (like `value` formatting) may vary slightly depending on the blockchain. ### Ethereum Example On EVM chains, values are typically expressed in Wei (1 ETH = 10^18 Wei). The following example will: 1. Retrieve the first Ethereum account (see [Manage Accounts](/sdk/core-module/guides/account-management)) 2. Send 0.001 ETH (1000000000000000 wei) to an account using `sendTransaction`. ```typescript title="Send ETH" const ethAccount = await wdk.getAccount('ethereum', 0) const result = await ethAccount.sendTransaction({ to: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', value: 1000000000000000n // 0.001 ETH (in Wei) }) console.log('Transaction sent! Hash:', result.hash) ``` ### TON Example On TON, values are expressed in Nanotons (1 TON = 10^9 Nanotons). The following example will: 1. Retrieve the first TON account 2. Send 1 TON (1000000000 nton) to an account using `sendTransaction`. ```typescript title="Send TON" // Send TON transaction const tonAccount = await wdk.getAccount('ton', 0) const tonResult = await tonAccount.sendTransaction({ to: 'UQCz5ON7jjK32HnqPushubsHxgsXgeSZDZPvh8P__oqol90r', value: 1000000000n // 1 TON (in nanotons) }) console.log('TON transaction:', tonResult.hash) ``` ## Sign Without Broadcasting Use [`account.signTransaction()`](/sdk/core-module/api-reference#signtransactiontx) when your app needs a signed transaction payload but does not want WDK to broadcast it immediately. Wallet modules accept their own transaction shape and may return a module-specific signed payload. ```typescript title="Sign An EVM Transaction" const ethAccount = await wdk.getAccount('ethereum', 0) const signedTransaction = await ethAccount.signTransaction({ to: '0x71C7656EC7ab88b098defB751B7401B5f6d8976F', value: 1000000000000000n }) console.log('Signed transaction:', signedTransaction) ``` `signTransaction()` only signs. Use `sendTransaction()` when you want WDK to sign, broadcast, and return the transaction hash. ## Handling Responses The `sendTransaction` method returns a [transaction result object](/sdk/core-module/api-reference). The most important field is typically `hash`, which represents the transaction ID on the blockchain. You can use this hash to track the status of your payment on a block explorer. ## Multi-Chain Transactions You can orchestrate payments across different chains in a single function by acting on multiple account objects sequentially. The following example will: 1. Retrieve an ETH and ton account using the `getAccount()` method. 2. Send ETH and `await` the transaction. 3. Send TON and `await` the transaction. ```typescript title="Multi-Chain Payment" async function sendCrossChainPayments(wdk) { const ethAccount = await wdk.getAccount('ethereum', 0) const tonAccount = await wdk.getAccount('ton', 0) // 1. Send ETH await ethAccount.sendTransaction({ to: '0x...', value: 1000000000000000000n }) // 2. Send TON await tonAccount.sendTransaction({ to: 'EQ...', value: 1000000000n }) } ``` ## Next Steps For more complex interactions like swapping tokens or bridging assets, learn how to [integrate protocols](/sdk/core-module/guides/protocol-integration). *** ## Register Wallets URL: https://docs.wdk.tether.io/sdk/core-module/guides/wallet-registration Description: Learn how to register wallet modules for different blockchains. This guide explains how to register wallet modules with your WDK instance. The WDK Core module itself doesn't contain blockchain-specific logic; instead, you register separate modules for each chain you want to support (e.g., Ethereum, TON, Bitcoin). ## How it works The WDK uses a builder pattern, allowing you to chain `.registerWallet()` calls. Each call connects a blockchain-specific manager to your central WDK instance. ### Parameters The `registerWallet` method (see [API Reference](/sdk/core-module/api-reference)) requires three arguments: 1. **Symbol**: A unique string identifier for the chain (e.g., `'ethereum'`, `'ton'`). You will use this ID later to retrieve accounts. 2. **Manager Class**: The wallet manager class imported from the specific module (e.g., `WalletManagerEvm`). 3. **Configuration**: An object containing the chain-specific settings (e.g., RPC providers, API keys). ## Installation Install the [wallet managers](/sdk/wallet-modules/) for the blockchains you want to support: ```bash npm install @tetherto/wdk-wallet-evm @tetherto/wdk-wallet-tron @tetherto/wdk-wallet-btc ``` ## Example: Registering Multiple Wallets ### Import the Wallet Manager Packages First, import the necessary wallet manager packages: ```typescript title="Import Modules" import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerTron from '@tetherto/wdk-wallet-tron' import WalletManagerBtc from '@tetherto/wdk-wallet-btc' ``` ### Register the Wallets Then, [instantiate WDK](/sdk/core-module/guides/getting-started#initialize-wdk) and chain the registration calls: ```typescript title="Register Wallets" const wdk = new WDK(seedPhrase) // 1. Register Ethereum .registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth.drpc.org' }) // 2. Register TRON .registerWallet('tron', WalletManagerTron, { provider: 'https://api.trongrid.io' }) // 3. Register Bitcoin .registerWallet('bitcoin', WalletManagerBtc, { provider: 'https://blockstream.info/api' }) ``` **RPC Providers:** The examples use public RPC endpoints for demonstration. We do not endorse any specific provider. * **Testnets:** You can find public RPCs for Ethereum and other EVM chains on [Chainlist](https://chainlist.org). * **Mainnet:** For production environments, we recommend using reliable, paid RPC providers to ensure stability. **TRON Networks:** Choose the correct provider for your environment. * **Mainnet:** `https://api.trongrid.io` * **Shasta (Testnet):** `https://api.shasta.trongrid.io` ## Next Steps Once your wallets are registered, you can [manage accounts and specific addresses](/sdk/core-module/guides/account-management). *** ## Usage URL: https://docs.wdk.tether.io/sdk/core-module/usage Description: Guide to using the WDK Core module. The WDK Core module is the central orchestrator for your wallet interactions. Install and instantiate the WDK. Connect specific blockchains (Ethereum, TON, etc.). Retrieve accounts and check balances. Transfer native tokens. Use Swidge, Swap, Bridge, and Lending protocols. Add logging and failover protection. Best practices for security and stability. *** ## Fiat Modules Overview URL: https://docs.wdk.tether.io/sdk/fiat-modules Description: Explore WDK fiat modules for on-ramp and off-ramp integrations. The Wallet Development Kit (WDK) provides fiat modules that enable on-ramp and off-ramp functionality, allowing users to seamlessly convert between fiat currencies and cryptocurrencies within your application. ## Fiat Protocol Modules On-ramp and off-ramp functionality for fiat currency integration: | Module | Provider | Status | Documentation | |--------|----------|--------|---------------| | [`@tetherto/wdk-protocol-fiat-moonpay`](https://github.com/tetherto/wdk-protocol-fiat-moonpay) | MoonPay | ✅ Ready | [Documentation](/sdk/fiat-modules/fiat-moonpay/) | ## Features Fiat modules provide: - **On-Ramp**: Allow users to purchase cryptocurrency using fiat currencies (credit card, bank transfer, etc.) - **Off-Ramp**: Enable users to sell cryptocurrency and receive fiat currencies - **Multiple Payment Methods**: Support for various payment options depending on the provider - **KYC Integration**: Built-in Know Your Customer verification flows - **Multi-Currency Support**: Support for multiple fiat and cryptocurrencies ## Next Steps To get started with WDK fiat modules, follow these steps: 1. Get up and running quickly with our [Quickstart Guide](/start-building/nodejs-bare-quickstart) 2. Choose the fiat module that best fits your needs from the table above 3. Check specific documentation for the module you wish to use You can also: - Learn about key concepts in our [Concepts](/resources/concepts) page - Explore [wallet modules](/sdk/wallet-modules/) to manage user wallets - Check our [examples](/examples-and-starters/react-native-starter) for production-ready implementations *** ## Fiat MoonPay Overview URL: https://docs.wdk.tether.io/sdk/fiat-modules/fiat-moonpay Description: Overview of the @tetherto/wdk-protocol-fiat-moonpay module # @tetherto/wdk-protocol-fiat-moonpay Overview A WDK module for integrating MoonPay's fiat on-ramp and off-ramp services. This module generates signed or unsigned widget URLs that allow users to buy and sell cryptocurrency using fiat currencies directly within your application. Provide a `signUrl` callback if you want the protocol to return signed URLs from a trusted backend, or omit it to use the unsigned widget URLs directly. Get started by reading the [Usage](/sdk/fiat-modules/fiat-moonpay/usage) guide. This module requires a MoonPay developer account. [Create your account here](https://dashboard.moonpay.com/signup). If you want MoonPay to sign the widget URLs before they are returned, provide a `signUrl` callback that talks to a trusted backend signer. If you omit `signUrl`, the protocol returns unsigned widget URLs directly. ## Features - **Fiat On-Ramp**: Generate signed or unsigned widget URLs for users to buy cryptocurrency with fiat - **Fiat Off-Ramp**: Generate signed or unsigned widget URLs for users to sell cryptocurrency with fiat - **Price Quotes**: Get real-time quotes for buy and sell operations - **Transaction Tracking**: Retrieve transaction status and details - **Currency Support**: Query supported cryptocurrencies, fiat currencies, and countries - **Customizable Widget**: Configure colors, themes, language, and behavior ## Supported Payment Methods - Credit and debit cards (Visa, Mastercard, etc.) - Bank transfers (ACH, SEPA, etc.) - Apple Pay and Google Pay - Local payment methods (varies by region) For the full list of supported payment methods by country, see [MoonPay's Supported Payment Methods](https://support.moonpay.com/en/articles/380823-moonpay-s-supported-payment-methods). ## Supported Cryptocurrencies This module supports purchasing and selling cryptocurrencies on networks compatible with WDK wallet modules, including: - Ethereum and EVM-compatible chains (ETH, USD₮, etc.) - Bitcoin (BTC) - TRON (TRX, USD₮) - TON - Solana (SOL, USD₮) ## Next Steps Set up your MoonPay API key, optional signing callback, and environment Learn how to integrate MoonPay in your application Complete API documentation for the module --- ### MoonPay Resources - [MoonPay Dashboard](https://dashboard.moonpay.com/signup) - Create your developer account - [MoonPay Support Center](https://support.moonpay.com/) - Official MoonPay documentation and support - [Supported Payment Methods](https://support.moonpay.com/en/articles/380823-moonpay-s-supported-payment-methods) - Full list by country --- ### Need Help? *** ## Fiat MoonPay API Reference URL: https://docs.wdk.tether.io/sdk/fiat-modules/fiat-moonpay/api-reference Description: API Reference for the @tetherto/wdk-protocol-fiat-moonpay module # API Reference Complete API documentation for the `@tetherto/wdk-protocol-fiat-moonpay` module. ## Constructor ### `new MoonPayProtocol(account, config)` Creates a new MoonPayProtocol instance. **Parameters:** | Name | Type | Description | |------|------|-------------| | `account` | `IWalletAccount` \| `IWalletAccountReadOnly` \| `undefined` | Wallet account for transactions | | `config` | `MoonPayProtocolConfig` | Configuration object | **Config Options:** | Name | Type | Required | Default | Description | |------|------|----------|---------|-------------| | `apiKey` | string | Yes | - | Your MoonPay publishable API key | | `signUrl` | function | No | - | Callback used to sign buy and sell widget URLs through a trusted backend | | `cacheTime` | number | No | `600000` | Cache duration for currencies (ms) | | `environment` | `'production' \| 'sandbox'` | No | `production` | MoonPay widget URL endpoint set | **Example:** ```typescript import MoonPayProtocol from '@tetherto/wdk-protocol-fiat-moonpay'; const moonpay = new MoonPayProtocol(walletAccount, { apiKey: 'pk_live_xxxxx', signUrl: async (urlForSignature) => urlForSignature, environment: 'production', }); ``` --- ## Methods ### `buy(options)` Generates a MoonPay widget URL for purchasing cryptocurrency. If `signUrl` is configured, the URL is signed through that callback before being returned. **Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `options.cryptoAsset` | string | Yes | Cryptocurrency code (e.g., 'eth', 'btc') | | `options.fiatCurrency` | string | Yes | Fiat currency code (e.g., 'usd', 'eur') | | `options.cryptoAmount` | number \| bigint | No* | Amount in smallest crypto units | | `options.fiatAmount` | number \| bigint | No* | Amount in smallest fiat units (cents) | | `options.recipient` | string | No | Wallet address (uses account address if not provided) | | `options.config` | MoonPayBuyParams | No | Widget configuration options | *Either `cryptoAmount` or `fiatAmount` must be provided, but not both. **Returns:** `Promise\<{ buyUrl: string }\>` --- ### `sell(options)` Generates a MoonPay widget URL for selling cryptocurrency. If `signUrl` is configured, the URL is signed through that callback before being returned. **Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `options.cryptoAsset` | string | Yes | Cryptocurrency code | | `options.fiatCurrency` | string | Yes | Fiat currency code | | `options.cryptoAmount` | number \| bigint | No* | Amount in smallest crypto units | | `options.fiatAmount` | number \| bigint | No* | Amount in smallest fiat units | | `options.refundAddress` | string | No | Refund wallet address | | `options.config` | MoonPaySellParams | No | Widget configuration options | **Returns:** `Promise\<{ sellUrl: string }\>` --- ### `quoteBuy(options)` Gets a price quote for a cryptocurrency purchase. **Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `options.cryptoAsset` | string | Yes | Cryptocurrency code | | `options.fiatCurrency` | string | Yes | Fiat currency code | | `options.cryptoAmount` | number \| bigint | No* | Amount in smallest crypto units | | `options.fiatAmount` | number \| bigint | No* | Amount in smallest fiat units | | `options.config` | MoonPayQuoteBuyParams | No | Quote parameters | **Returns:** `Promise\` ```typescript { cryptoAmount: bigint, // Crypto amount you'll receive fiatAmount: bigint, // Fiat amount to pay fee: bigint, // Total fee amount rate: string, // Exchange rate metadata: MoonPayBuyQuoteMetadata } ``` --- ### `quoteSell(options)` Gets a price quote for selling cryptocurrency. **Parameters:** | Name | Type | Required | Description | |------|------|----------|-------------| | `options.cryptoAsset` | string | Yes | Cryptocurrency code | | `options.fiatCurrency` | string | Yes | Fiat currency code | | `options.cryptoAmount` | number \| bigint | Yes | Amount in smallest crypto units | | `options.config` | MoonPayQuoteSellParams | No | Quote parameters | **Returns:** `Promise\` ```typescript { cryptoAmount: bigint, // Crypto amount to sell fiatAmount: bigint, // Fiat amount you'll receive fee: bigint, // Total fee amount rate: string, // Exchange rate metadata: MoonPaySellQuoteMetadata } ``` --- ### `getSupportedCryptoAssets()` Fetches the list of supported cryptocurrencies. Results are cached. **Returns:** `Promise\` ```typescript { code: string, // Currency code (e.g., 'eth') decimals: number, // Decimal places networkCode: string, // Network identifier name: string, // Display name metadata: MoonPayCryptoCurrencyDetails } ``` --- ### `getSupportedFiatCurrencies()` Fetches the list of supported fiat currencies. Results are cached. **Returns:** `Promise\` ```typescript { code: string, // Currency code (e.g., 'usd') decimals: number, // Decimal places name: string, // Display name metadata: MoonPayFiatCurrencyDetails } ``` --- ### `getSupportedCountries()` Fetches the list of supported countries. **Returns:** `Promise\` ```typescript { code: string, // ISO country code name: string, // Country name isBuyAllowed: boolean, // Buy operations allowed isSellAllowed: boolean,// Sell operations allowed metadata: MoonPayCountryDetail } ``` --- ### `getTransactionDetail(txId, direction?)` Retrieves details of a specific transaction. **Parameters:** | Name | Type | Required | Default | Description | |------|------|----------|---------|-------------| | `txId` | string | Yes | - | MoonPay transaction ID | | `direction` | `'buy' \| 'sell'` | No | `'buy'` | Transaction type | **Returns:** `Promise\` ```typescript { status: 'completed' | 'failed' | 'in_progress', cryptoAsset: string, fiatCurrency: string, metadata: MoonPayBuyTransaction | MoonPaySellTransaction } ``` --- ## Types ### `MoonPayProtocolConfig` ```typescript interface MoonPayProtocolConfig { apiKey: string; signUrl?: (urlForSignature: string) => Promise; cacheTime?: number; environment?: 'production' | 'sandbox'; } ``` ### `MoonPayBuyParams` Widget configuration options for `buy()` operations: ```typescript interface MoonPayBuyParams { // UI options (shared with MoonPaySellParams) colorCode?: string; theme?: 'dark' | 'light'; themeId?: string; language?: string; showAllCurrencies?: boolean; showOnlyCurrencies?: string; showWalletAddressForm?: boolean; redirectURL?: string; unsupportedRegionRedirectUrl?: string; skipUnsupportedRegionScreen?: boolean; // Buy-specific options defaultCurrencyCode?: string; walletAddress?: string; walletAddressTag?: string; walletAddresses?: string; walletAddressTags?: string; contractAddress?: string; networkCode?: string; lockAmount?: boolean; email?: string; externalTransactionId?: string; externalCustomerId?: string; paymentMethod?: string; } ``` ### `MoonPaySellParams` Widget configuration options for `sell()` operations: ```typescript interface MoonPaySellParams { // UI options (shared with MoonPayBuyParams) colorCode?: string; theme?: 'dark' | 'light'; themeId?: string; language?: string; showAllCurrencies?: boolean; showOnlyCurrencies?: string; showWalletAddressForm?: boolean; redirectURL?: string; unsupportedRegionRedirectUrl?: string; skipUnsupportedRegionScreen?: boolean; // Sell-specific options defaultBaseCurrencyCode?: string; refundWalletAddresses?: string; lockAmount?: boolean; email?: string; externalTransactionId?: string; externalCustomerId?: string; paymentMethod?: string; } ``` ### `MoonPayQuoteBuyParams` ```typescript interface MoonPayQuoteBuyParams { extraFeePercentage?: number; // 0-10% paymentMethod?: string; areFeesIncluded?: boolean; walletAddress?: string; } ``` ### `MoonPayQuoteSellParams` ```typescript interface MoonPayQuoteSellParams { extraFeePercentage?: number; // 0-10% payoutMethod?: string; } ``` --- ## Next Steps - [Configuration](/sdk/fiat-modules/fiat-moonpay/configuration) - Setup and configuration options - [Usage Guide](/sdk/fiat-modules/fiat-moonpay/usage) - Common usage patterns *** ## Fiat MoonPay Configuration URL: https://docs.wdk.tether.io/sdk/fiat-modules/fiat-moonpay/configuration Description: Configuration options for the @tetherto/wdk-protocol-fiat-moonpay module # Configuration This page covers all configuration options for the MoonPay fiat module, including optional URL signing and environment selection. ## Prerequisites Before using this module, you need: 1. A MoonPay developer account - [Create an account on MoonPay Dashboard](https://dashboard.moonpay.com/signup) 2. A publishable API key from your dashboard 3. If you want signed widget URLs, a trusted backend signing endpoint for the `signUrl` callback ## Installation ```bash npm install @tetherto/wdk-protocol-fiat-moonpay ``` ## Basic Configuration ```typescript import MoonPayProtocol from '@tetherto/wdk-protocol-fiat-moonpay'; const moonpay = new MoonPayProtocol(walletAccount, { apiKey: 'pk_live_xxxxx', // Your MoonPay publishable API key signUrl: async (urlForSignature) => { const response = await fetch('/api/moonpay/sign-url', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ urlForSignature }), }); if (!response.ok) { throw new Error(`Failed to sign MoonPay URL: ${response.status} ${response.statusText}`); } const { signedUrl } = await response.json(); return signedUrl; }, environment: 'sandbox', }); ``` ## Configuration Options | Option | Type | Required | Default | Description | |--------|------|----------|---------|-------------| | `apiKey` | string | Yes | - | Your MoonPay publishable API key | | `signUrl` | function | No | - | Callback used to sign buy and sell widget URLs through a trusted backend | | `cacheTime` | number | No | `600000` (10 min) | Duration in milliseconds to cache supported currencies | | `environment` | `'production' \| 'sandbox'` | No | `production` | MoonPay widget URL endpoint set | ## Constructor Overloads The `MoonPayProtocol` class supports three constructor patterns: ```typescript // Without account (for public read operations like fetching supported currencies) const moonpay = new MoonPayProtocol(undefined, config); // With read-only account const moonpay = new MoonPayProtocol(readOnlyAccount, config); // With full wallet account (for buy/sell operations) const moonpay = new MoonPayProtocol(walletAccount, config); ``` ## Environment Configuration ### Sandbox (Testing) Use sandbox endpoints for development and testing: ```typescript const moonpay = new MoonPayProtocol(walletAccount, { apiKey: 'pk_test_xxxxx', signUrl: async (urlForSignature) => { const response = await fetch('/api/moonpay/sign-url', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ urlForSignature }), }); return (await response.json()).signedUrl; }, environment: 'sandbox', }); ``` In sandbox mode: - No real transactions are processed - Use test card numbers provided by MoonPay - KYC verification is simulated If you do not need signed URLs, omit `signUrl` and the protocol returns unsigned widget URLs directly. ### Production For production deployments, use live API keys and the production endpoint set: ```typescript const moonpay = new MoonPayProtocol(walletAccount, { apiKey: 'pk_live_xxxxx', signUrl: async (urlForSignature) => { const response = await fetch('/api/moonpay/sign-url', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ urlForSignature }), }); return (await response.json()).signedUrl; }, environment: 'production', }); ``` ## Widget Customization When calling `buy()` or `sell()`, you can customize the MoonPay widget appearance: ```typescript const result = await moonpay.buy({ cryptoAsset: 'eth', fiatCurrency: 'usd', fiatAmount: 10000n, // $100.00 in cents config: { colorCode: '#3B82F6', // Your brand color (hex) theme: 'dark', // 'dark' or 'light' language: 'en', // ISO 639-1 language code redirectURL: 'https://yourapp.com/callback', }, }); ``` ### Available Buy Widget Options | Option | Type | Description | |--------|------|-------------| | `colorCode` | string | Hexadecimal color for widget accent | | `theme` | `'dark' \| 'light'` | Widget appearance theme | | `themeId` | string | ID of a custom theme | | `language` | string | ISO 639-1 language code | | `showAllCurrencies` | boolean | Show all supported cryptocurrencies | | `showOnlyCurrencies` | string | Comma-separated currency codes to display | | `showWalletAddressForm` | boolean | Show wallet address input form | | `redirectURL` | string | URL to redirect after completion | | `unsupportedRegionRedirectUrl` | string | URL for unsupported regions | | `skipUnsupportedRegionScreen` | boolean | Skip unsupported region screen | | `defaultCurrencyCode` | string | Pre-selected cryptocurrency code | | `walletAddress` | string | Pre-filled wallet address | | `walletAddressTag` | string | Wallet address memo/tag (for EOS, XRP, etc.) | | `walletAddresses` | string | JSON string of wallet addresses for multiple currencies | | `walletAddressTags` | string | JSON string of address tags for multiple currencies | | `contractAddress` | string | Token contract address (DeFi Buy only) | | `networkCode` | string | Network for the token contract (DeFi Buy only) | | `lockAmount` | boolean | Prevent user from changing amount | | `email` | string | Pre-fill customer email | | `externalTransactionId` | string | Your transaction identifier | | `externalCustomerId` | string | Your customer identifier | | `paymentMethod` | string | Pre-select payment method | ### Available Sell Widget Options For `sell()`, the widget config uses `MoonPaySellParams` with different options: | Option | Type | Description | |--------|------|-------------| | `colorCode` | string | Hexadecimal color for widget accent | | `theme` | `'dark'` \| `'light'` | Widget appearance theme | | `themeId` | string | ID of a custom theme | | `language` | string | ISO 639-1 language code | | `showAllCurrencies` | boolean | Show all supported cryptocurrencies | | `showOnlyCurrencies` | string | Comma-separated currency codes to display | | `showWalletAddressForm` | boolean | Show wallet address input form | | `redirectURL` | string | URL to redirect after completion | | `unsupportedRegionRedirectUrl` | string | URL for unsupported regions | | `skipUnsupportedRegionScreen` | boolean | Skip unsupported region screen | | `defaultBaseCurrencyCode` | string | Pre-selected cryptocurrency to sell | | `refundWalletAddresses` | string | JSON string of wallet addresses for refunds | | `lockAmount` | boolean | Prevent user from changing amount | | `email` | string | Pre-fill customer email | | `externalTransactionId` | string | Your transaction identifier | | `externalCustomerId` | string | Your customer identifier | | `paymentMethod` | string | Pre-select payout method | ## Next Steps - [Usage Guide](/sdk/fiat-modules/fiat-moonpay/usage) - Learn how to integrate MoonPay - [API Reference](/sdk/fiat-modules/fiat-moonpay/api-reference) - Complete API documentation *** ## Buy and Sell URL: https://docs.wdk.tether.io/sdk/fiat-modules/fiat-moonpay/guides/buy-and-sell Description: On-ramp, off-ramp, quotes, supported assets, widget options, and custom recipients. This guide explains [buying crypto (on-ramp)](#buy-crypto-on-ramp), [selling crypto (off-ramp)](#sell-crypto-off-ramp), [quotes](#get-price-quotes), [supported currencies](#supported-currencies-and-countries), [widget customization](#widget-customization), and [custom recipients](#custom-recipient-addresses). It assumes a [`MoonPayProtocol`](/sdk/fiat-modules/fiat-moonpay/api-reference) instance named `moonpay`. Amounts use smallest units: fiat in minor units (cents), crypto in on-chain base units (for example wei for ETH). ## Buy crypto (on-ramp) You can build a signed purchase URL with [`buy()`](/sdk/fiat-modules/fiat-moonpay/api-reference) when you know the fiat spend: ```typescript title="Buy with fiat amount" const result = await moonpay.buy({ cryptoAsset: 'usdt', fiatCurrency: 'usd', fiatAmount: 10000n }) window.open(result.buyUrl, '_blank') ``` You can request a fixed crypto amount instead by passing `cryptoAmount` to [`buy()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Buy with crypto amount" const result = await moonpay.buy({ cryptoAsset: 'eth', fiatCurrency: 'usd', cryptoAmount: 100000000000000000n }) window.open(result.buyUrl, '_blank') ``` ## Sell crypto (off-ramp) You can generate a sell widget URL with [`sell()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Sell ETH for USD" const result = await moonpay.sell({ cryptoAsset: 'eth', fiatCurrency: 'usd', cryptoAmount: 500000000000000000n }) window.open(result.sellUrl, '_blank') ``` ## Get price quotes You can preview economics before opening the widget using [`quoteBuy()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Buy quote" const buyQuote = await moonpay.quoteBuy({ cryptoAsset: 'eth', fiatCurrency: 'usd', fiatAmount: 10000n }) console.log('Crypto amount:', buyQuote.cryptoAmount) console.log('Fee:', buyQuote.fee) console.log('Exchange rate:', buyQuote.rate) ``` You can estimate proceeds for a sell with [`quoteSell()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Sell quote" const sellQuote = await moonpay.quoteSell({ cryptoAsset: 'eth', fiatCurrency: 'usd', cryptoAmount: 500000000000000000n }) console.log('Fiat amount:', sellQuote.fiatAmount) ``` ## Supported currencies and countries You can list tradable assets with [`getSupportedCryptoAssets()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Supported crypto" const cryptoAssets = await moonpay.getSupportedCryptoAssets() console.log(cryptoAssets) ``` You can list fiat currencies with [`getSupportedFiatCurrencies()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Supported fiat" const fiatCurrencies = await moonpay.getSupportedFiatCurrencies() console.log(fiatCurrencies) ``` You can check regional availability with [`getSupportedCountries()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Supported countries" const countries = await moonpay.getSupportedCountries() console.log(countries) ``` ## Widget customization You can pass UI options under `config` to [`buy()`](/sdk/fiat-modules/fiat-moonpay/api-reference) (see [`MoonPayBuyParams`](/sdk/fiat-modules/fiat-moonpay/api-reference)): ```typescript title="Themed buy widget" const result = await moonpay.buy({ cryptoAsset: 'usdt', fiatCurrency: 'eur', fiatAmount: 5000n, config: { colorCode: '#1f2937', theme: 'dark', language: 'de', redirectURL: 'https://yourapp.com/payment-complete', lockAmount: true, email: 'user@example.com', externalCustomerId: 'user_123' } }) window.open(result.buyUrl, '_blank') ``` ## Custom recipient addresses By default [`buy()`](/sdk/fiat-modules/fiat-moonpay/api-reference) credits the connected wallet. You can override the destination with `recipient`: ```typescript title="Custom buy recipient" const result = await moonpay.buy({ cryptoAsset: 'eth', fiatCurrency: 'usd', fiatAmount: 10000n, recipient: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' }) window.open(result.buyUrl, '_blank') ``` You can set a refund destination on sells with `refundAddress` on [`sell()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Custom sell refund address" const result = await moonpay.sell({ cryptoAsset: 'eth', fiatCurrency: 'usd', cryptoAmount: 500000000000000000n, refundAddress: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' }) window.open(result.sellUrl, '_blank') ``` ## Next Steps - [Manage transactions](manage-transactions) - [Get started](get-started) - [API reference](/sdk/fiat-modules/fiat-moonpay/api-reference) *** ## Get Started URL: https://docs.wdk.tether.io/sdk/fiat-modules/fiat-moonpay/guides/get-started Description: Install the package and initialize MoonPayProtocol with your wallet and keys. This guide covers [installation](#installation) and [initializing the protocol](#initialize-moonpayprotocol). You need [Node.js](https://nodejs.org/), [npm](https://www.npmjs.com/), and MoonPay API keys from your MoonPay dashboard. ## Installation Run the following to install [@tetherto/wdk-protocol-fiat-moonpay](https://www.npmjs.com/package/@tetherto/wdk-protocol-fiat-moonpay): ```bash title="Install with npm" npm install @tetherto/wdk-protocol-fiat-moonpay ``` ## Initialize MoonPayProtocol You can create a fiat ramp client with [`new MoonPayProtocol(account, config)`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Construct MoonPayProtocol" import MoonPayProtocol from '@tetherto/wdk-protocol-fiat-moonpay' const moonpay = new MoonPayProtocol(walletAccount, { apiKey: process.env.MOONPAY_PUBLISHABLE_KEY, secretKey: process.env.MOONPAY_SECRET_KEY }) ``` Never ship a secret key to browsers. Run server-side signing where your architecture allows, and rotate keys if they leak. See [Configuration](/sdk/fiat-modules/fiat-moonpay/configuration) for `cacheTime` and related options. ## Next Steps - [Buy and sell](buy-and-sell) - [Manage transactions](manage-transactions) *** ## Manage Transactions URL: https://docs.wdk.tether.io/sdk/fiat-modules/fiat-moonpay/guides/manage-transactions Description: Poll MoonPay for transaction status and inspect returned details. This guide shows how to [check transaction status](#check-transaction-status) and [read transaction details](#read-transaction-details) with [`getTransactionDetail()`](/sdk/fiat-modules/fiat-moonpay/api-reference). Pass the identifier MoonPay returns after checkout (for example from your redirect URL or webhook payload). ## Check transaction status You can read the high-level state of a buy with [`getTransactionDetail()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Buy transaction status" const buyTx = await moonpay.getTransactionDetail(moonpayTransactionId, 'buy') console.log('Status:', buyTx.status) ``` `status` is one of `completed`, `failed`, or `in_progress` as described in the API reference. ## Read transaction details You can load the same record to inspect assets and currencies using [`getTransactionDetail()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Buy transaction fields" const buyTx = await moonpay.getTransactionDetail(moonpayTransactionId, 'buy') console.log('Crypto asset:', buyTx.cryptoAsset) console.log('Fiat currency:', buyTx.fiatCurrency) console.log('Metadata:', buyTx.metadata) ``` You can query a sell the same way by passing `sell` as the direction to [`getTransactionDetail()`](/sdk/fiat-modules/fiat-moonpay/api-reference): ```typescript title="Sell transaction details" const sellTx = await moonpay.getTransactionDetail(moonpayTransactionId, 'sell') console.log('Status:', sellTx.status) console.log('Crypto asset:', sellTx.cryptoAsset) console.log('Fiat currency:', sellTx.fiatCurrency) ``` The second argument defaults to `buy` when omitted; set it explicitly for sell flows. ## Next Steps - [Buy and sell](buy-and-sell) - [Get started](get-started) - [Configuration](/sdk/fiat-modules/fiat-moonpay/configuration) *** ## Fiat MoonPay Usage URL: https://docs.wdk.tether.io/sdk/fiat-modules/fiat-moonpay/usage Description: How to use the @tetherto/wdk-protocol-fiat-moonpay module # Usage The [@tetherto/wdk-protocol-fiat-moonpay](https://www.npmjs.com/package/@tetherto/wdk-protocol-fiat-moonpay) module builds signed MoonPay widget URLs and quotes for on-ramp and off-ramp flows. Use the guides below for setup, trading, and transaction follow-up. Install the package and initialize MoonPayProtocol. On-ramp, off-ramp, quotes, supported assets, widget options, recipients. Check status and load transaction details from MoonPay. Get started with WDK in a Node.js environment API keys, caching, and MoonPay configuration options Constructor, methods, and types for MoonPayProtocol *** ## Get Started URL: https://docs.wdk.tether.io/sdk/get-started Description: Learn about the SDK and modules architecture The SDK is a comprehensive, modular plug-in framework designed to simplify multi-chain wallet development. It is built on some core principles: **self-custodial and stateless** (private keys never leave your app and no data is stored by WDK), **unified interface** (consistent API across all blockchains), and **cross-platform compatibility** (works seamlessly from Node.js to React Native to embedded systems). #### Capabilities * **Multi-Chain Support**: Bitcoin, Ethereum, TON, TRON, Solana, Spark, and more * **Account Abstraction**: Gasless transactions on supported chains * **DeFi Integration**: Plug-in support for swidge routes, swaps, bridges, and lending protocols * **Extensible Design**: Add custom modules for new blockchains or protocols *** ### Modular Architecture WDK's architecture is built around the concept of composable modules. Each module is a specialized component that handles specific functionality, allowing you to build exactly what you need without unnecessary complexity. Each module has a single responsibility. Wallet modules handle blockchain operations, protocol modules manage DeFi interactions, and the core module orchestrates everything. New functionality is added through modules rather than modifying core code. Also, modules are configured through simple objects, making them easy to customize for different environments and use cases. *** #### Module Types WDK modules are organized into six main categories, each serving a specific purpose in the blockchain application stack: Main orchestrator and shared utilities Blockchain-specific wallet operations Swap-only, bridge-only, or combined asset routes Token swapping across DEXs Cross-chain asset transfers DeFi lending and borrowing *** ### How to use the SDK The WDK SDK uses a registration-based system where modules are added to a central orchestrator. This creates a unified interface while maintaining module independence. #### Registration Flow **1. Core Module Initialization** ```typescript title="Initialize WDK" import WDK from '@tetherto/wdk' // Generate 24-word seed phrase for higher security const seedPhrase = WDK.getRandomSeedPhrase(24) // Or use 12-word seed phrase (default) // const seedPhrase = WDK.getRandomSeedPhrase() const wdk = new WDK(seedPhrase) ``` **2. Wallet Module Registration** ```typescript title="Register Wallets" import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerBtc from '@tetherto/wdk-wallet-btc' const wdkWithWallets = wdk .registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth.drpc.org' }) .registerWallet('bitcoin', WalletManagerBtc, { provider: 'https://blockstream.info/api' }) ``` **3. Protocol Module Registration** ```typescript title="Register Protocols" import SwapveloraEvm from '@tetherto/wdk-protocol-swap-velora-evm' const wdkWithProtocols = wdkWithWallets .registerProtocol('swap-velora-evm', SwapveloraEvm) ``` #### Unified Operations Once registered, all modules work through the same interface: ```typescript title="Unified Operations" // Get accounts from different blockchains using the same method const ethAccount = await wdkWithProtocols.getAccount('ethereum', 0) const btcAccount = await wdkWithProtocols.getAccount('bitcoin', 0) // Check balances using unified interface const ethBalance = await ethAccount.getBalance() const btcBalance = await btcAccount.getBalance() // Send transactions with consistent API const ethTx = await ethAccount.sendTransaction({ to: '0x...', value: '1000000000000000000' }) const btcTx = await btcAccount.sendTransaction({ to: '1A1z...', value: 100000000 }) // Use DeFi protocols through the same interface const swapResult = await wdkWithProtocols.executeProtocol('swap-velora-evm', { fromToken: 'ETH', toToken: 'USDT', amount: '1000000000000000000' }) ``` *** ### Creating Custom Modules WDK's modular architecture makes it straightforward to add support for new blockchains or protocols. Each module type has a specific interface that must be implemented. #### Wallet Module Interface ```typescript title="Custom Wallet Module Setup" interface WalletModule { // Account management getAccount(index: number): Promise getAddress(index: number): Promise getBalance(index: number): Promise // Transaction operations sendTransaction(params: TransactionParams): Promise estimateTransaction(params: TransactionParams): Promise // Key management signMessage(message: string, index: number): Promise verifySignature(message: string, signature: string, address: string): Promise // Blockchain-specific operations getTransactionHistory(index: number, limit?: number): Promise getTokenBalance(index: number, tokenAddress: string): Promise } ``` #### Protocol Module Interface ```typescript title="Custom Protocol Module Setup" interface ProtocolModule { // Protocol execution execute(params: ProtocolParams): Promise estimate(params: ProtocolParams): Promise // Supported operations getSupportedTokens(): Promise getSupportedChains(): Promise getOperationTypes(): Promise // Protocol-specific methods getLiquidityPools?(): Promise getLendingRates?(): Promise getBridgeRoutes?(): Promise } ``` #### Module Implementation Example ```typescript title="Custom Wallet Module Implementation" class CustomWalletModule implements WalletModule { private provider: string private chainId: number constructor(config: { provider: string; chainId: number }) { this.provider = config.provider this.chainId = config.chainId } async getAccount(index: number): Promise { // Implement account derivation logic const privateKey = await this.derivePrivateKey(index) return new CustomAccount(privateKey, this.provider) } async getAddress(index: number): Promise { const account = await this.getAccount(index) return account.getAddress() } async getBalance(index: number): Promise { const address = await this.getAddress(index) // Implement balance fetching logic const balance = await this.fetchBalance(address) return new BigNumber(balance) } async sendTransaction(params: TransactionParams): Promise { // Implement transaction sending logic const account = await this.getAccount(params.accountIndex) const tx = await account.sendTransaction(params) return tx } // Additional methods... } ``` #### Module Registration ```typescript title="Custom Wallet Module Registration" // Register your custom module const wdkWithCustom = wdk.registerWallet('custom-chain', CustomWalletModule, { provider: 'https://custom-rpc-endpoint.com', chainId: 12345 }) // Use it like any other module const customAccount = await wdkWithCustom.getAccount('custom-chain', 0) const balance = await customAccount.getBalance() ``` *** ### Quickstart Paths Ready to start building? Choose your development environment: Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo *** ## Need Help? *** ## Lending Modules Overview URL: https://docs.wdk.tether.io/sdk/lending-modules Description: Explore WDK lending modules for integrating lending protocols with WDK. The Wallet Development Kit (WDK) provides a set of modules that support connection with lending protocols on different blockchain networks. All modules share a common interface, ensuring consistent behavior across different blockchain implementations. ## Lending & Borrowing Protocol Modules DeFi lending functionality for different lending & borrowing protocols | Module | Route | Status | Documentation | |--------|-------|--------|---------------| | [`@tetherto/wdk-protocol-lending-aave-evm`](https://github.com/tetherto/wdk-protocol-lending-aave-evm) | EVM | ✅ Ready | [Documentation](/sdk/lending-modules/lending-aave-evm/) | | [`@morpho-org/wdk-protocol-lending-morpho-evm`](https://www.npmjs.com/package/@morpho-org/wdk-protocol-lending-morpho-evm) | EVM | Community | [Documentation](/sdk/lending-modules/lending-morpho-evm/) | ## Next Steps Compare the available EVM lending modules and open the implementation that matches your protocol target: Use the Tether-maintained Aave V3 lending module for EVM accounts. Use the community Morpho module for Vault V2 and Morpho Blue EVM flows. Install the Morpho module, create the client, and review prerequisites. *** ## Need Help? *** ## Lending Aave EVM Overview URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm Description: Overview of the @tetherto/wdk-protocol-lending-aave-evm module A lightweight package that lets EVM wallet accounts interact with Aave V3: supply, withdraw, borrow, repay, and read account data. It works with both standard EVM wallets and ERC‑4337 smart accounts. ## Features - **Supply/Withdraw**: Add and remove supported assets from Aave pools - **Borrow/Repay**: Borrow assets and repay debt - **Account Data**: Read collateral, debt, health factor, and more - **Quote System**: Estimate fees before sending transactions - **AA Support**: Works with standard EVM and ERC‑4337 smart accounts - **TypeScript Support**: Full TypeScript definitions ## Supported Networks Works on Aave V3 supported EVM networks (e.g., Ethereum, Arbitrum, Base, Optimism, Polygon, Avalanche, BNB, Celo, Gnosis, Linea, Scroll, Soneium, Sonic, ZkSync, Metis). A working RPC provider and correct token addresses are required. ## Wallet Compatibility - **Standard EVM Wallets**: `@tetherto/wdk-wallet-evm` - **ERC‑4337 Smart Accounts**: `@tetherto/wdk-wallet-evm-erc-4337` - **Read‑Only Accounts**: For quoting and reading account data without sending transactions ## Key Components - **Aave V3 Integration**: Supply, withdraw, borrow, repay primitives - **Quote Helpers**: `quoteSupply`, `quoteWithdraw`, `quoteBorrow`, `quoteRepay` - **Collateral Controls**: Toggle collateral usage; set user eMode ## Next Steps How to supply, withdraw, borrow and repay with Aave Service setup, account config, ERC‑4337 options Full API for Aave Protocol Evm methods and types *** ## Lending Aave EVM API Reference URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm/api-reference Description: API Reference for @tetherto/wdk-protocol-lending-aave-evm # API Reference ## Class: AaveProtocolEvm Main class for Aave V3 lending on EVM. ### Constructor ```javascript new AaveProtocolEvm(account) ``` Parameters: - `account`: `WalletAccountEvm | WalletAccountReadOnlyEvm | WalletAccountEvmErc4337 | WalletAccountReadOnlyEvmErc4337` Example: ```javascript const aave = new AaveProtocolEvm(account) ``` ### Methods | Method | Description | Returns | |--------|-------------|---------| | `supply(options, config?)` | Add tokens to the pool | `Promise<{hash: string, fee: bigint, approveHash?: string, resetAllowanceHash?: string}>` | | `quoteSupply(options, config?)` | Estimate cost to add tokens | `Promise<{fee: bigint}>` | | `withdraw(options, config?)` | Remove tokens from the pool | `Promise<{hash: string, fee: bigint}>` | | `quoteWithdraw(options, config?)` | Estimate cost to withdraw | `Promise<{fee: bigint}>` | | `borrow(options, config?)` | Borrow tokens | `Promise<{hash: string, fee: bigint}>` | | `quoteBorrow(options, config?)` | Estimate borrowing cost | `Promise<{fee: bigint}>` | | `repay(options, config?)` | Repay borrowed tokens | `Promise<{hash: string, fee: bigint}>` | | `quoteRepay(options, config?)` | Estimate repayment cost | `Promise<{fee: bigint}>` | | `setUseReserveAsCollateral(token, use, config?)` | Toggle token as collateral | `Promise<{hash: string, fee: bigint}>` | | `setUserEMode(categoryId, config?)` | Set user eMode | `Promise<{hash: string, fee: bigint}>` | | `getAccountData(account?)` | Read account stats | `Promise<{ totalCollateralBase: bigint, totalDebtBase: bigint, availableBorrowsBase: bigint, currentLiquidationThreshold: bigint, ltv: bigint, healthFactor: bigint }>` | --- When `AaveProtocolEvm` is initialized with an ERC‑4337 smart account, the optional `config` argument on mutating and quote methods accepts the same gas-payment override families documented in [`@tetherto/wdk-wallet-evm-erc-4337`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): paymaster token, sponsorship policy, and native coins. ### `supply(options, config?)` Add tokens to the pool. Options: - `token` (`string`): token address - `amount` (`number | bigint`): amount in base units - `onBehalfOf` (`string`, optional) Returns: - May include `approveHash` and `resetAllowanceHash` for standard accounts (e.g., USD₮ allowance reset on Ethereum mainnet) Example: ```javascript const res = await aave.supply({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` --- ### `quoteSupply(options, config?)` Estimate fee to add tokens. ```javascript const q = await aave.quoteSupply({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` --- ### `withdraw(options, config?)` Remove tokens from the pool. Options: - `token` (`string`) - `amount` (`number | bigint`) - `to` (`string`, optional) ```javascript const tx = await aave.withdraw({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` --- ### `quoteWithdraw(options, config?)` Estimate fee to withdraw tokens. ```javascript const q = await aave.quoteWithdraw({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` --- ### `borrow(options, config?)` Borrow tokens. Options: - `token` (`string`) - `amount` (`number | bigint`) - `onBehalfOf` (`string`, optional) ```javascript const tx = await aave.borrow({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` --- ### `quoteBorrow(options, config?)` Estimate fee to borrow tokens. ```javascript const q = await aave.quoteBorrow({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` --- ### `repay(options, config?)` Repay borrowed tokens. Options: - `token` (`string`) - `amount` (`number | bigint`) - `onBehalfOf` (`string`, optional) ```javascript const tx = await aave.repay({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` Returns: - For standard accounts, may include `approveHash` / `resetAllowanceHash` when applicable. --- ### `quoteRepay(options, config?)` Estimate fee to repay borrowed tokens. ```javascript const q = await aave.quoteRepay({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` --- ### `setUseReserveAsCollateral(token, use, config?)` Toggle token as collateral for the user. ```javascript const tx = await aave.setUseReserveAsCollateral('TOKEN_ADDRESS', true) ``` --- ### `setUserEMode(categoryId, config?)` Set user eMode category. ```javascript const tx = await aave.setUserEMode(1) ``` --- ### `getAccountData(account?)` Read account stats like total collateral, debt, and health. ```javascript const data = await aave.getAccountData() ``` Returns the following structure: ```javascript { totalCollateralBase: bigint, totalDebtBase: bigint, availableBorrowsBase: bigint, currentLiquidationThreshold: bigint, ltv: bigint, healthFactor: bigint } ``` --- ## ERC‑4337 Config Override (optional) When the protocol uses `WalletAccountEvmErc4337` or `WalletAccountReadOnlyEvmErc4337`, the optional `config` argument on `supply`, `quoteSupply`, `withdraw`, `quoteWithdraw`, `borrow`, `quoteBorrow`, `repay`, `quoteRepay`, `setUseReserveAsCollateral`, and `setUserEMode` accepts the wallet module's per-call gas-payment overrides. - **Paymaster token mode**: `paymasterUrl`, `paymasterAddress`, `paymasterToken`, `transferMaxFee` - **Sponsorship policy mode**: `isSponsored`, `paymasterUrl`, `sponsorshipPolicyId` - **Native coin mode**: `useNativeCoins`, `transferMaxFee` Example: ```javascript const res = await aave.supply( { token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }, { paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } } ) ``` ## Rules & Notes - `token` must be a valid (non‑zero) address - `amount` > 0 and in token base units (use BigInt) - `onBehalfOf`/`to` (if set) must be valid, non‑zero addresses - A provider is required to read/send transactions - For USD₮ on mainnet, allowance may be reset to 0 then set again before actions Get started with WDK in a Node.js environment Get started with WDK's Lending Aave EVM Protocol configuration Get started with WDK's Lending Aave EVM Protocol usage *** ### Need Help? *** ## Lending Aave EVM Configuration URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm/configuration Description: Configuration options and settings for @tetherto/wdk-protocol-lending-aave-evm # Configuration ## Service Setup ```javascript import AaveProtocolEvm from '@tetherto/wdk-protocol-lending-aave-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' // Create wallet account first const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) // Create lending service const aave = new AaveProtocolEvm(account) ``` ## Account Configuration The service uses the wallet account configuration to connect to the target network and sign transactions. ```javascript import { WalletAccountEvm, WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm' // Full access account const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) // Read-only account (quotes, reads) const readOnly = new WalletAccountReadOnlyEvm('0xYourAddress', { provider: 'https://ethereum-rpc.publicnode.com' }) const aave = new AaveProtocolEvm(account) ``` ## ERC‑4337 (Account Abstraction) When using ERC‑4337 smart accounts, every mutating method and quote helper accepts an optional `config` override. In `v1.0.0-beta.4`, that override matches the three gas-payment families exposed by [`@tetherto/wdk-wallet-evm-erc-4337`](/sdk/wallet-modules/wallet-evm-erc-4337/configuration): paymaster token, sponsorship policy, or native coins. Use the fields that match the gas-payment mode you want for that call. For the full field-level definitions, see the [`@tetherto/wdk-wallet-evm-erc-4337` configuration docs](/sdk/wallet-modules/wallet-evm-erc-4337/configuration) and [`Config Override`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) reference. ```javascript import { WalletAccountEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337' const aa = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", { chainId: 1, provider: 'https://arb1.arbitrum.io/rpc', bundlerUrl: 'YOUR_BUNDLER_URL', paymasterUrl: 'YOUR_PAYMASTER_URL' }) const aaveAA = new AaveProtocolEvm(aa) const result = await aaveAA.supply({ token: '0xdAC17F...ec7', amount: 1000000n }, { paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } }) ``` ### Supported Override Families - **Paymaster token mode**: `paymasterUrl`, `paymasterAddress`, `paymasterToken`, `transferMaxFee` - **Sponsorship policy mode**: `isSponsored`, `paymasterUrl`, `sponsorshipPolicyId` - **Native coin mode**: `useNativeCoins`, `transferMaxFee` ## Network Support Aave V3 spans multiple EVM chains (Ethereum, Arbitrum, Base, Optimism, Polygon, Avalanche, BNB, Celo, Gnosis, Linea, Scroll, Soneium, Sonic, ZkSync, Metis). Ensure the correct RPC and token addresses for the target chain. ```javascript // Ethereum Mainnet const eth = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) // Arbitrum const arb = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://arb1.arbitrum.io/rpc' }) ``` ## Operation Options Each operation accepts a simple options object: ```javascript // Supply await aave.supply({ token: 'TOKEN_ADDRESS', amount: 1000000n }) // Withdraw await aave.withdraw({ token: 'TOKEN_ADDRESS', amount: 1000000n }) // Borrow await aave.borrow({ token: 'TOKEN_ADDRESS', amount: 1000000n }) // Repay await aave.repay({ token: 'TOKEN_ADDRESS', amount: 1000000n }) ``` ### Common Parameters - `token` (`string`): ERC‑20 token address - `amount` (`number | bigint`): token amount in base units - `onBehalfOf` (`string`, optional): another address to act for (supply/borrow/repay) - `to` (`string`, optional): destination address (withdraw) > Note: `amount` must be > 0. Addresses must be valid/non‑zero. A provider is required for any write. Get started with WDK in a Node.js environment Get started with WDK's Lending Aave EVM Protocol API Get started with WDK's Lending Aave EVM Protocol usage *** ### Need Help? *** ## Get Started URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm/guides/get-started Description: Install the package, create AaveProtocolEvm, and review prerequisites. This guide covers [installation](#installation), [creating the lending client](#create-the-lending-client), and [prerequisites](#prerequisites). Use [Node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/) on your machine. ## Installation Run the following to install [@tetherto/wdk-protocol-lending-aave-evm](https://www.npmjs.com/package/@tetherto/wdk-protocol-lending-aave-evm): ```bash title="Install with npm" npm install @tetherto/wdk-protocol-lending-aave-evm ``` ## Create the lending client You can attach Aave V3 actions to an EVM account from [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference) with [`new AaveProtocolEvm(account)`](/sdk/lending-modules/lending-aave-evm/api-reference) on [`AaveProtocolEvm`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Create AaveProtocolEvm" import AaveProtocolEvm from '@tetherto/wdk-protocol-lending-aave-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) const aave = new AaveProtocolEvm(account) ``` ## Prerequisites **Token balance:** To supply or repay, hold the ERC-20 in the wallet. **Gas:** Keep native balance (ETH on Ethereum, and so on) for transaction fees unless you use sponsored ERC-4337 flows. **Networks:** This module targets mainnet deployments; confirm your RPC matches [supported networks](/sdk/lending-modules/lending-aave-evm/configuration). Use contract addresses for Aave-supported reserves. On Ethereum mainnet, USD₮ uses `0xdAC17F958D2ee523a2206206994597C13D831ec7` (use `USDT` in code identifiers and literals). ## Next Steps - [Lending operations](lending-operations) - [Handle errors](handle-errors) *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm/guides/handle-errors Description: Catch lending failures and release wallet secrets safely. This guide explains how to [handle operation errors](#operation-errors) and follow [best practices](#best-practices) for disposing wallet state. ## Operation errors You can catch failures from [`supply()`](/sdk/lending-modules/lending-aave-evm/api-reference), [`withdraw()`](/sdk/lending-modules/lending-aave-evm/api-reference), [`borrow()`](/sdk/lending-modules/lending-aave-evm/api-reference), and [`repay()`](/sdk/lending-modules/lending-aave-evm/api-reference) with `try/catch`: ```javascript title="Handle a failed supply" try { await aave.supply({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 0n }) } catch (e) { console.error('Lending failed:', e.message) if (e.message.includes('zero')) { console.log('Amount must be greater than zero') } } ``` You can isolate quote failures from [`quoteSupply()`](/sdk/lending-modules/lending-aave-evm/api-reference) (or the other `quote*` methods) when you only need an estimate: ```javascript title="Handle quote errors" try { const q = await aave.quoteBorrow({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) console.log('Borrow fee (wei):', q.fee) } catch (e) { console.error('Quote failed:', e.message) } ``` See [Rules & Notes](/sdk/lending-modules/lending-aave-evm/api-reference) for address and amount validation expectations. ## Best Practices You can wipe private keys after lending work by calling [`dispose()`](/sdk/wallet-modules/wallet-evm/api-reference) on [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference), or [`dispose()`](/sdk/wallet-modules/wallet-evm/api-reference) on [`WalletManagerEvm`](/sdk/wallet-modules/wallet-evm/api-reference): ```javascript title="Dispose after lending session" try { await aave.supply({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) } finally { account.dispose() } ``` For ERC-4337 accounts, use [`dispose()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) on the smart-account type. Clear references to [`AaveProtocolEvm`](/sdk/lending-modules/lending-aave-evm/api-reference) when the session ends. ## Next Steps - [Lending operations](lending-operations) - [Get started](get-started) - [API reference](/sdk/lending-modules/lending-aave-evm/api-reference) *** ## Lending Operations URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm/guides/lending-operations Description: Supply, withdraw, borrow, repay, quote fees, use ERC-4337, and read account data. This guide walks through [supply](#supply), [withdraw](#withdraw), [borrow](#borrow), [repay](#repay), [quotes](#quotes-before-sending), [ERC-4337 usage](#erc-4337-smart-accounts), and [reading account data](#reading-account-data). It assumes an [`AaveProtocolEvm`](/sdk/lending-modules/lending-aave-evm/api-reference) instance named `aave` and the USD₮ contract on Ethereum mainnet `0xdAC17F958D2ee523a2206206994597C13D831ec7`. ## Supply You can deposit reserves into the pool using [`supply()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Supply USDT" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const tx = await aave.supply({ token: USDT, amount: 1000000n }) console.log('Supply tx hash:', tx.hash) ``` ## Withdraw You can remove supplied liquidity using [`withdraw()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Withdraw USDT" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const tx = await aave.withdraw({ token: USDT, amount: 1000000n }) console.log('Withdraw tx hash:', tx.hash) ``` ## Borrow You can draw debt against your collateral using [`borrow()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Borrow USDT" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const tx = await aave.borrow({ token: USDT, amount: 1000000n }) console.log('Borrow tx hash:', tx.hash) ``` ## Repay You can pay down debt using [`repay()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Repay USDT" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const tx = await aave.repay({ token: USDT, amount: 1000000n }) console.log('Repay tx hash:', tx.hash) ``` ## Quotes before sending You can estimate the supply fee with [`quoteSupply()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Quote supply fee" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const supplyQuote = await aave.quoteSupply({ token: USDT, amount: 1000000n }) console.log('Supply fee (wei):', supplyQuote.fee) ``` You can estimate the withdraw fee with [`quoteWithdraw()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Quote withdraw fee" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const withdrawQuote = await aave.quoteWithdraw({ token: USDT, amount: 1000000n }) console.log('Withdraw fee (wei):', withdrawQuote.fee) ``` You can estimate the borrow fee with [`quoteBorrow()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Quote borrow fee" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const borrowQuote = await aave.quoteBorrow({ token: USDT, amount: 1000000n }) console.log('Borrow fee (wei):', borrowQuote.fee) ``` You can estimate the repay fee with [`quoteRepay()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Quote repay fee" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const repayQuote = await aave.quoteRepay({ token: USDT, amount: 1000000n }) console.log('Repay fee (wei):', repayQuote.fee) ``` Health factor and collateralization limits still apply. A quote does not guarantee the transaction will succeed if on-chain state changes. ## ERC-4337 smart accounts You can run the same methods through [`WalletAccountEvmErc4337`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) and pass a second `config` argument to override per-call gas payment settings. In `v1.0.0-beta.4`, the lending methods accept the same override families as the wallet module: paymaster token, sponsorship policy, and native coins. See the [ERC-4337 config override](/sdk/lending-modules/lending-aave-evm/api-reference) section for the full field list. ```javascript title="Supply with paymaster" import { WalletAccountEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337' import AaveProtocolEvm from '@tetherto/wdk-protocol-lending-aave-evm' const aa = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", { chainId: 42161, provider: 'https://arb1.arbitrum.io/rpc', bundlerUrl: process.env.BUNDLER_URL, paymasterUrl: process.env.PAYMASTER_URL }) const aaveAA = new AaveProtocolEvm(aa) const result = await aaveAA.supply( { token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }, { paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } } ) console.log('Supply hash:', result.hash) ``` You can use the same second argument to: - override paymaster-token mode with `paymasterUrl`, `paymasterAddress`, `paymasterToken`, or `transferMaxFee` - switch one call to sponsorship mode with `isSponsored`, `paymasterUrl`, and `sponsorshipPolicyId` - switch one call to native-coin gas mode with `useNativeCoins` and `transferMaxFee` Use token addresses that exist on the same chain as the smart account RPC. ## Reading account data You can inspect collateral, debt, and health using [`getAccountData()`](/sdk/lending-modules/lending-aave-evm/api-reference): ```javascript title="Read Aave account data" const data = await aave.getAccountData() console.log({ totalCollateralBase: data.totalCollateralBase, totalDebtBase: data.totalDebtBase, availableBorrowsBase: data.availableBorrowsBase, currentLiquidationThreshold: data.currentLiquidationThreshold, ltv: data.ltv, healthFactor: data.healthFactor }) ``` ## Next Steps - [Handle errors](handle-errors) - [Get started](get-started) - [API reference](/sdk/lending-modules/lending-aave-evm/api-reference) *** ## Lending Aave EVM Guides URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm/usage Description: How to install and use @tetherto/wdk-protocol-lending-aave-evm on EVM # Usage The [@tetherto/wdk-protocol-lending-aave-evm](https://www.npmjs.com/package/@tetherto/wdk-protocol-lending-aave-evm) module exposes Aave V3 supply, borrow, and repayment flows for EVM accounts. Follow the guides below for setup, day-to-day operations, and error handling. Install the package, create AaveProtocolEvm, and review prerequisites. Supply, withdraw, borrow, repay, quotes, ERC-4337, and account data. Handle failures and dispose wallet secrets when finished. Get started with WDK in a Node.js environment Networks and deployment settings for the Aave lending protocol Methods and parameters for AaveProtocolEvm *** ## Lending Morpho EVM Overview URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-morpho-evm Description: Overview of the @morpho-org/wdk-protocol-lending-morpho-evm community module Community modules are developed and maintained independently by third-party contributors. Tether and the WDK Team do not endorse or assume responsibility for their code, security, or maintenance. Use your own judgment and proceed at your own risk. The [@morpho-org/wdk-protocol-lending-morpho-evm](https://www.npmjs.com/package/@morpho-org/wdk-protocol-lending-morpho-evm) community module lets WDK-compatible EVM wallet accounts interact with Morpho Vault V2 earn targets and Morpho Blue markets through [`@morpho-org/morpho-sdk`](https://www.npmjs.com/package/@morpho-org/morpho-sdk). ## Features - **Vault earn flows**: Deposit into and withdraw from configured Morpho Vault V2 targets - **Market collateral**: Supply and withdraw collateral in a configured Morpho Blue market - **Borrow/Repay**: Borrow from and repay a configured Morpho Blue market - **Requirements API**: Surface Morpho SDK approval, signature, and authorization requirements - **Quotes**: Estimate transaction costs before sending - **Account Reads**: Read vault, market, and combined account position data - **Account Support**: Works with standard EVM accounts and ERC-4337 smart accounts ## Supported Targets The module supports curated Ethereum mainnet presets and explicit Morpho target configuration. ### Earn Presets | Preset | Vault | |--------|-------| | `sky-money-usdt-savings` | sky.money USDT Savings V2 | | `steakhouse-prime-instant` | Steakhouse Prime Instant V2 | ### Borrow Presets | Preset | Collateral | |--------|------------| | `susds` | sUSDS | | `wsteth` | wstETH | | `wbtc` | WBTC | | `xaut` | XAUt | ## Wallet Compatibility - **Standard EVM Wallets**: `@tetherto/wdk-wallet-evm` - **ERC-4337 Smart Accounts**: `@tetherto/wdk-wallet-evm-erc-4337` - **Read-Only Accounts**: For quoting and reading configured vault or market positions without sending transactions ## Key Components - **MorphoProtocolEvm**: Main class for Morpho lending operations - **Morpho Protocol Options**: Configure vaults, markets, presets, chain guards, slippage, signatures, and deployless reads - **Requirement Helpers**: `getSupplyRequirements`, `getSupplyCollateralRequirements`, `getBorrowRequirements`, and `getRepayRequirements` - **Position Reads**: `getVaultPosition`, `getMarketPosition`, and `getAccountData` ## Next Steps Install the package, create MorphoProtocolEvm, and review prerequisites. How to use Morpho vault, market, quote, requirement, and position flows. Install the package and configure presets, explicit targets, and options. Methods, options, presets, and return shapes for MorphoProtocolEvm. *** ## Lending Morpho EVM API Reference URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-morpho-evm/api-reference Description: API Reference for @morpho-org/wdk-protocol-lending-morpho-evm # API Reference ## Class: MorphoProtocolEvm Main class for Morpho Vault V2 and Morpho Blue lending on EVM. ### Constructor ```javascript new MorphoProtocolEvm(account, options) ``` Parameters: - `account`: `WalletAccountEvm | WalletAccountReadOnlyEvm | WalletAccountEvmErc4337 | WalletAccountReadOnlyEvmErc4337` - `options`: `MorphoProtocolOptions` Example: ```javascript const morpho = new MorphoProtocolEvm(account, { presets: { earn: 'sky-money-usdt-savings', borrow: 'wsteth' } }) ``` ### Methods | Method | Description | Returns | |--------|-------------|---------| | `supply(options, config?)` | Deposit assets into the configured vault | `Promise` | | `getSupplyRequirements(options, requirementOptions?)` | Return approval or signature requirements for vault deposit | `Promise` | | `quoteSupply(options, config?)` | Quote vault deposit | `Promise>` | | `withdraw(options, config?)` | Withdraw assets from the configured vault | `Promise` | | `quoteWithdraw(options, config?)` | Quote vault withdrawal | `Promise>` | | `supplyCollateral(options, config?)` | Supply collateral to the configured market | `Promise` | | `getSupplyCollateralRequirements(options, requirementOptions?)` | Return approval or signature requirements for collateral supply | `Promise` | | `quoteSupplyCollateral(options, config?)` | Quote collateral supply | `Promise>` | | `borrow(options, config?)` | Borrow from the configured market | `Promise` | | `getBorrowRequirements(options)` | Return Morpho authorization requirements for borrow | `Promise` | | `quoteBorrow(options, config?)` | Quote borrow | `Promise>` | | `repay(options, config?)` | Repay the configured market | `Promise` | | `getRepayRequirements(options, requirementOptions?)` | Return approval or signature requirements for repay | `Promise` | | `quoteRepay(options, config?)` | Quote repay | `Promise>` | | `withdrawCollateral(options, config?)` | Withdraw collateral from the configured market | `Promise` | | `quoteWithdrawCollateral(options, config?)` | Quote collateral withdrawal | `Promise>` | | `getVaultPosition(account?)` | Read configured vault position | `Promise` | | `getMarketPosition(account?)` | Read configured market position | `Promise` | | `getAccountData(account?)` | Read combined configured vault and market position | `Promise` | | `getVaultAddress()` | Return the configured vault address | `Address` | | `getBorrowMarketId()` | Return the configured borrow market id | `string` | --- ## Requirements and Results - `ApprovalOrSignatureRequirement`: returned by supply, collateral supply, and repay requirement helpers. Each item is either a Morpho SDK approval transaction with `to`, `value`, and `data`, or a signature requirement with `sign(client, userAddress)`. - `RequirementAuthorization`: returned by `getBorrowRequirements()`. These are Morpho authorization transactions with `to`, `value`, and `data`. - `RequirementSignature`: returned by a signature requirement's `sign(client, userAddress)` helper. Pass it to `supply()`, `supplyCollateral()`, or `repay()` as `requirementSignature`. - Write methods return the WDK wallet transaction result, including `hash` and `fee`. Quote methods return the same protocol result without `hash`. --- ### `supply(options, config?)` Deposit assets into the configured Morpho vault. Options: - `token` (`string`): configured vault asset - `amount` (`number | bigint`, optional when `nativeAmount` is set): ERC-20 amount in base units - `nativeAmount` (`number | bigint`, optional): native amount to wrap and supply - `onBehalfOf` (`string`, optional): must equal the connected wallet address when set - `requirementSignature` (`RequirementSignature`, optional): signature returned by a Morpho SDK requirement - `slippageTolerance` (`bigint`, optional): per-call Morpho SDK slippage tolerance ```javascript const tx = await morpho.supply({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) ``` ### `getSupplyRequirements(options, requirementOptions?)` Return Morpho SDK approval or signature requirements for a vault deposit. Use it before `supply()` when the account has not approved the required spender or when signature support is enabled. ```javascript const requirements = await morpho.getSupplyRequirements({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) ``` ### `quoteSupply(options, config?)` Quote the fee for a vault deposit transaction. ```javascript const quote = await morpho.quoteSupply({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) ``` --- ### `withdraw(options, config?)` Withdraw assets from the configured Morpho vault. Options: - `token` (`string`): configured vault asset - `amount` (`number | bigint`): amount in base units - `to` (`string`, optional): must equal the connected wallet address when set ```javascript const tx = await morpho.withdraw({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) ``` ### `quoteWithdraw(options, config?)` Quote the fee for a vault withdrawal transaction. ```javascript const quote = await morpho.quoteWithdraw({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) ``` --- ### `supplyCollateral(options, config?)` Supply collateral to the configured Morpho Blue market. Options: - `token` (`string`): configured market collateral token - `amount` (`number | bigint`, optional when `nativeAmount` is set): ERC-20 amount in base units - `nativeAmount` (`number | bigint`, optional): native amount to wrap and supply - `onBehalfOf` (`string`, optional): must equal the connected wallet address when set - `requirementSignature` (`RequirementSignature`, optional): signature returned by a Morpho SDK requirement ```javascript const tx = await morpho.supplyCollateral({ token: 'COLLATERAL_TOKEN_ADDRESS', amount: 1000000000000000000n }) ``` ### `getSupplyCollateralRequirements(options, requirementOptions?)` Return Morpho SDK approval or signature requirements for collateral supply. ```javascript const requirements = await morpho.getSupplyCollateralRequirements({ token: 'COLLATERAL_TOKEN_ADDRESS', amount: 1000000000000000000n }) ``` ### `quoteSupplyCollateral(options, config?)` Quote the fee for supplying collateral. ```javascript const quote = await morpho.quoteSupplyCollateral({ token: 'COLLATERAL_TOKEN_ADDRESS', amount: 1000000000000000000n }) ``` --- ### `borrow(options, config?)` Borrow assets from the configured Morpho Blue market. Options: - `token` (`string`): configured market loan token - `amount` (`number | bigint`): amount in base units - `onBehalfOf` (`string`, optional): must equal the connected wallet address when set - `reallocations` (`readonly VaultReallocation[]`, optional): Morpho Vault V2 reallocations - `slippageTolerance` (`bigint`, optional): per-call Morpho SDK slippage tolerance ```javascript const tx = await morpho.borrow({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) ``` ### `getBorrowRequirements(options)` Return Morpho authorization requirements for borrow flows. ```javascript const requirements = await morpho.getBorrowRequirements({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) ``` ### `quoteBorrow(options, config?)` Quote the fee for borrowing. ```javascript const quote = await morpho.quoteBorrow({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) ``` --- ### `repay(options, config?)` Repay assets to the configured Morpho Blue market. Options: - `token` (`string`): configured market loan token - `amount` (`number | bigint | "max"`): amount in base units, or `"max"` to repay current borrow shares - `onBehalfOf` (`string`, optional): must equal the connected wallet address when set - `requirementSignature` (`RequirementSignature`, optional): signature returned by a Morpho SDK requirement - `slippageTolerance` (`bigint`, optional): per-call Morpho SDK slippage tolerance ```javascript const tx = await morpho.repay({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 'max' }) ``` When `amount` is `"max"`, Morpho repays borrow shares. Re-run `getRepayRequirements({ amount: "max" })` immediately before sending `repay()` so the approval or permit reflects current market state, or approve `getChainAddresses(chainId).bundler3.generalAdapter1` with a small buffer above current debt and pass a non-zero `slippageTolerance`. `slippageTolerance` caps the repay share price or transfer amount; it is not approval slippage. ### `getRepayRequirements(options, requirementOptions?)` Return Morpho SDK approval or signature requirements for repayment. ```javascript const requirements = await morpho.getRepayRequirements({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 'max' }) ``` ### `quoteRepay(options, config?)` Quote the fee for repayment. ```javascript const quote = await morpho.quoteRepay({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 'max' }) ``` --- ### `withdrawCollateral(options, config?)` Withdraw collateral from the configured Morpho Blue market. Options: - `token` (`string`): configured market collateral token - `amount` (`number | bigint`): amount in base units - `to` (`string`, optional): must equal the connected wallet address when set ```javascript const tx = await morpho.withdrawCollateral({ token: 'COLLATERAL_TOKEN_ADDRESS', amount: 1000000000000000000n }) ``` ### `quoteWithdrawCollateral(options, config?)` Quote the fee for withdrawing collateral. ```javascript const quote = await morpho.quoteWithdrawCollateral({ token: 'COLLATERAL_TOKEN_ADDRESS', amount: 1000000000000000000n }) ``` --- ## Position Reads ### `getVaultPosition(account?)` Read this or another account's configured vault position. Returns: ```javascript { shares: bigint, assets: bigint, vaultAddress: Address } ``` ### `getMarketPosition(account?)` Read this or another account's configured market position. Returns: ```javascript { supplyShares: bigint, borrowShares: bigint, borrowAssets: bigint, collateral: bigint, marketId: string } ``` ### `getAccountData(account?)` Read combined configured vault and market position data. Returns: ```javascript { vaultShares: bigint, vaultAssets: bigint, marketSupplyShares: bigint, marketBorrowShares: bigint, marketBorrowAssets: bigint, collateral: bigint, vaultAddress: Address, marketId: string } ``` ## Rules & Notes - The wallet account must include a provider. - Write methods require a writable EVM account. - `token`, `onBehalfOf`, `to`, and `account` addresses must be valid when provided. - For vault supply and collateral supply, pass `amount`, `nativeAmount`, or both, and make sure the combined supplied amount is greater than zero. - Withdraw, borrow, and collateral-withdraw amounts must be greater than zero. `repay()` also accepts `amount: "max"`. - Vault operations require the token to match the configured vault asset. - Market borrow and repay operations require the token to match the configured market loan token. - Collateral operations require the token to match the configured market collateral token. - `onBehalfOf` and vault or collateral withdrawal `to` must equal the connected wallet address when set. - `slippageTolerance` applies to vault supply, borrow, and repay calls in the published adapter. Collateral supply uses the Morpho SDK collateral-supply action defaults. - Use `get*Requirements` methods before final actions when the Morpho SDK reports approval, signature, or authorization requirements. Presets, explicit targets, and Morpho SDK options Get started with Morpho lending operations *** ### Need Help? *** ## Lending Morpho EVM Configuration URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-morpho-evm/configuration Description: Configuration options and settings for @morpho-org/wdk-protocol-lending-morpho-evm # Configuration ## Installation Install the Morpho lending module with the EVM wallet module used by the examples and the `viem` peer dependency: ```bash title="Install with npm" npm install @morpho-org/wdk-protocol-lending-morpho-evm @tetherto/wdk-wallet-evm viem ``` ```bash title="Install with pnpm" pnpm add @morpho-org/wdk-protocol-lending-morpho-evm @tetherto/wdk-wallet-evm viem ``` The package declares Node.js `22.13` or later in its published engine metadata. If you use ERC-4337 smart accounts, also install `@tetherto/wdk-wallet-evm-erc-4337`. ## Service Setup Create a WDK-compatible EVM wallet account, then pass it to `MorphoProtocolEvm` with either presets or explicit Morpho targets. ```javascript title="Create MorphoProtocolEvm with presets" import MorphoProtocolEvm from '@morpho-org/wdk-protocol-lending-morpho-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) const morpho = new MorphoProtocolEvm(account, { presets: { earn: 'sky-money-usdt-savings', borrow: 'wsteth' } }) ``` ## Constructor ```javascript new MorphoProtocolEvm(account, options) ``` Parameters: - `account`: `WalletAccountEvm`, `WalletAccountReadOnlyEvm`, `WalletAccountEvmErc4337`, or `WalletAccountReadOnlyEvmErc4337` - `options`: Morpho target configuration The wallet account must include a provider. Read-only accounts can read positions and quote transactions; mutating methods require a writable account. ## Presets Built-in presets target Ethereum mainnet USDT earn and borrow flows. ```javascript title="Use built-in presets" const morpho = new MorphoProtocolEvm(account, { presets: { earn: 'steakhouse-prime-instant', borrow: 'wbtc' } }) ``` Borrow presets: | Preset | Chain ID | Market ID | Collateral | LLTV | |--------|----------|-----------|------------|------| | `susds` | `1` | `0x3274643db77a064abd3bc851de77556a4ad2e2f502f4f0c80845fa8f909ecf0b` | sUSDS | 96.5% | | `wsteth` | `1` | `0xe7e9694b754c4d4f7e21faf7223f6fa71abaeb10296a4c43a54a7977149687d2` | wstETH | 86% | | `wbtc` | `1` | `0xa921ef34e2fc7a27ccc50ae7e4b154e16c9799d3387076c421423ef52ac4df99` | WBTC | 86% | | `xaut` | `1` | `0xb7843fe78e7e7fd3106a1b939645367967d1f986c2e45edb8932ad1896450877` | XAUt | 77% | Earn presets: | Preset | Chain ID | Vault Address | Vault | |--------|----------|---------------|-------| | `sky-money-usdt-savings` | `1` | `0x23f5E9c35820f4baB695Ac1F19c203cC3f8e1e11` | sky.money USDT Savings V2 | | `steakhouse-prime-instant` | `1` | `0xbeef003C68896c7D2c3c60d363e8d71a49Ab2bf9` | Steakhouse Prime Instant V2 | ## Explicit Targets Use explicit targets when you need a vault or market outside the built-in presets. ```javascript title="Use explicit Morpho targets" const morpho = new MorphoProtocolEvm(account, { chainId: 1, earnVaultAddress: '0x23f5E9c35820f4baB695Ac1F19c203cC3f8e1e11', borrowMarketId: '0xe7e9694b754c4d4f7e21faf7223f6fa71abaeb10296a4c43a54a7977149687d2' }) ``` When you use `earnVaultAddress`, `borrowMarketParams`, or `borrowMarketId` directly, pass `chainId`. The adapter uses it to guard transaction building if a browser wallet switches chains. Use `borrowMarketParams` when you already know the full Morpho Blue market configuration: ```javascript title="Use explicit Morpho Blue market params" const morpho = new MorphoProtocolEvm(account, { chainId: 1, borrowMarketParams: { loanToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', collateralToken: 'COLLATERAL_TOKEN_ADDRESS', oracle: 'ORACLE_ADDRESS', irm: 'INTEREST_RATE_MODEL_ADDRESS', lltv: 860000000000000000n } }) ``` `InputMarketParams` contains `loanToken`, `collateralToken`, `oracle`, `irm`, and `lltv`. Confirm explicit market params against Morpho market data before using them in production. ## Options | Option | Type | Description | |--------|------|-------------| | `chainId` | `number \| bigint` | Required with explicit Morpho targets | | `earnVaultAddress` | `string` | Explicit Morpho Vault V2 address | | `borrowMarketParams` | `InputMarketParams` | Explicit Morpho Blue market params | | `borrowMarketId` | `string` | Market id used to fetch market params on-chain | | `presets` | `{ earn?: string, borrow?: string }` | Built-in earn and borrow target names | | `slippageTolerance` | `bigint` | Morpho SDK slippage tolerance in WAD precision | | `supportSignature` | `boolean` | Enables Morpho SDK permit or Permit2 requirements | | `supportDeployless` | `boolean` | Enables Morpho SDK deployless reads | | `metadata` | `Metadata` | Optional Morpho SDK metadata passed to action encoders | ## Native Amounts For vault deposits and collateral supply, pass either `amount`, `nativeAmount`, or both. `nativeAmount` follows Morpho SDK semantics and is only valid when the configured vault asset or collateral token is the wrapped native token for the chain. ```javascript title="Supply with native amount" await morpho.supply({ token: 'WRAPPED_NATIVE_TOKEN_ADDRESS', nativeAmount: 1000000000000000n }) ``` ## ERC-4337 Config Overrides When using `WalletAccountEvmErc4337`, mutating methods and quote helpers accept an optional second `config` argument for the wallet module's per-call gas payment settings. ```javascript title="Supply with an ERC-4337 config override" await morpho.supply( { token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }, { paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } } ) ``` See the [`@tetherto/wdk-wallet-evm-erc-4337` configuration docs](/sdk/wallet-modules/wallet-evm-erc-4337/configuration) for paymaster token, sponsorship policy, and native coin override fields. Get started with WDK in a Node.js environment Methods and parameters for MorphoProtocolEvm Get started with Morpho lending operations *** ### Need Help? *** ## Get Started URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-morpho-evm/guides/get-started Description: Install the package, create MorphoProtocolEvm, and review prerequisites. This guide covers [installation](#installation), [creating the lending client](#create-the-lending-client), and [prerequisites](#prerequisites). Use [Node.js](https://nodejs.org/) `22.13` or later and [npm](https://www.npmjs.com/) on your machine. ## Installation Run the following to install [@morpho-org/wdk-protocol-lending-morpho-evm](https://www.npmjs.com/package/@morpho-org/wdk-protocol-lending-morpho-evm), the EVM wallet module used by the examples, and the `viem` peer dependency: ```bash title="Install with npm" npm install @morpho-org/wdk-protocol-lending-morpho-evm @tetherto/wdk-wallet-evm viem ``` ```bash title="Install with pnpm" pnpm add @morpho-org/wdk-protocol-lending-morpho-evm @tetherto/wdk-wallet-evm viem ``` If you use ERC-4337 smart accounts, also install `@tetherto/wdk-wallet-evm-erc-4337`. ## Create the lending client You can attach Morpho actions to an EVM account from [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference) with [`new MorphoProtocolEvm(account, options)`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Create MorphoProtocolEvm" import MorphoProtocolEvm from '@morpho-org/wdk-protocol-lending-morpho-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) const morpho = new MorphoProtocolEvm(account, { presets: { earn: 'sky-money-usdt-savings', borrow: 'wsteth' } }) ``` ## Prerequisites **Runtime:** Use Node.js `22.13` or later. **Token balance:** To supply, supply collateral, or repay, hold the required ERC-20 in the wallet. **Gas:** Keep native balance for transaction fees unless you use sponsored ERC-4337 flows. **Targets:** Confirm the configured vault or market matches the token and chain you plan to use. The built-in presets target Ethereum mainnet. If you configure an explicit vault address, market id, or market params, set `chainId` in [`MorphoProtocolOptions`](/sdk/lending-modules/lending-morpho-evm/configuration). ## Requirements before actions Morpho SDK actions can return approval, signature, or authorization requirements. Call the matching `get*Requirements` method before the final action when the account has not already satisfied those requirements. ```javascript title="Check supply requirements" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const requirements = await morpho.getSupplyRequirements({ token: USDT, amount: 1000000n }) console.log('Requirements:', requirements) ``` Send any returned transaction requirements with your EVM account flow before calling the final action. For signature requirements, call the requirement's `sign(client, userAddress)` helper, then pass the returned `requirementSignature` to `supply`, `supplyCollateral`, or `repay`. ## Next Steps - [Lending operations](lending-operations) - [Handle errors](handle-errors) *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-morpho-evm/guides/handle-errors Description: Catch Morpho lending failures and release wallet secrets safely. This guide explains how to [handle operation errors](#operation-errors), [handle requirement errors](#requirement-errors), and follow [best practices](#best-practices) for disposing wallet state. ## Operation errors You can catch failures from [`supply()`](/sdk/lending-modules/lending-morpho-evm/api-reference), [`withdraw()`](/sdk/lending-modules/lending-morpho-evm/api-reference), [`supplyCollateral()`](/sdk/lending-modules/lending-morpho-evm/api-reference), [`borrow()`](/sdk/lending-modules/lending-morpho-evm/api-reference), [`repay()`](/sdk/lending-modules/lending-morpho-evm/api-reference), and [`withdrawCollateral()`](/sdk/lending-modules/lending-morpho-evm/api-reference) with `try/catch`: ```javascript title="Handle a failed supply" try { await morpho.supply({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 0n }) } catch (e) { console.error('Morpho lending failed:', e.message) if (e.message.includes('amount')) { console.log('Amount must be greater than zero') } } ``` Common failure causes include: - wallet account does not have a provider configured - write method is called with a read-only account - token does not match the configured vault asset, market loan token, or market collateral token - explicit target is used without the required `chainId` - connected chain does not match the configured Morpho target - amount is zero, invalid, or larger than the account balance - `onBehalfOf` or `to` does not match the connected wallet address when required Use this checklist to map the most common failures to fixes: | Symptom | Likely cause | Fix | |---------|--------------|-----| | Constructor fails before any operation | Wallet account has no provider | Create the EVM account with a provider or use a read-only account with an RPC provider | | Write method fails on a read-only account | Read-only account can quote and read, but cannot send transactions | Use `WalletAccountEvm` or `WalletAccountEvmErc4337` for mutating methods | | Explicit target fails during setup | `chainId` is missing, invalid, or does not match the wallet chain | Pass the expected `chainId` with `earnVaultAddress`, `borrowMarketId`, or `borrowMarketParams` | | Token mismatch error | The supplied token is not the configured vault asset, market loan token, or collateral token | Use the token from the configured vault or market target | | Zero amount error | `amount` and `nativeAmount` are both absent or zero | Pass a positive ERC-20 `amount`, a positive `nativeAmount`, or `amount: "max"` for repay | | Requirement lookup returns approval or authorization | Allowance, permit, Permit2, or Morpho authorization is missing | Send returned transaction requirements or sign the returned signature requirement before the final action | | Final action fails after requirements | Allowance, balance, signature, authorization, quote, or market state changed after the requirement lookup | Re-run the matching `get*Requirements()` method and rebuild the final action | | Quote succeeds but write fails | On-chain state changed, the account lacks balance, or requirements were not satisfied | Re-check requirements, balances, token addresses, and chain before sending | ## Requirement errors Requirement helpers can fail if the configured target, token, account, or provider cannot produce a valid Morpho SDK action. ```javascript title="Handle requirement errors" try { const requirements = await morpho.getBorrowRequirements({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) console.log('Borrow requirements:', requirements) } catch (e) { console.error('Requirement lookup failed:', e.message) } ``` If a final action fails after requirements were returned, re-check the requirements. Allowances, signatures, authorizations, or on-chain market state can change between the requirement lookup and the final transaction. ## Quote errors You can isolate quote failures from write failures when you only need an estimate: ```javascript title="Handle quote errors" try { const quote = await morpho.quoteBorrow({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) console.log('Borrow fee:', quote.fee) } catch (e) { console.error('Quote failed:', e.message) } ``` See [Rules & Notes](/sdk/lending-modules/lending-morpho-evm/api-reference) for address, token, amount, and target validation expectations. ## Best Practices Dispose wallet secrets after a lending session by calling [`dispose()`](/sdk/wallet-modules/wallet-evm/api-reference) on [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference), or the matching dispose method on your smart account type. ```javascript title="Dispose after a Morpho lending session" try { await morpho.supply({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }) } finally { account.dispose() } ``` For ERC-4337 accounts, use [`dispose()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) on the smart-account type. Clear references to [`MorphoProtocolEvm`](/sdk/lending-modules/lending-morpho-evm/api-reference) when the session ends. ## Next Steps - [Lending operations](lending-operations) - [Get started](get-started) - [API reference](/sdk/lending-modules/lending-morpho-evm/api-reference) *** ## Lending Operations URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-morpho-evm/guides/lending-operations Description: Supply, withdraw, manage collateral, borrow, repay, quote fees, handle requirements, and read positions. This guide walks through [vault supply](#vault-supply), [vault withdraw](#vault-withdraw), [collateral](#collateral), [borrow](#borrow), [repay](#repay), [requirements](#requirements), [quotes](#quotes-before-sending), [ERC-4337 usage](#erc-4337-smart-accounts), and [position reads](#reading-positions). It assumes a [`MorphoProtocolEvm`](/sdk/lending-modules/lending-morpho-evm/api-reference) instance named `morpho`. ## Vault supply Deposit into the configured Morpho Vault V2 target with [`supply()`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Supply USDT to the configured vault" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const requirements = await morpho.getSupplyRequirements({ token: USDT, amount: 1000000n }) console.log('Supply requirements:', requirements) const tx = await morpho.supply({ token: USDT, amount: 1000000n }) console.log('Supply tx hash:', tx.hash) ``` The `token` must match the configured vault asset. ## Vault withdraw Withdraw from the configured vault with [`withdraw()`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Withdraw USDT from the configured vault" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const tx = await morpho.withdraw({ token: USDT, amount: 1000000n }) console.log('Withdraw tx hash:', tx.hash) ``` If you pass `to`, it must equal the connected wallet address. ## Collateral Supply collateral to the configured Morpho Blue market with [`supplyCollateral()`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Supply collateral" const COLLATERAL = 'COLLATERAL_TOKEN_ADDRESS' const requirements = await morpho.getSupplyCollateralRequirements({ token: COLLATERAL, amount: 1000000000000000000n }) console.log('Collateral requirements:', requirements) const tx = await morpho.supplyCollateral({ token: COLLATERAL, amount: 1000000000000000000n }) console.log('Collateral supply tx hash:', tx.hash) ``` Withdraw collateral with [`withdrawCollateral()`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Withdraw collateral" const COLLATERAL = 'COLLATERAL_TOKEN_ADDRESS' const tx = await morpho.withdrawCollateral({ token: COLLATERAL, amount: 1000000000000000000n }) console.log('Collateral withdrawal tx hash:', tx.hash) ``` The collateral token must match the configured market collateral token. If you pass `to`, it must equal the connected wallet address. ## Borrow Borrow from the configured market with [`borrow()`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Borrow USDT" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const requirements = await morpho.getBorrowRequirements({ token: USDT, amount: 1000000n }) console.log('Borrow requirements:', requirements) const tx = await morpho.borrow({ token: USDT, amount: 1000000n }) console.log('Borrow tx hash:', tx.hash) ``` The borrow token must match the configured market loan token. ## Repay Repay by asset amount, or pass `amount: 'max'` to repay current borrow shares: ```javascript title="Repay max borrow shares" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const requirements = await morpho.getRepayRequirements({ token: USDT, amount: 'max' }) console.log('Repay requirements:', requirements) const tx = await morpho.repay({ token: USDT, amount: 'max' }) console.log('Repay tx hash:', tx.hash) ``` For `amount: "max"`, Morpho repays borrow shares. The loan-token transfer amount returned by `getRepayRequirements()` is computed from live market state, so accrued interest can make a delayed approval or permit insufficient. Re-run `getRepayRequirements({ amount: "max" })` immediately before `repay()`, or approve `getChainAddresses(chainId).bundler3.generalAdapter1` with a small buffer above current debt and pass a non-zero `slippageTolerance`. `slippageTolerance` caps the repay share price or transfer amount; it is not approval slippage. Residual loan tokens pulled in shares mode are skimmed back to the user. The repay token must match the configured market loan token. ## Requirements Morpho SDK actions can require approvals, Permit or Permit2 signatures, or Morpho authorization before the final action. | Requirement source | Final action | |--------------------|--------------| | `getSupplyRequirements()` | `supply()` | | `getSupplyCollateralRequirements()` | `supplyCollateral()` | | `getBorrowRequirements()` | `borrow()` | | `getRepayRequirements()` | `repay()` | For EOA accounts, send returned transaction requirements before the final operation. For signature requirements, call the returned requirement's `sign(client, userAddress)` method and pass the result as `requirementSignature`. ```javascript title="Resolve EOA requirements before the final action" async function resolveRequirements({ account, walletClient, userAddress, requirements }) { let requirementSignature for (const requirement of requirements) { if (typeof requirement.sign === 'function') { requirementSignature = await requirement.sign(walletClient, userAddress) continue } await account.sendTransaction({ to: requirement.to, value: requirement.value, data: requirement.data }) } return requirementSignature } const requirements = await morpho.getSupplyRequirements({ token: USDT, amount: 1000000n }) const requirementSignature = await resolveRequirements({ account, walletClient, userAddress, requirements }) const tx = await morpho.supply({ token: USDT, amount: 1000000n, requirementSignature }) ``` Create `walletClient` with `viem` using the same signer and address as the WDK EVM account. Borrow requirements are authorization transactions, so send them before `borrow()`: ```javascript title="Resolve borrow authorization requirements" const requirements = await morpho.getBorrowRequirements({ token: USDT, amount: 1000000n }) for (const requirement of requirements) { await account.sendTransaction({ to: requirement.to, value: requirement.value, data: requirement.data }) } await morpho.borrow({ token: USDT, amount: 1000000n }) ``` For ERC-4337 accounts, you can batch returned transaction requirements with your account-level flow when supported by the wallet module. Signature requirements still need to be signed before the final action. Morpho SDK enforces builder and executor invariants for bundled actions. In this WDK adapter, `onBehalfOf` and vault or collateral withdrawal `to` must equal the connected wallet address when set. ## Quotes before sending Quote helpers build the target transaction and return the account-level fee estimate without sending it: ```javascript title="Quote Morpho operations" const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' const COLLATERAL = 'COLLATERAL_TOKEN_ADDRESS' const supplyQuote = await morpho.quoteSupply({ token: USDT, amount: 1000000n }) const withdrawQuote = await morpho.quoteWithdraw({ token: USDT, amount: 1000000n }) const collateralQuote = await morpho.quoteSupplyCollateral({ token: COLLATERAL, amount: 1000000000000000000n }) const borrowQuote = await morpho.quoteBorrow({ token: USDT, amount: 1000000n }) const repayQuote = await morpho.quoteRepay({ token: USDT, amount: 'max' }) console.log({ supplyQuote, withdrawQuote, collateralQuote, borrowQuote, repayQuote }) ``` Quotes do not guarantee that a later transaction will succeed if balances, allowances, authorization, market state, or vault state change. ## ERC-4337 smart accounts You can use the same methods with [`WalletAccountEvmErc4337`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) and pass a second `config` argument for per-call gas payment overrides. ```javascript title="Supply with an ERC-4337 paymaster override" const result = await morpho.supply( { token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', amount: 1000000n }, { paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } } ) console.log('Supply hash:', result.hash) ``` Use token addresses that exist on the same chain as the smart account RPC. ## Reading positions Read the configured vault position with [`getVaultPosition()`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Read configured vault position" const vaultPosition = await morpho.getVaultPosition() console.log({ shares: vaultPosition.shares, assets: vaultPosition.assets, vaultAddress: vaultPosition.vaultAddress }) ``` Read the configured market position with [`getMarketPosition()`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Read configured market position" const marketPosition = await morpho.getMarketPosition() console.log({ supplyShares: marketPosition.supplyShares, borrowShares: marketPosition.borrowShares, borrowAssets: marketPosition.borrowAssets, collateral: marketPosition.collateral, marketId: marketPosition.marketId }) ``` Read both configured positions with [`getAccountData()`](/sdk/lending-modules/lending-morpho-evm/api-reference): ```javascript title="Read combined Morpho account data" const data = await morpho.getAccountData() console.log({ vaultAssets: data.vaultAssets, marketBorrowAssets: data.marketBorrowAssets, collateral: data.collateral, vaultAddress: data.vaultAddress, marketId: data.marketId }) ``` ## Next Steps - [Handle errors](handle-errors) - [Get started](get-started) - [API reference](/sdk/lending-modules/lending-morpho-evm/api-reference) *** ## Lending Morpho EVM Guides URL: https://docs.wdk.tether.io/sdk/lending-modules/lending-morpho-evm/usage Description: How to install and use @morpho-org/wdk-protocol-lending-morpho-evm on EVM # Usage The [@morpho-org/wdk-protocol-lending-morpho-evm](https://www.npmjs.com/package/@morpho-org/wdk-protocol-lending-morpho-evm) community module exposes Morpho Vault V2 and Morpho Blue operations for WDK-compatible EVM accounts. Follow the guides below for setup, lending operations, and error handling. Install the package, create MorphoProtocolEvm, and review prerequisites. Supply, withdraw, manage collateral, borrow, repay, quotes, requirements, and position reads. Handle target, token, requirement, and transaction failures. Get started with WDK in a Node.js environment Presets, explicit targets, and Morpho SDK options Methods and parameters for MorphoProtocolEvm *** ## Swap Modules Overview URL: https://docs.wdk.tether.io/sdk/swap-modules Description: Explore WDK swap modules for token swap integrations across supported providers. The Swap Development Kit (WDK) provides a set of modules that support swap on top of multiple blockchain networks. All modules share a common interface, ensuring consistent behavior across different blockchain implementations. WDK is introducing the [swidge interface](/sdk/swidge-modules) as the preferred interface for new swap, bridge, and combined route providers. Existing swap modules remain supported, but new protocol integrations should prefer swidge because the standalone swap interface is expected to be deprecated in a future release once swidge provider coverage is available. ## Swap Protocol Modules DeFi swap functionality for token exchanges across different DEXs: | Module | Blockchain | Status | Documentation | |--------|------------|--------|---------------| | [`@tetherto/wdk-protocol-swap-velora-evm`](https://github.com/tetherto/wdk-protocol-swap-velora-evm) | EVM | ✅ Ready | [Documentation](/sdk/swap-modules/swap-velora-evm/) | | `@tetherto/wdk-protocol-swap-stonfi-ton` | TON | In progress | Coming soon | ## Next steps To get started with WDK modules, follow these steps: 1. Get up and running quickly with our [Quickstart Guide](/start-building/nodejs-bare-quickstart) 2. Choose the modules that best fit your needs from the tables above 3. Check specific documentation for modules you wish to use You can also: - Learn about key concepts like [Account Abstraction](/resources/concepts#account-abstraction) and other important definitions - Use one of our ready-to-use examples to be production ready *** ## Swap velora EVM Overview URL: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm Description: Overview of the @tetherto/wdk-protocol-swap-velora-evm module A lightweight package that lets EVM wallet accounts swap tokens using the velora aggregator. It provides a clean SDK for token swaps on EVM chains and works with both standard wallets and ERC‑4337 smart accounts. ## Features - **Token Swapping**: Execute token swaps through velora on supported EVM networks - **Account Abstraction**: Compatible with standard EVM accounts and ERC‑4337 smart accounts - **Fee Controls**: Optional `swapMaxFee` to cap gas costs - **Allowance Safety**: Handles USD₮ mainnet pattern (reset allowance to 0 before approve) - **Provider Flexibility**: Works with JSON‑RPC URLs and EIP‑1193 providers - **TypeScript Support**: Full TypeScript definitions included ## Supported Networks Works with EVM networks supported by velora (e.g., Ethereum, Polygon, Arbitrum, etc.). A working RPC provider is required. ## Wallet Compatibility The swap service supports multiple EVM wallet types: - **Standard EVM Wallets**: `@tetherto/wdk-wallet-evm` accounts - **ERC‑4337 Smart Accounts**: `@tetherto/wdk-wallet-evm-erc-4337` accounts with bundler/paymaster - **Read‑Only Accounts**: For quoting swaps without sending transactions ## Key Components - **velora Integration**: Uses velora aggregator for routing and quotes - **Quote System**: Pre‑transaction fee and amount estimation via `quoteSwap` - **AA Integration**: Optional paymaster token and fee cap overrides when using ERC‑4337 - **Allowance Management**: Approve flow handled automatically when required ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's velora Swap Protocol configuration Get started with WDK's velora Swap Protocol API Get started with WDK's velora Swap Protocol usage *** ## Need Help? *** ## API Reference URL: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm/api-reference Description: API Reference for @tetherto/wdk-protocol-swap-velora-evm ## Class: VeloraProtocolEvm Main class for velora token swaps on EVM. ### Constructor ```javascript new VeloraProtocolEvm(account, config?) ``` Parameters: - `account`: `WalletAccountEvm | WalletAccountReadOnlyEvm | WalletAccountEvmErc4337 | WalletAccountReadOnlyEvmErc4337` - `config` (optional): - `swapMaxFee` (`bigint`): maximum total gas fee allowed (wei) Example: ```javascript const swap = new VeloraProtocolEvm(account, { swapMaxFee: 200000000000000n }) ``` ### Methods | Method | Description | Returns | |--------|-------------|---------| | `swap(options, config?)` | Perform a token swap | `Promise<{hash: string, fee: bigint, tokenInAmount: bigint, tokenOutAmount: bigint, approveHash?: string, resetAllowanceHash?: string}>` | | `quoteSwap(options, config?)` | Get estimated fee and amounts | `Promise<{fee: bigint, tokenInAmount: bigint, tokenOutAmount: bigint}>` | --- ### `swap(options, config?)` Execute a swap via velora. Options: - `tokenIn` (`string`): Address of the ERC‑20 token to sell - `tokenOut` (`string`): Address of the ERC‑20 token to buy - `tokenInAmount` (`bigint`, optional): Exact input amount (base units) - `tokenOutAmount` (`bigint`, optional): Exact output amount (base units) - `to` (`string`, optional): Recipient address (defaults to account address) Config (ERC‑4337 only): - `paymasterToken` (`string`, optional): Token symbol/address for fee sponsorship - `swapMaxFee` (`bigint`, optional): Per‑swap fee cap (wei) Returns: - Standard account: `{ hash, fee, tokenInAmount, tokenOutAmount, approveHash?, resetAllowanceHash? }` - ERC‑4337 account: `{ hash, fee, tokenInAmount, tokenOutAmount }` (approve may be bundled) Notes: - On Ethereum mainnet, selling USD₮ may first set allowance to 0, then approve. - Requires a provider; requires a non read‑only account to send transactions. Example: ```javascript const tx = await swap.swap({ tokenIn: '0xdAC17F...ec7', // USD₮ tokenOut: '0xC02a...6Cc2', // WETH tokenInAmount: 1000000n }) ``` --- ### `quoteSwap(options, config?)` Get estimated fee and token in/out amounts. Options are the same as `swap`. Returns: `{ fee, tokenInAmount, tokenOutAmount }` Config (ERC‑4337 only): - `paymasterToken` (`string`, optional): Token symbol/address for fee sponsorship Works with read‑only accounts. Example: ```javascript const quote = await swap.quoteSwap({ tokenIn: '0xdAC17F...ec7', // USD₮ tokenOut: '0xC02a...6Cc2', // WETH tokenOutAmount: 500000000000000000n // 0.5 WETH }) ``` --- ## Errors Common errors include: - Insufficient liquidity / no route for pair - Fee exceeds `swapMaxFee` - Read‑only account cannot send swaps - Provider/RPC errors (invalid endpoint, network mismatch) --- ## Types - `swapMaxFee: bigint` — Upper bound for gas fees (wei) - `tokenInAmount/tokenOutAmount: bigint` — ERC‑20 base units - `paymasterToken: string` — token symbol or address (AA only) Get started with WDK in a Node.js environment Get started with WDK's Swap velora EVM Protocol configuration Get started with WDK's Swap velora EVM Protocol usage *** ## Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm/configuration Description: Configuration options and settings for @tetherto/wdk-protocol-swap-velora-evm ## Swap Service Configuration The `VeloraProtocolEvm` accepts a configuration object that defines fee controls and behavior: ```javascript import VeloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' // Create wallet account first const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) // Create swap service with configuration const swapProtocol = new VeloraProtocolEvm(account, { swapMaxFee: 200000000000000n // Optional: Max swap fee in wei }) ``` ## Account Configuration The swap service uses the wallet account configuration for network access and signing: ```javascript import { WalletAccountEvm, WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm' // Full access account const account = new WalletAccountEvm( seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' } ) // Read-only account (quotes only) const readOnly = new WalletAccountReadOnlyEvm( '0xYourAddress', { provider: 'https://ethereum-rpc.publicnode.com' } ) // Create swap service const swapProtocol = new VeloraProtocolEvm(account, { swapMaxFee: 200000000000000n }) ``` ## Configuration Options ### Swap Max Fee The `swapMaxFee` option sets an upper bound for total gas costs to prevent excessive fees. **Type:** `bigint` (optional) **Unit:** Wei **Examples:** ```javascript const config = { // Cap total gas fee to 0.0002 ETH (in wei) swapMaxFee: 200000000000000n, } // Usage example try { const result = await swapProtocol.swap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USD₮ (6 decimals) tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH (18 decimals) tokenInAmount: 1000000n }) } catch (error) { if (error.message.includes('max fee')) { console.error('Swap stopped: Fee too high') } } ``` ## ERC‑4337 (Account Abstraction) Configuration When using ERC‑4337 smart accounts (`@tetherto/wdk-wallet-evm-erc-4337`), you can override fee behavior per swap and specify a paymaster token: ```javascript import { WalletAccountEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337' const aa = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", { chainId: 1, provider: 'https://arb1.arbitrum.io/rpc', bundlerUrl: 'YOUR_BUNDLER_URL', paymasterUrl: 'YOUR_PAYMASTER_URL' }) const swapAA = new VeloraProtocolEvm(aa, { swapMaxFee: 200000000000000n }) const result = await swapAA.swap({ tokenIn: '0xTokenIn', tokenOut: '0xTokenOut', tokenInAmount: 1000000n }, { paymasterToken: 'USDT', // Token used to pay for gas swapMaxFee: 200000000000000n // Per‑swap override }) ``` ### Paymaster Token (ERC‑4337) The `paymasterToken` option indicates which token the paymaster should use to sponsor gas. **Type:** `string` (optional) **Format:** Token symbol or address **Example:** ```javascript const result = await swapAA.swap({ tokenIn: '0xdAC17F...ec7', tokenOut: '0xC02a...6Cc2', // WETH tokenInAmount: 1000000n }, { paymasterToken: 'USDT' }) ``` ## Network Support velora supports multiple EVM networks (e.g., Ethereum, Polygon, Arbitrum). Ensure your account is configured with a valid provider for the target network. ```javascript // Ethereum Mainnet const eth = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) // Polygon const polygon = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://polygon-bor-rpc.publicnode.com' }) ``` ## Swap Options When calling `swap`, provide the swap parameters: ```javascript const swapOptions = { tokenIn: '0xTokenIn', // ERC‑20 to sell tokenOut: '0xTokenOut', // ERC‑20 to buy tokenInAmount: 1000000n, // exact input (base units) // OR // tokenOutAmount: 1000000n, // exact output (base units) to: '0xRecipient' // optional recipient (defaults to your address) } const result = await swapProtocol.swap(swapOptions) ``` ### Parameters - `tokenIn` (`string`): ERC‑20 address to sell - `tokenOut` (`string`): ERC‑20 address to buy - `tokenInAmount` (`bigint`, optional): exact input amount in token base units - `tokenOutAmount` (`bigint`, optional): exact output amount in token base units - `to` (`string`, optional): recipient address (defaults to account address) > Note: Use either `tokenInAmount` OR `tokenOutAmount`, not both. Get started with WDK in a Node.js environment Get started with WDK's velora Swap Protocol API Get started with WDK's velora Swap Protocol usage *** ## Need Help? *** ## Execute Swaps URL: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm/guides/execute-swaps Description: Run exact-input swaps, exact-output swaps, and swaps with ERC-4337 accounts. This guide explains how to run a [basic exact-input swap](#basic-exact-input-swap), an [exact-output swap](#exact-output-swap), and a [swap from an ERC-4337 smart account](#swap-with-erc-4337). You should already have a [`VeloraProtocolEvm`](/sdk/swap-modules/swap-velora-evm/api-reference) instance. Swaps spend tokens and gas on-chain. Use amounts you control and an RPC you trust. ## Basic exact-input swap You can sell an exact amount of the input token using [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference): ```javascript title="Exact input: USDT to WETH" const result = await swapProtocol.swap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }) console.log('Swap transaction hash:', result.hash) console.log('Total fee (wei):', result.fee) console.log('Tokens sold (base units):', result.tokenInAmount) console.log('Tokens bought (base units):', result.tokenOutAmount) ``` ## Exact output swap You can receive an exact amount of the output token by passing `tokenOutAmount` to [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference): ```javascript title="Exact output amount" const result = await swapProtocol.swap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenOutAmount: 500000000000000000n }) console.log('Swap hash:', result.hash) console.log('Tokens sold (base units):', result.tokenInAmount) console.log('Tokens bought (base units):', result.tokenOutAmount) ``` ## Swap with ERC-4337 You can perform a user-operation-backed swap by constructing [`VeloraProtocolEvm`](/sdk/swap-modules/swap-velora-evm/api-reference) with [`WalletAccountEvmErc4337`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) and passing paymaster options to [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference): ```javascript title="Swap with smart account and paymaster" import { WalletAccountEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337' import VeloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' const aa = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", { chainId: 42161, provider: 'https://arb1.arbitrum.io/rpc', bundlerUrl: process.env.BUNDLER_URL, paymasterUrl: process.env.PAYMASTER_URL }) const swapAA = new VeloraProtocolEvm(aa, { swapMaxFee: 200000000000000n }) const result = await swapAA.swap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }, { paymasterToken: 'USDT', swapMaxFee: 200000000000000n }) console.log('Swap hash:', result.hash) console.log('Total fee (wei):', result.fee) ``` Token addresses must match the chain your account uses (for example, mainnet USD₮ addresses differ from Arbitrum). ## Next Steps - [Get swap quotes](/sdk/swap-modules/swap-velora-evm/guides/get-swap-quotes) before sending - [Handle errors](/sdk/swap-modules/swap-velora-evm/guides/handle-errors) - [Get started](/sdk/swap-modules/swap-velora-evm/guides/get-started) if you still need setup *** ## Get Started URL: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm/guides/get-started Description: Install the package, create VeloraProtocolEvm, and learn supported networks. This guide covers [installation](#installation), [create the swap protocol](#create-the-swap-protocol), and [supported networks](#supported-networks). You need [Node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/) to follow along. ## Installation Run the following to install [@tetherto/wdk-protocol-swap-velora-evm](https://www.npmjs.com/package/@tetherto/wdk-protocol-swap-velora-evm): ```bash title="Install with npm" npm install @tetherto/wdk-protocol-swap-velora-evm ``` You also need an EVM wallet account from [`@tetherto/wdk-wallet-evm`](https://www.npmjs.com/package/@tetherto/wdk-wallet-evm) (or an ERC-4337 account from [`@tetherto/wdk-wallet-evm-erc-4337`](https://www.npmjs.com/package/@tetherto/wdk-wallet-evm-erc-4337)) on the same chain as your RPC provider. ## Create the swap protocol You can construct a swap client with [`new VeloraProtocolEvm(account, config?)`](/sdk/swap-modules/swap-velora-evm/api-reference) on top of [`VeloraProtocolEvm`](/sdk/swap-modules/swap-velora-evm/api-reference): ```javascript title="Create VeloraProtocolEvm" import VeloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm' const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://ethereum-rpc.publicnode.com' }) const swapProtocol = new VeloraProtocolEvm(account, { swapMaxFee: 200000000000000n }) ``` Optional `swapMaxFee` caps the total gas fee in wei for swaps. See [configuration](/sdk/swap-modules/swap-velora-evm/configuration) for environment-specific settings. ## Supported networks Velora routing works on EVM networks the aggregator supports, including **Ethereum**, **Polygon**, **Arbitrum**, and other chains where Velora exposes liquidity. Use an RPC endpoint for the network your [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference) is configured for so [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference) and [`quoteSwap()`](/sdk/swap-modules/swap-velora-evm/api-reference) target the correct chain. ## Next Steps - [Execute swaps](/sdk/swap-modules/swap-velora-evm/guides/execute-swaps) - [Get swap quotes](/sdk/swap-modules/swap-velora-evm/guides/get-swap-quotes) - [Handle errors](/sdk/swap-modules/swap-velora-evm/guides/handle-errors) *** ## Get Swap Quotes URL: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm/guides/get-swap-quotes Description: Estimate fees and amounts with quoteSwap before executing a swap. This guide shows how to [quote before swapping](#quote-before-swapping) and use quotes for [fee estimation](#fee-estimation). Quotes use [`quoteSwap()`](/sdk/swap-modules/swap-velora-evm/api-reference), which works with read-only accounts as well as signing accounts. ## Quote before swapping You can preview fee and token amounts for the same parameters you would pass to [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference) using [`quoteSwap()`](/sdk/swap-modules/swap-velora-evm/api-reference): ```javascript title="Quote exact input swap" const quote = await swapProtocol.quoteSwap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }) console.log('Estimated fee (wei):', quote.fee) console.log('Tokens in (base units):', quote.tokenInAmount) console.log('Tokens out (base units):', quote.tokenOutAmount) ``` You can quote an exact-output style trade the same way by passing `tokenOutAmount` instead of `tokenInAmount` to [`quoteSwap()`](/sdk/swap-modules/swap-velora-evm/api-reference): ```javascript title="Quote exact output swap" const quote = await swapProtocol.quoteSwap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenOutAmount: 500000000000000000n }) console.log('Estimated fee (wei):', quote.fee) console.log('Required token in (base units):', quote.tokenInAmount) ``` ## Fee estimation You can read `quote.fee` from [`quoteSwap()`](/sdk/swap-modules/swap-velora-evm/api-reference) as the estimated total swap fee in wei before calling [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference): ```javascript title="Quote fee before deciding" const quote = await swapProtocol.quoteSwap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }) const maxFee = 200000000000000n console.log('Quoted fee (wei):', quote.fee, 'cap:', maxFee) ``` You can compare that estimate to `swapMaxFee` on [`VeloraProtocolEvm`](/sdk/swap-modules/swap-velora-evm/api-reference) and only then call [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference) when the quote is within your cap: ```javascript title="Swap when fee is under cap" const maxFee = 200000000000000n const quote = await swapProtocol.quoteSwap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }) if (quote.fee <= maxFee) { const result = await swapProtocol.swap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }) console.log('Swap hash:', result.hash) } ``` On-chain conditions can change between quote and execution. The executed [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference) may still differ slightly from the last [`quoteSwap()`](/sdk/swap-modules/swap-velora-evm/api-reference) result. ## Next Steps - [Execute swaps](/sdk/swap-modules/swap-velora-evm/guides/execute-swaps) - [Handle errors](/sdk/swap-modules/swap-velora-evm/guides/handle-errors) - [Get started](/sdk/swap-modules/swap-velora-evm/guides/get-started) *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm/guides/handle-errors Description: Catch swap failures, interpret common messages, and clean up sensitive state. This guide covers [swap errors](#swap-errors), [quote errors](#quote-errors), and [best practices](#best-practices) for clearing wallet material after use. ## Swap errors You can detect failed swaps by wrapping [`swap()`](/sdk/swap-modules/swap-velora-evm/api-reference) in `try/catch` and inspecting `error.message`: ```javascript title="Handle swap failures" try { const result = await swapProtocol.swap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }) console.log('Swap successful:', result.hash) } catch (error) { console.error('Swap failed:', error.message) if (error.message.includes('liquidity')) { console.log('No route or insufficient liquidity for this pair') } if (error.message.includes('max fee')) { console.log('Swap fee exceeds swapMaxFee') } if (error.message.includes('read-only')) { console.log('Read-only account cannot swap') } } ``` Match string fragments only as a convenience; production apps should prefer stable error codes from your runtime when available. ## Quote errors You can handle failures from [`quoteSwap()`](/sdk/swap-modules/swap-velora-evm/api-reference) the same way, including provider or routing errors: ```javascript title="Handle quote failures" try { const quote = await swapProtocol.quoteSwap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }) console.log('Quoted fee (wei):', quote.fee) } catch (error) { console.error('Quote failed:', error.message) } ``` Common causes are listed under [Errors](/sdk/swap-modules/swap-velora-evm/api-reference) in the API reference (liquidity, fee cap, read-only send attempts, RPC issues). ## Best Practices You can clear signing material when a session ends by calling [`dispose()`](/sdk/wallet-modules/wallet-evm/api-reference) on each [`WalletAccountEvm`](/sdk/wallet-modules/wallet-evm/api-reference), or [`dispose()`](/sdk/wallet-modules/wallet-evm/api-reference) on [`WalletManagerEvm`](/sdk/wallet-modules/wallet-evm/api-reference) if you use a manager: ```javascript title="Dispose wallet accounts" try { await swapProtocol.swap({ tokenIn: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenOut: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tokenInAmount: 1000000n }) } finally { account.dispose() } ``` If you use an ERC-4337 account, call [`dispose()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) on that account type per its API reference. Drop references to your [`VeloraProtocolEvm`](/sdk/swap-modules/swap-velora-evm/api-reference) instance when you no longer need it. ## Next Steps - [Get swap quotes](/sdk/swap-modules/swap-velora-evm/guides/get-swap-quotes) - [Execute swaps](/sdk/swap-modules/swap-velora-evm/guides/execute-swaps) - [API reference](/sdk/swap-modules/swap-velora-evm/api-reference) *** ## Swap velora EVM Guides URL: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm/usage Description: How to install and use @tetherto/wdk-protocol-swap-velora-evm for swapping tokens on EVM # Usage The [@tetherto/wdk-protocol-swap-velora-evm](https://www.npmjs.com/package/@tetherto/wdk-protocol-swap-velora-evm) module routes ERC-20 swaps on EVM chains through Velora. Use the guides below for setup, execution, quotes, and error handling. Install the package, create VeloraProtocolEvm, and review supported networks. Exact-input and exact-output swaps, including ERC-4337 smart accounts. Quote before swapping and compare fees to your max fee cap. Handle swap and quote failures and dispose wallet state safely. Get started with WDK in a Node.js environment RPC, fee limits, and environment settings for the Velora swap protocol Methods, options, and error notes for VeloraProtocolEvm *** ## Swidge Protocol Interface URL: https://docs.wdk.tether.io/sdk/swidge-modules Description: Learn the preferred WDK swidge interface for swap-only, bridge-only, and combined asset routes. The swidge interface is WDK's preferred route interface for protocol providers that can quote and execute asset movement. A swidge route moves from one token and chain to another token and chain. The provider may fulfill that route as a swap-only route, a bridge-only route, a combined swap and bridge route, an intent, a solver route, or an aggregator route. Use swidge for new protocol integrations. Existing standalone swap and bridge interfaces remain supported for released modules, but WDK plans to deprecate those standalone interfaces in a future release after swidge provider coverage is available. ## When to use swidge Use a swidge provider when a route matches any of these shapes: - Swap one token into another token on the same chain. - Bridge the same token from one chain to another chain. - Swap and bridge in one route. - Route between different tokens, different chains, or both. Use the existing [swap modules](/sdk/swap-modules) and [bridge modules](/sdk/bridge-modules) docs when maintaining released modules that have not shipped a swidge implementation yet. For new provider modules, prefer the swidge interface. ## Interface methods Concrete swidge provider modules extend `SwidgeProtocol` from `@tetherto/wdk-wallet/protocols`. The base interface exposes discovery, quote, execution, and status methods: | Method | Description | |--------|-------------| | `quoteSwidge(options)` | Quotes the route before execution. | | `swidge(options, config?)` | Executes a swidge operation. This is a write method and must be user-confirmed. | | `getSwidgeStatus(id, options?)` | Looks up the status of an in-flight swidge execution. | | `getSupportedChains()` | Lists the provider's supported chains for swidge operations. | | `getSupportedTokens(options?)` | Lists supported tokens, optionally filtered by chain or route context. | ```typescript title="Swidge protocol shape" interface ISwidgeProtocol { quoteSwidge(options: SwidgeOptions): Promise swidge( options: SwidgeOptions, config?: SwidgeProtocolConfig ): Promise getSwidgeStatus( id: string, options?: SwidgeStatusOptions ): Promise getSupportedChains(): Promise getSupportedTokens( options?: SwidgeSupportedTokensOptions ): Promise } ``` ## Discover supported chains and tokens Use `getSupportedChains()` and `getSupportedTokens()` before building route pickers or validating a requested route. These methods are read-only discovery calls; they do not quote or execute a transaction. ```typescript title="Discover swidge support" const chains = await swidge.getSupportedChains() const ethereumTokens = await swidge.getSupportedTokens({ fromChain: 'ethereum' }) const routeTokens = await swidge.getSupportedTokens({ fromChain: 'ethereum', fromToken: '0xSourceToken...', toChain: 'arbitrum' }) ``` `getSupportedChains()` returns provider-specific chain identifiers plus display metadata. `getSupportedTokens(options?)` returns provider-specific token identifiers, chain identifiers, symbols, decimals, and optional token addresses. Use the returned `token` and `chain` values when calling `quoteSwidge()` unless the provider module documents a stricter identifier format. | Discovery type | Key fields | |----------------|------------| | `SwidgeSupportedChain` | `id`, `name`, `type`, `nativeToken` | | `SwidgeSupportedToken` | `token`, `chain`, `symbol`, `decimals`, optional `address`, optional `name` | | `SwidgeSupportedTokensOptions` | Optional `fromChain`, `fromToken`, and `toChain` filters | ## Quote a route Call `quoteSwidge()` before execution so the user can review source amount, expected destination amount, minimum output, fees, and quote expiry. ```typescript title="Quote a swidge route" const quote = await swidge.quoteSwidge({ fromToken: '0xSourceToken...', toToken: '0xDestinationToken...', toChain: 'arbitrum', recipient: '0xRecipient...', fromTokenAmount: 1000000n, slippage: 0.01 }) console.log('Expected output:', quote.toTokenAmount) console.log('Minimum output:', quote.toTokenAmountMin) console.log('Fees:', quote.fees) ``` `SwidgeOptions` supports exact-in and exact-out routes: | Option | Type | Description | |--------|------|-------------| | `fromToken` | `string` | Source token address or provider-specific asset identifier. | | `toToken` | `string` | Destination token address or provider-specific asset identifier. | | `toChain` | `string \| number` | Optional destination chain identifier. If omitted, it defaults to the source chain for same-chain swaps. | | `recipient` | `string` | Optional recipient for the output tokens. | | `refundAddress` | `string` | Optional address that receives refunds if the transaction cannot complete. | | `slippage` | `number` | Optional decimal slippage tolerance, for example `0.01` for 1%. | | `fromTokenAmount` | `number \| bigint` | Exact source amount to spend. Do not pass with `toTokenAmount`. | | `toTokenAmount` | `number \| bigint` | Exact destination amount to receive. Do not pass with `fromTokenAmount`. | ## Execute a route Call `quoteSwidge()` first, show the quote to the user, then call `swidge()` with the same route options after explicit confirmation. Apply fee caps with `SwidgeProtocolConfig` where supported by the provider. ```typescript title="Execute a swidge route" const options = { fromToken: '0xSourceToken...', toToken: '0xDestinationToken...', toChain: 'arbitrum', recipient: '0xRecipient...', fromTokenAmount: 1000000n, slippage: 0.01 } const quote = await swidge.quoteSwidge(options) // Show quote details and ask for confirmation before continuing. const result = await swidge.swidge(options, { maxNetworkFeeBps: 50, maxProtocolFeeBps: 25 }) console.log('Swidge ID:', result.id) console.log('Primary transaction:', result.hash) console.log('Transactions:', result.transactions) ``` `swidge()` can submit one or more transactions. Ask for explicit user confirmation before calling it, and display the quote amounts, fee breakdown, route, and recipient first. `SwidgeProtocolConfig` supports fee limits expressed in basis points of the input amount: | Config field | Type | Description | |--------------|------|-------------| | `maxNetworkFeeBps` | `number \| bigint` | Maximum acceptable network fee in basis points of the input amount. | | `maxProtocolFeeBps` | `number \| bigint` | Maximum acceptable protocol fee in basis points of the input amount. | ## Track status Use `getSwidgeStatus()` when the provider returns an execution ID and the final destination result may settle asynchronously. ```typescript title="Check swidge status" const status = await swidge.getSwidgeStatus(result.id, { fromChain: 'ethereum', toChain: 'arbitrum' }) if (status.status === 'completed') { console.log('Swidge complete') } ``` Supported statuses are: | Status | Meaning | |--------|---------| | `pending` | The route has started and is waiting for progress. | | `action-required` | The user or integrator must take another action. | | `completed` | The route completed successfully. | | `failed` | The route failed. | | `refund-pending` | A refund has started but has not completed. | | `refunded` | The route was refunded. | | `cancelled` | The route was cancelled. | | `expired` | The quote or route expired. | | `partial` | The route partially completed. | ## Result fields `SwidgeQuote` and `SwidgeResult` can include an itemized `fees` array. Prefer displaying the itemized fees when available. Public fee categories are `network`, `protocol`, `affiliate`, and `other`; providers can use the optional fee `description` for route-specific detail. | Field | Type | Description | |-------|------|-------------| | `id` | `string` | Execution identifier returned by `swidge()`. | | `hash` | `string` | Primary transaction hash, if available immediately. | | `transactions` | `SwidgeTransaction[]` | Source, destination, approval, refund, or other transaction hashes. | | `fromTokenAmount` | `bigint` | Actual source amount spent. | | `toTokenAmount` | `bigint` | Actual or expected destination amount. | | `toTokenAmountMin` | `bigint` | Minimum destination amount after slippage. | | `fees` | `SwidgeFee[]` | Itemized fee breakdown. | ## Supported assets and chains Provider modules should still document route shapes, network caveats, token identifier formats, and provider-specific limits on their module pages. Use `getSupportedChains()` and `getSupportedTokens(options?)` for runtime discovery, then consult the provider module docs for behavior that cannot be represented in the shared discovery response. ## Next steps Register and use protocol modules with WDK accounts Maintain existing standalone swap modules Maintain existing standalone bridge modules *** ## Wallet Modules Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules Description: Explore WDK wallet modules for building self-custodial wallets across supported chains. The Wallet Development Kit (WDK) provides a set of modules that support multiple blockchain networks. All modules share a common interface, ensuring consistent behavior across different blockchain implementations. ## Supported Networks This package works with multiple blockchain networks through wallet registration. Bitcoin Mainnet Ethereum, Sepolia Testnet, L2s, etc. Tron Mainnet TON Mainnet Solana Mainnet Spark Mainnet ## Classic Wallet Modules Standard wallet implementations that use native blockchain tokens for transaction fees: | Module | Blockchain | Status | Documentation | |--------|------------|--------|---------------| | [`@tetherto/wdk-wallet-evm`](https://github.com/tetherto/wdk-wallet-evm) | EVM | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-evm) | | [`@tetherto/wdk-wallet-ton`](https://github.com/tetherto/wdk-wallet-ton) | TON | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-ton) | | [`@tetherto/wdk-wallet-btc`](https://github.com/tetherto/wdk-wallet-btc) | Bitcoin | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-btc) | | [`@tetherto/wdk-wallet-spark`](https://github.com/tetherto/wdk-wallet-spark) | Spark | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-spark) | | [`@tetherto/wdk-wallet-tron`](https://github.com/tetherto/wdk-wallet-tron) | TRON | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-tron) | | [`@tetherto/wdk-wallet-solana`](https://github.com/tetherto/wdk-wallet-solana) | Solana | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-solana) | | `@tetherto/wdk-wallet-ark` | Ark | In progress | - | ## Account Abstraction Wallet Modules Wallet implementations that support [Account Abstraction](/resources/concepts#account-abstraction) for gasless transactions using paymaster tokens like USD₮: | Module | Blockchain | Status | Documentation | |--------|------------|--------|---------------| | [`@tetherto/wdk-wallet-evm-erc4337`](https://github.com/tetherto/wdk-wallet-evm-erc-4337) | EVM | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-evm-erc-4337) | | [`@tetherto/wdk-wallet-ton-gasless`](https://github.com/tetherto/wdk-wallet-ton-gasless) | TON | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-ton-gasless) | | [`@tetherto/wdk-wallet-tron-gasfree`](https://github.com/tetherto/wdk-wallet-tron-gasfree) | TRON | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-tron-gasfree) | | `@tetherto/wdk-wallet-solana-jupiterz` | Solana | In progress | - | ## Community Wallet Modules Wallet modules developed by the community. See the [Community Modules](/sdk/community-modules/) page for more details. Community modules are developed and maintained independently. Use your own judgment and proceed at your own risk. | Module | Blockchain | Description | Repository | |--------|------------|-------------|------------| | `@utexo/wdk-wallet-rgb` | Bitcoin (RGB) | RGB protocol wallet integration for Bitcoin-based smart contracts | [GitHub](https://github.com/UTEXO-Protocol/wdk-wallet-rgb) | ## Next Steps To get started with WDK modules, follow these steps: 1. Get up and running quickly with our [Quickstart Guide](/start-building/nodejs-bare-quickstart) 2. Choose the modules that best fit your needs from the tables above 3. Check specific documentation for modules you wish to use You can also: - Learn about key concepts like [Account Abstraction](/resources/concepts#account-abstraction) and other important definitions - Use one of our ready-to-use examples to be production ready *** ## Wallet BTC Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc Description: Overview of the @tetherto/wdk-wallet-btc module A simple and secure package to manage BIP-84 (SegWit) and BIP-44 (Legacy) wallets for the Bitcoin blockchain. This package provides a clean API for creating, managing, and interacting with Bitcoin wallets using BIP-39 seed phrases and Bitcoin-specific derivation paths. **Default Derivation Path Change in v1.0.0-beta.4+** The default derivation path was updated in v1.0.0-beta.4 to use BIP-84 (Native SegWit) instead of BIP-44 (Legacy): - **Previous path** (up to v1.0.0-beta.3): `m/44'/0'/0'/0/{index}` (Legacy addresses) - **Current path** (v1.0.0-beta.4+): `m/84'/0'/0'/0/{index}` (Native SegWit addresses) If you're upgrading from an earlier version, existing wallets created with the old path will generate different addresses. Make sure to migrate any existing wallets or use the old path explicitly if needed for compatibility. Use [`getAccountByPath`](/sdk/wallet-modules/wallet-btc/api-reference#getaccountbypathpath) to supply an explicit derivation path when importing or recreating legacy wallets. ## Features - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases - **Bitcoin Derivation Paths**: Support for BIP-84 (Native SegWit, default) and BIP-44 (Legacy) derivation paths - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase - **Address Types Support**: Generate Native SegWit (P2WPKH) addresses by default, with Legacy (P2PKH) support via configuration - **UTXO Management**: Track and manage unspent transaction outputs - **Offline Transaction Signing**: Sign Bitcoin transactions with `signTransaction()` without broadcasting them - **Transaction Management**: Create, sign, and broadcast Bitcoin transactions (single recipient per transaction) - **Fee Estimation**: Dynamic fee calculation via mempool.space API - **Provider Failover**: Configure ordered Electrum, WebSocket, Blockbook, or custom client fallbacks - **TypeScript Support**: Full TypeScript definitions included - **Memory Safety**: Secure private key management with memory-safe implementation - **Network Flexibility**: Support for mainnet, testnet, and regtest ## Supported Networks This package works with Bitcoin networks: - **Bitcoin Mainnet** (`"bitcoin"`) - **Bitcoin Testnet** (`"testnet"`) - **Bitcoin Regtest** (`"regtest"`) ### Electrum Server Configuration **Important**: While the package defaults to `electrum.blockstream.info:50001` for convenience, **we strongly recommend configuring your own Electrum server** for production use. #### Recommended Approach: **For Production:** - Set up your own Fulcrum server for optimal performance and reliability - Use recent Fulcrum versions that support pagination for high-transaction addresses **For Development/Testing:** - `fulcrum.frznode.com:50001` - Generally faster than default - `electrum.blockstream.info:50001` - Default fallback ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's Bitcoin Wallet configuration Get started with WDK's Bitcoin Wallet API Get started with WDK's Bitcoin Wallet usage *** ### Need Help? *** ## Wallet BTC API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-btc ## Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerBtc](#walletmanagerbtc) | Main class for managing Bitcoin wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountBtc](#walletaccountbtc) | Individual Bitcoin wallet account implementation. Implements `IWalletAccount`. | [Constructor](#constructor-1), [Methods](#methods-1), [Properties](#properties) | | [WalletAccountReadOnlyBtc](#walletaccountreadonlybtc) | Read-only Bitcoin wallet account. Extends `WalletAccountReadOnly` from `@tetherto/wdk-wallet`. | [Constructor](#constructor-2), [Methods](#methods-2) | | [ElectrumTcp](#electrumtcp) | Standard TCP Electrum client. Implements `IBtcClient`. | [Constructor](#constructor-3) | | [ElectrumTls](#electrumtls) | TLS Electrum client. Implements `IBtcClient`. | [Constructor](#constructor-4) | | [ElectrumSsl](#electrumssl) | SSL Electrum client. Implements `IBtcClient`. | [Constructor](#constructor-5) | | [ElectrumWs](#electrumws) | WebSocket Electrum client for browser environments. Implements `IBtcClient`. | [Constructor](#constructor-6), [Methods](#methods-3) | ## WalletManagerBtc The main class for managing Bitcoin wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. #### Constructor ```javascript new WalletManagerBtc(seed, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (BtcWalletConfig, optional): Configuration object - `client` (`IBtcClient | BtcClientDescriptor | Array`, optional): Bitcoin client, descriptor, or ordered failover list - `network` (string, optional): "bitcoin", "testnet", or "regtest" (default: "bitcoin") - `bip` (number, optional): BIP address type - 44 (legacy) or 84 (native SegWit) (default: 84) - `retries` (number, optional): Additional retry attempts when `client` is an array ### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAccount(index)` | Returns a wallet account at the specified index | `Promise` | | `getAccountByPath(path)` | Returns a wallet account at the specified derivation path | `Promise` | | `getFeeRates()` | Returns current fee rates for transactions | `Promise<{normal: bigint, fast: bigint}>` | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory and closing internal Electrum connections | `void` | ##### `getAccount(index)` Returns a wallet account at the specified index using BIP-84 (default) or BIP-44 derivation. **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise` - The wallet account **Example:** ```javascript // Returns the account with derivation path: // For mainnet (bitcoin): m/84'/0'/0'/0/1 // For testnet or regtest: m/84'/1'/0'/0/1 const account = await wallet.getAccount(1) ``` ##### `getAccountByPath(path)` Returns a wallet account at the specified derivation path. **Parameters:** - `path` (string): The derivation path (e.g., "0'/0/0") **Returns:** `Promise` - The wallet account **Example:** ```javascript // Returns the account with derivation path: // For mainnet (bitcoin): m/84'/0'/0'/0/1 // For testnet or regtest: m/84'/1'/0'/0/1 const account = await wallet.getAccountByPath("0'/0/1") ``` ##### `getFeeRates()` Returns current fee rates from mempool.space API. **Returns:** `Promise<{normal: bigint, fast: bigint}>` - Object containing fee rates in sat/vB - `normal`: Standard fee rate for confirmation within ~1 hour - `fast`: Higher fee rate for faster confirmation **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'sat/vB') console.log('Fast fee rate:', feeRates.fast, 'sat/vB') ``` ##### `dispose()` Disposes all wallet accounts, clears sensitive data from memory, and closes internal Electrum connections. **Returns:** `void` **Example:** ```javascript wallet.dispose() ``` ## WalletAccountBtc Represents an individual Bitcoin wallet account. Extends `WalletAccountReadOnlyBtc` and implements `IWalletAccount` from `@tetherto/wdk-wallet`. #### Constructor ```javascript new WalletAccountBtc(seed, path, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `path` (string): Derivation path suffix (e.g., "0'/0/0") - `config` (BtcWalletConfig, optional): Configuration object - `client` (`IBtcClient | BtcClientDescriptor | Array`, optional): Bitcoin client, descriptor, or ordered failover list - `network` (string, optional): "bitcoin", "testnet", or "regtest" (default: "bitcoin") - `bip` (number, optional): BIP address type - 44 (legacy) or 84 (native SegWit) (default: 84) - `retries` (number, optional): Additional retry attempts when `client` is an array ### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's Bitcoin address | `Promise` | | `getBalance()` | Returns the total account balance in satoshis, including unconfirmed funds when present | `Promise` | | `sendTransaction(options, timeoutMs?)` | Sends a Bitcoin transaction and optionally polls until spent inputs disappear from unspent outputs | `Promise<{hash: string, fee: bigint}>` | | `signTransaction(options)` | Signs a Bitcoin transaction without broadcasting it | `Promise` | | `quoteSendTransaction(options)` | Estimates the fee for a transaction | `Promise<{fee: bigint}>` | | `getTransfers(options?)` | Returns the account's transfer history | `Promise` | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise` | | `getMaxSpendable()` | Returns the maximum spendable amount | `Promise` | | `sign(message)` | Signs a message with the account's private key | `Promise` | | `verify(message, signature)` | Verifies a message signature | `Promise` | | `toReadOnlyAccount()` | Creates a read-only version of this account | `Promise` | | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` | ##### `getAddress()` Returns the account's Bitcoin address (Native SegWit bech32 by default, or legacy if using BIP-44). **Returns:** `Promise` - The Bitcoin address **Example:** ```javascript const address = await account.getAddress() console.log('Address:', address) // bc1q... (BIP-84) or 1... (BIP-44) ``` ##### `getBalance()` Returns the account's total balance in satoshis, including unconfirmed funds when present. **Returns:** `Promise` - Balance in satoshis **Example:** ```javascript const balance = await account.getBalance() console.log('Balance:', balance, 'satoshis') ``` ##### `sendTransaction(options, timeoutMs?)` Sends a Bitcoin transaction to a single recipient and optionally polls after broadcast until spent inputs disappear from the unspent-output set. **Parameters:** - `options` (BtcTransaction): Transaction options - `to` (string): Recipient's Bitcoin address - `value` (number | bigint): Amount in satoshis - `feeRate` (number | bigint, optional): Fee rate in sat/vB. If provided, overrides the fee rate estimated from the blockchain. - `confirmationTarget` (number, optional): Target blocks for confirmation (default: 1) - `timeoutMs` (number, optional): Maximum milliseconds to poll after broadcast before returning (default: 10000) **Returns:** `Promise<{hash: string, fee: bigint}>` - `hash`: Transaction hash - `fee`: Transaction fee in satoshis **Example:** ```javascript const result = await account.sendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 50000n }) console.log('Transaction hash:', result.hash) console.log('Fee:', result.fee, 'satoshis') ``` ##### `signTransaction(options)` Signs a Bitcoin transaction and returns the signed raw transaction as a hex string. This method does not broadcast the transaction. **Parameters:** - `options` (BtcTransaction): Transaction options - `to` (string): Recipient's Bitcoin address - `value` (number | bigint): Amount in satoshis - `feeRate` (number | bigint, optional): Fee rate in sat/vB. If provided, overrides the fee rate estimated from the blockchain. - `confirmationTarget` (number, optional): Target blocks for confirmation (default: 1) **Returns:** `Promise` - Signed raw transaction hex string **Example:** ```javascript const signedTransaction = await account.signTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 50000n, feeRate: 10n }) console.log('Signed transaction:', signedTransaction) ``` ##### `quoteSendTransaction(options)` Estimates the fee for a transaction without broadcasting it. **Parameters:** - `options` (BtcTransaction): Same as sendTransaction options - `to` (string): Recipient's Bitcoin address - `value` (number | bigint): Amount in satoshis - `feeRate` (number | bigint, optional): Fee rate in sat/vB. If provided, overrides the fee rate estimated from the blockchain. - `confirmationTarget` (number, optional): Target blocks for confirmation (default: 1) **Returns:** `Promise<{fee: bigint}>` - `fee`: Estimated transaction fee in satoshis **Example:** ```javascript const quote = await account.quoteSendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 50000n }) console.log('Estimated fee:', quote.fee, 'satoshis') ``` ##### `getTransfers(options?)` Returns the account's transfer history with detailed transaction information. **Parameters:** - `options` (object, optional): Filter options - `direction` (string, optional): 'incoming', 'outgoing', or 'all' (default: 'all') - `limit` (number, optional): Maximum number of transfers (default: 10) - `skip` (number, optional): Number of transfers to skip (default: 0) **Returns:** `Promise` - Array of transfer objects - `txid`: Transaction ID - `address`: Account's own address - `vout`: Output index in the transaction - `height`: Block height (0 if unconfirmed) - `value`: Transfer value in satoshis (bigint) - `direction`: 'incoming' or 'outgoing' - `fee`: Transaction fee in satoshis (bigint, for outgoing transfers) - `recipient`: Receiving address (for outgoing transfers) **Example:** ```javascript const transfers = await account.getTransfers({ direction: 'incoming', limit: 5 }) console.log('Recent incoming transfers:', transfers) ``` ##### `getTransactionReceipt(hash)` Returns a transaction's receipt if it has been included in a block. **Parameters:** - `hash` (string): The transaction hash (64 hex characters) **Returns:** `Promise` - The receipt, or null if the transaction has not been included in a block yet. **Example:** ```javascript const receipt = await account.getTransactionReceipt('abc123...') if (receipt) { console.log('Transaction confirmed') } ``` ##### `getMaxSpendable()` Returns the maximum spendable amount that can be sent in a single transaction. The maximum spendable amount can differ from the wallet's total balance for several reasons: - **Transaction fees**: Fees are subtracted from the total balance - **Uneconomic UTXOs**: Small UTXOs where the fee to spend them exceeds their value are excluded - **UTXO limit**: A transaction can include at most 200 inputs. Wallets with more UTXOs cannot spend their full balance in a single transaction. - **Dust limit**: Outputs below the dust threshold (294 sats for SegWit, 546 sats for legacy) cannot be created **Returns:** `Promise` - Maximum spendable result - `amount`: Maximum spendable amount in satoshis (bigint) - `fee`: Estimated network fee in satoshis (bigint) - `changeValue`: Estimated change value in satoshis (bigint) **Example:** ```javascript const { amount, fee } = await account.getMaxSpendable() console.log('Max spendable:', amount, 'satoshis') console.log('Estimated fee:', fee, 'satoshis') ``` ##### `sign(message)` Signs a message using the account's private key. **Parameters:** - `message` (string): Message to sign **Returns:** `Promise` - Signature as base64 string **Example:** ```javascript const signature = await account.sign('Hello Bitcoin!') console.log('Signature:', signature) ``` ##### `verify(message, signature)` Verifies a message signature using the account's public key. **Parameters:** - `message` (string): Original message - `signature` (string): Signature as base64 string **Returns:** `Promise` - True if signature is valid **Example:** ```javascript const isValid = await account.verify('Hello Bitcoin!', signature) console.log('Signature valid:', isValid) ``` ##### `toReadOnlyAccount()` Creates a read-only version of this account that can query balances and transactions but cannot sign or send transactions. **Returns:** `Promise` - Read-only account instance **Example:** ```javascript const readOnlyAccount = await account.toReadOnlyAccount() const balance = await readOnlyAccount.getBalance() ``` ##### `dispose()` Disposes the wallet account, securely erasing the private key from memory and closing the Electrum connection. **Returns:** `void` **Example:** ```javascript account.dispose() // Private key is now securely wiped from memory ``` #### Properties | Property | Type | Description | |----------|------|-------------| | `index` | `number` | The derivation path's index of this account | | `path` | `string` | The full derivation path of this account | | `keyPair` | `KeyPair` | The account's public and private key pair. Treat the returned `Uint8Array` values as read-only views because mutations affect the account's internal key material. | ## WalletAccountReadOnlyBtc Represents a read-only Bitcoin wallet account. Extends `WalletAccountReadOnly` from `@tetherto/wdk-wallet`. #### Constructor ```javascript new WalletAccountReadOnlyBtc(address, config) ``` **Parameters:** - `address` (string): The account's Bitcoin address - `config` (object, optional): Configuration object (same as BtcWalletConfig but without `bip`) - `client` (`IBtcClient | BtcClientDescriptor | Array`, optional): Bitcoin client, descriptor, or ordered failover list - `network` (string, optional): "bitcoin", "testnet", or "regtest" (default: "bitcoin") - `retries` (number, optional): Additional retry attempts when `client` is an array ### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's Bitcoin address | `Promise` | | `getBalance()` | Returns the total account balance in satoshis, including unconfirmed funds when present | `Promise` | | `quoteSendTransaction(options)` | Estimates the fee for a transaction | `Promise<{fee: bigint}>` | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise` | | `getMaxSpendable()` | Returns the maximum spendable amount | `Promise` | | `verify(message, signature)` | Verifies a message signature | `Promise` | | `dispose()` | Closes any internal Electrum connection | `void` | ##### `getAddress()` Returns the account's Bitcoin address. **Returns:** `Promise` - The Bitcoin address **Example:** ```javascript const address = await readOnlyAccount.getAddress() console.log('Address:', address) ``` ##### `getBalance()` Returns the account's confirmed balance in satoshis. **Returns:** `Promise` - Balance in satoshis **Example:** ```javascript const balance = await readOnlyAccount.getBalance() console.log('Balance:', balance, 'satoshis') ``` ##### `quoteSendTransaction(options)` Estimates the fee for a transaction without broadcasting it. **Parameters:** - `options` (BtcTransaction): Transaction options - `to` (string): Recipient's Bitcoin address - `value` (number | bigint): Amount in satoshis - `feeRate` (number | bigint, optional): Fee rate in sat/vB - `confirmationTarget` (number, optional): Target blocks for confirmation (default: 1) **Returns:** `Promise<{fee: bigint}>` - Estimated fee in satoshis **Example:** ```javascript const quote = await readOnlyAccount.quoteSendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 50000n }) console.log('Estimated fee:', quote.fee, 'satoshis') ``` ##### `getTransactionReceipt(hash)` Returns a transaction's receipt if it has been included in a block. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise` - The receipt, or null if not yet included **Example:** ```javascript const receipt = await readOnlyAccount.getTransactionReceipt('abc123...') if (receipt) { console.log('Transaction confirmed') } ``` ##### `getMaxSpendable()` Returns the maximum spendable amount that can be sent in a single transaction. **Returns:** `Promise` - Maximum spendable result - `amount`: Maximum spendable amount in satoshis (bigint) - `fee`: Estimated network fee in satoshis (bigint) - `changeValue`: Estimated change value in satoshis (bigint) **Example:** ```javascript const { amount, fee } = await readOnlyAccount.getMaxSpendable() console.log('Max spendable:', amount, 'satoshis') ``` ##### `verify(message, signature)` Verifies a message signature using the account's public key. **Parameters:** - `message` (string): Original message - `signature` (string): Signature as base64 string **Returns:** `Promise` - True if signature is valid **Example:** ```javascript const isValid = await readOnlyAccount.verify('Hello Bitcoin!', signature) console.log('Signature valid:', isValid) ``` ##### `dispose()` Closes any internal Electrum connection owned by this account. If a [`client`](/sdk/wallet-modules/wallet-btc/configuration#client) was provided via config, the connection is left open (the caller manages its lifecycle). **Returns:** `void` **Example:** ```javascript readOnlyAccount.dispose() ``` ## ElectrumTcp Electrum client using TCP transport. Standard for command-line and server-side environments. Implements `IBtcClient`. #### Constructor ```javascript new ElectrumTcp(config) ``` **Parameters:** - `config` (`Omit`): Configuration options - `host` (string): Electrum server hostname - `port` (number): Electrum server port ## ElectrumTls Electrum client using TLS transport. Implements `IBtcClient`. #### Constructor ```javascript new ElectrumTls(config) ``` **Parameters:** - `config` (`Omit`): Configuration options - `host` (string): Electrum server hostname - `port` (number): Electrum server port ## ElectrumSsl Electrum client using SSL transport. Implements `IBtcClient`. #### Constructor ```javascript new ElectrumSsl(config) ``` **Parameters:** - `config` (`Omit`): Configuration options - `host` (string): Electrum server hostname - `port` (number): Electrum server port ## ElectrumWs Electrum client using WebSocket transport. Compatible with browser environments where TCP sockets are not available. Implements `IBtcClient`. #### Constructor ```javascript new ElectrumWs(config) ``` **Parameters:** - `config` (ElectrumWsConfig): Configuration options - `url` (string): The WebSocket URL (e.g., 'wss://electrum.example.com:50004') ### Methods | Method | Description | Returns | |--------|-------------|---------| | `connect()` | Establishes connection to Electrum server | `Promise` | | `close()` | Closes the connection | `Promise` | | `reconnect()` | Recreates the underlying socket and reinitializes the session | `Promise` | | `getBalance(scripthash)` | Returns balance for a script hash | `Promise` | | `listUnspent(scripthash)` | Returns UTXOs for a script hash | `Promise` | | `getHistory(scripthash)` | Returns transaction history | `Promise` | | `getTransaction(txHash)` | Returns raw transaction hex | `Promise` | | `broadcast(rawTx)` | Broadcasts raw transaction | `Promise` | | `estimateFee(blocks)` | Returns estimated fee rate | `Promise` | ## Types ### BtcTransaction ```typescript interface BtcTransaction { to: string // The transaction's recipient value: number | bigint // The amount of bitcoins to send to the recipient (in satoshis) confirmationTarget?: number // Optional confirmation target in blocks (default: 1) feeRate?: number | bigint // Optional fee rate in satoshis per virtual byte } ``` ### TransactionResult ```typescript interface TransactionResult { hash: string // Transaction hash/ID fee: bigint // Transaction fee in satoshis } ``` ### FeeRates ```typescript interface FeeRates { normal: bigint // Standard fee rate (sat/vB) for ~1 hour confirmation fast: bigint // Higher fee rate (sat/vB) for faster confirmation } ``` ### BtcTransfer ```typescript interface BtcTransfer { txid: string // The transaction's ID address: string // The user's own address vout: number // The index of the output in the transaction height: number // The block height (if unconfirmed, 0) value: bigint // The value of the transfer (in satoshis) direction: 'incoming' | 'outgoing' // The direction of the transfer fee?: bigint // The fee paid for the full transaction (in satoshis) recipient?: string // The receiving address for outgoing transfers } ``` ### BtcMaxSpendableResult ```typescript interface BtcMaxSpendableResult { amount: bigint // The maximum spendable amount in satoshis fee: bigint // The estimated network fee in satoshis changeValue: bigint // The estimated change value in satoshis } ``` ### KeyPair ```typescript interface KeyPair { publicKey: Uint8Array // Public key bytes. Treat as read-only. privateKey: Uint8Array | null // Private key bytes. Treat as read-only. Null after dispose. } ``` ### BtcWalletConfig ```typescript interface BtcWalletConfig { client?: IBtcClient | BtcClientDescriptor | Array // Client, descriptor, or failover list network?: 'bitcoin' | 'testnet' | 'regtest' // Network to use (default: "bitcoin") bip?: 44 | 84 // BIP address type - 44 (legacy) or 84 (native SegWit) (default: 84) retries?: number // Additional retry attempts for client arrays } ``` ### BtcClientDescriptor ```typescript type BtcClientDescriptor = | { type: 'electrum'; clientConfig: MempoolElectrumConfig } | { type: 'electrum-ws'; clientConfig: ElectrumWsConfig } | { type: 'blockbook-http'; clientConfig: BlockbookClientConfig } ``` ### IBtcClient Interface for implementing custom Bitcoin network clients. ```typescript interface IBtcClient { connect(): Promise close(): Promise reconnect(): Promise getBalance(scripthash: string): Promise listUnspent(scripthash: string): Promise getHistory(scripthash: string): Promise getTransaction(txHash: string): Promise broadcast(rawTx: string): Promise estimateFee(blocks: number): Promise } ``` ### ElectrumBalance ```typescript interface ElectrumBalance { confirmed: number // Confirmed balance in satoshis unconfirmed?: number // Unconfirmed balance in satoshis } ``` ### ElectrumUtxo ```typescript interface ElectrumUtxo { tx_hash: string // The transaction hash containing this UTXO tx_pos: number // The output index within the transaction value: number // The UTXO value in satoshis height?: number // The block height (0 if unconfirmed) } ``` ### ElectrumHistoryItem ```typescript interface ElectrumHistoryItem { tx_hash: string // The transaction hash height: number // The block height (0 or negative if unconfirmed) } ``` ### MempoolElectrumConfig ```typescript interface MempoolElectrumConfig { host: string // Electrum server hostname port: number // Electrum server port protocol?: 'tcp' | 'ssl' | 'tls' // Transport protocol (default: 'tcp') maxRetry?: number // Maximum reconnection attempts (default: 2) retryPeriod?: number // Delay between reconnection attempts in ms (default: 1000) pingPeriod?: number // Delay between keep-alive pings in ms (default: 120000) callback?: (err: Error | null) => void // Called when all retries are exhausted } ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Bitcoin Wallet Usage Get started with WDK's Bitcoin Wallet Configuration *** ### Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-btc ## Wallet Configuration ```javascript import WalletManagerBtc from '@tetherto/wdk-wallet-btc' const wallet = new WalletManagerBtc(seedPhrase, { client: { type: 'electrum', clientConfig: { host: 'electrum.blockstream.info', port: 50002, protocol: 'tls' } }, network: 'bitcoin' }) ``` ## Account Creation ```javascript // WalletAccountBtc is created by the WalletManagerBtc // It takes the same configuration as the manager const account = await wallet.getAccount(0) // Get account at index 0 const customAccount = await wallet.getAccountByPath("0'/0/5") // Custom path ``` ## Configuration Options ### Client The `client` option specifies how the wallet connects to Bitcoin network data. It accepts a pre-built `IBtcClient`, a client descriptor, or an ordered array of clients and descriptors for automatic failover. **Type:** `IBtcClient | BtcClientDescriptor | Array` **Default:** Uses an Electrum descriptor for `electrum.blockstream.info:50001`. **Example:** ```javascript const config = { client: { type: 'electrum', clientConfig: { host: 'fulcrum.frznode.com', port: 50002, protocol: 'tls' } } } ``` `BtcClientDescriptor` supports: | Type | Description | |------|-------------| | `electrum` | Creates a TCP, TLS, or SSL Electrum client from `clientConfig`. | | `electrum-ws` | Creates a WebSocket Electrum client from `clientConfig`. | | `blockbook-http` | Creates a stateless Blockbook HTTP client from `clientConfig`. | #### Built-in Transport Clients The package still exports built-in transport clients when you want to instantiate the client yourself: ```javascript import { ElectrumTcp, // TCP transport (default, port 50001) ElectrumTls, // TLS transport (port 50002) ElectrumSsl, // SSL transport (port 50002) ElectrumWs // WebSocket transport } from '@tetherto/wdk-wallet-btc' // TCP (default) const tcpClient = new ElectrumTcp({ host: 'electrum.blockstream.info', port: 50001 }) // TLS const tlsClient = new ElectrumTls({ host: 'electrum.blockstream.info', port: 50002 }) // SSL const sslClient = new ElectrumSsl({ host: 'electrum.blockstream.info', port: 50002 }) // WebSocket const wsClient = new ElectrumWs({ url: 'wss://electrum.example.com:50004' }) ``` #### Custom Bitcoin Client You can implement your own client by implementing `IBtcClient`: ```typescript import { IBtcClient } from '@tetherto/wdk-wallet-btc' class MyCustomBitcoinClient implements IBtcClient { // Implement the required interface methods } const wallet = new WalletManagerBtc(seedPhrase, { client: new MyCustomBitcoinClient(params), network: 'bitcoin' }) ``` #### Client Failover Pass an ordered `client` array to retry connection failures against fallback clients. Set `retries` to control how many additional attempts can happen after the first failed call. ```javascript const wallet = new WalletManagerBtc(seedPhrase, { client: [ { type: 'electrum', clientConfig: { host: 'primary-electrum.example', port: 50002, protocol: 'tls' } }, { type: 'electrum', clientConfig: { host: 'secondary-electrum.example', port: 50002, protocol: 'tls' } } ], retries: 1, network: 'bitcoin' }) ``` ### Host The `host` value belongs inside an `electrum` descriptor's `clientConfig` or an `Electrum*` client constructor. It is not a top-level wallet config option. **Type:** `string` **Default:** `"electrum.blockstream.info"` **Recommended:** Configure your own Electrum server for production use. Public servers can be 10-300x slower and may fail for addresses with many transactions. **Example:** ```javascript const config = { client: { type: 'electrum', clientConfig: { host: 'fulcrum.frznode.com', port: 50002, protocol: 'tls' } } } ``` ### Port The `port` value belongs inside an `electrum` descriptor's `clientConfig` or an `Electrum*` client constructor. It is not a top-level wallet config option. **Type:** `number` **Default:** `50001` **Common Ports:** - `50001` - TCP (default) - `50002` - TLS/SSL - `50003` - WebSocket **Example:** ```javascript const config = { client: { type: 'electrum', clientConfig: { host: 'electrum.blockstream.info', port: 50002, protocol: 'tls' } } } ``` ### Protocol The `protocol` value belongs inside an `electrum` descriptor's `clientConfig`. It is not a top-level wallet config option. **Type:** `string` **Values:** - `"tcp"` - TCP transport (default) - `"tls"` - TLS transport - `"ssl"` - SSL transport **Default:** `"tcp"` **Example:** ```javascript const config = { client: { type: 'electrum', clientConfig: { host: 'electrum.blockstream.info', port: 50002, protocol: 'tls' } } } ``` ### Retries The `retries` option controls failover retry attempts when `client` is an array. **Type:** `number` (optional) **Example:** ```javascript const config = { client: [ { type: 'electrum', clientConfig: { host: 'primary.example', port: 50002, protocol: 'tls' } }, { type: 'electrum', clientConfig: { host: 'secondary.example', port: 50002, protocol: 'tls' } } ], retries: 2 } ``` ### Network The `network` option specifies which Bitcoin network to use. **Type:** `string` **Values:** - `"bitcoin"` - Bitcoin [mainnet](/resources/concepts#mainnet) (production) - `"testnet"` - Bitcoin [testnet](/resources/concepts#testnet) (development) - `"regtest"` - Bitcoin [regtest](/resources/concepts#regtest) (local testing) **Default:** `"bitcoin"` **Example:** ```javascript const config = { network: 'testnet' // Use testnet for development } ``` ### BIP The `bip` option specifies the address type derivation standard to use. **Type:** `number` **Values:** - `84` - [BIP-84](/resources/concepts#bip-84-native-segwit) (P2WPKH / Native SegWit) - addresses start with `bc1` (mainnet) or `tb1` (testnet) - `44` - [BIP-44](/resources/concepts#bip-44-multi-account-hierarchy) (P2PKH / Legacy) - addresses start with `1` (mainnet) or `m`/`n` (testnet) **Default:** `84` **Example:** ```javascript // Use legacy addresses const config = { bip: 44 } ``` ## Electrum Server Configuration **Important**: While the package defaults to `electrum.blockstream.info:50001` for convenience, **we strongly recommend configuring your own Electrum server** for production use. ### Recommended Approach **For Production:** - Set up your own Fulcrum server for optimal performance and reliability - Use recent Fulcrum versions that support pagination for high-transaction addresses **For Development/Testing:** - `fulcrum.frznode.com:50001` - Generally faster than default - `electrum.blockstream.info:50001` - Default fallback ### Configuration Examples ```javascript import { ElectrumTcp, ElectrumTls } from '@tetherto/wdk-wallet-btc' // Production with custom Fulcrum server const productionClient = new ElectrumTls({ host: 'your-fulcrum-server.com', port: 50002 }) const productionWallet = new WalletManagerBtc(seedPhrase, { client: productionClient, network: 'bitcoin' }) // Development with alternative public server const developmentClient = new ElectrumTcp({ host: 'fulcrum.frznode.com', port: 50001 }) const developmentWallet = new WalletManagerBtc(seedPhrase, { client: developmentClient, network: 'bitcoin' }) ``` ### Network-Specific Configuration #### Bitcoin Mainnet ```javascript import { ElectrumTcp } from '@tetherto/wdk-wallet-btc' const client = new ElectrumTcp({ host: 'electrum.blockstream.info', // Or your own server port: 50001 }) const wallet = new WalletManagerBtc(seedPhrase, { client, network: 'bitcoin' }) ``` #### Bitcoin Testnet ```javascript import { ElectrumTcp } from '@tetherto/wdk-wallet-btc' const client = new ElectrumTcp({ host: 'testnet.hsmiths.com', // Example testnet server port: 53011 }) const wallet = new WalletManagerBtc(seedPhrase, { client, network: 'testnet' }) ``` #### Bitcoin Regtest ```javascript import { ElectrumTcp } from '@tetherto/wdk-wallet-btc' const client = new ElectrumTcp({ host: 'localhost', // Local regtest node port: 50001 }) const wallet = new WalletManagerBtc(seedPhrase, { client, network: 'regtest' }) ``` ## Derivation Paths Bitcoin wallet addresses are derived using BIP-32 hierarchical deterministic paths: ### BIP-84 (Native SegWit) - Default - `m/84'/0'/0'/0/0` for mainnet account 0, address 0 - `m/84'/1'/0'/0/0` for testnet/regtest account 0, address 0 Addresses start with `bc1` (mainnet) or `tb1` (testnet). ### BIP-44 (Legacy) - `m/44'/0'/0'/0/0` for mainnet account 0, address 0 - `m/44'/1'/0'/0/0` for testnet/regtest account 0, address 0 Addresses start with `1` (mainnet) or `m`/`n` (testnet). **Default Derivation Path Change in v1.0.0-beta.4+** The default derivation path was updated in v1.0.0-beta.4 to use BIP-84 (Native SegWit) instead of BIP-44 (Legacy): - **Previous path** (up to v1.0.0-beta.3): `m/44'/0'/0'/0/{index}` (Legacy addresses) - **Current path** (v1.0.0-beta.4+): `m/84'/0'/0'/0/{index}` (Native SegWit addresses) If you're upgrading from an earlier version, existing wallets created with the old path will generate different addresses. Make sure to migrate any existing wallets or use the old path explicitly if needed for compatibility. Use [`getAccountByPath`](/sdk/wallet-modules/wallet-btc/api-reference#getaccountbypathpath) to supply an explicit derivation path when importing or recreating legacy wallets. ## Complete Configuration Example ```javascript import WalletManagerBtc, { ElectrumTls } from '@tetherto/wdk-wallet-btc' // Create Electrum client const client = new ElectrumTls({ host: 'your-electrum-server.com', // Replace with your server port: 50002 }) // Create wallet manager with configuration const wallet = new WalletManagerBtc(seedPhrase, { client, network: 'bitcoin', bip: 84 // Native SegWit (default) }) // Get accounts (inherit configuration from manager) const account0 = await wallet.getAccount(0) const account1 = await wallet.getAccount(1) const customAccount = await wallet.getAccountByPath("0'/0/5") // Clean up when done wallet.dispose() ``` ## Performance Considerations **Electrum Server Performance:** - Public servers like Blockstream's can be significantly slower - Addresses with many transactions may cause timeouts - Custom Fulcrum servers provide better performance and reliability - Consider server location and network latency **Configuration Tips:** - Use `fulcrum.frznode.com` for better development performance - Set up your own Fulcrum server for production - Monitor connection stability and implement retry logic - Consider using multiple backup servers Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's BTC Wallet Usage Get started with WDK's BTC Wallet API *** ### Need Help? *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/guides/check-balances Description: Query native BTC balances for owned and read-only accounts. This guide explains how to check [native BTC balances](#native-btc-balance), [maximum spendable amounts](#maximum-spendable-amount), and [read-only account balances](#read-only-account-balances). ## Native BTC Balance You can retrieve the confirmed balance in satoshis using [`account.getBalance()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Get Native BTC Balance" const balance = await account.getBalance() console.log('Total balance:', balance, 'satoshis') ``` On Bitcoin, balances are expressed in satoshis (1 BTC = 100,000,000 satoshis). The [`getBalance()`](/sdk/wallet-modules/wallet-btc/api-reference) method returns the total balance, including unconfirmed funds when present. ## Maximum Spendable Amount You can check the maximum amount available to send in a single transaction using [`account.getMaxSpendable()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Get Maximum Spendable" const { amount, fee } = await account.getMaxSpendable() console.log('Max spendable:', amount, 'satoshis') console.log('Estimated fee:', fee, 'satoshis') ``` The maximum spendable amount can differ from the total balance due to transaction fees, uneconomic UTXOs, the 200-input limit per transaction, and the dust threshold (294 satoshis for SegWit, 546 for legacy). ## Read-Only Account Balances You can check balances for any Bitcoin address without a seed phrase using [`WalletAccountReadOnlyBtc`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Create Read-Only Account" import { WalletAccountReadOnlyBtc, ElectrumTcp } from '@tetherto/wdk-wallet-btc' const client = new ElectrumTcp({ host: 'electrum.blockstream.info', port: 50001 }) const readOnlyAccount = new WalletAccountReadOnlyBtc('bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', { client, network: 'bitcoin' }) ``` You can retrieve the balance from a read-only account using [`readOnlyAccount.getBalance()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Read-Only Balance" const balance = await readOnlyAccount.getBalance() console.log('Read-only account balance:', balance, 'satoshis') ``` Read-only accounts follow the same balance behavior as owned accounts: [`getBalance()`](/sdk/wallet-modules/wallet-btc/api-reference) includes unconfirmed funds when present. You can also create a read-only account from an existing owned account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-btc/api-reference). ## Next Steps With balance checks in place, learn how to [send BTC](/sdk/wallet-modules/wallet-btc/guides/send-transactions). *** ## Get Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/guides/get-started Description: Install and create your first Bitcoin wallet. This guide explains how to [install the package](#1-install-the-package), [create a wallet](#2-create-a-wallet), [get your first account](#3-get-your-first-account), and optionally [convert to read-only](#4-optional-convert-to-read-only). ## 1. Install the Package ### Prerequisites * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ```bash title="Install @tetherto/wdk-wallet-btc" npm install @tetherto/wdk-wallet-btc ``` ## 2. Create a Wallet You can create a new wallet instance using the [`WalletManagerBtc`](/sdk/wallet-modules/wallet-btc/api-reference) constructor with a BIP-39 seed phrase and an Electrum client: ```javascript title="Create Bitcoin Wallet" import WalletManagerBtc, { ElectrumTcp } from '@tetherto/wdk-wallet-btc' const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const client = new ElectrumTcp({ host: 'electrum.blockstream.info', port: 50001 }) const wallet = new WalletManagerBtc(seedPhrase, { client, network: 'bitcoin' }) ``` **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. **Electrum Server Performance:** Public servers like Blockstream's can be 10-300x slower than private servers. For production use, set up your own [Fulcrum](https://github.com/cculianu/Fulcrum) server. For development, consider `fulcrum.frznode.com` as a faster alternative. ## 3. Get Your First Account You can retrieve an account at a given index using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Get Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Wallet address:', address) ``` This implementation uses BIP-84 derivation paths and generates Native SegWit (bech32) addresses by default. Addresses start with `bc1` on mainnet. Set `bip: 44` in config for legacy (P2PKH) addresses. ## 4. (optional) Convert to Read-Only You can convert an owned account to a read-only account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Convert to Read-Only" const readOnlyAccount = await account.toReadOnlyAccount() ``` ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-btc/guides/manage-accounts). *** ## Transaction History URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/guides/get-transaction-history Description: Retrieve and filter Bitcoin transfer history. This guide explains how to [retrieve all transfers](#retrieve-all-transfers), [filter by direction](#filter-by-direction), [paginate results](#paginate-results), and [check transaction receipts](#check-transaction-receipts). ## Retrieve All Transfers You can retrieve the account's transfer history using [`account.getTransfers()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Get All Transfers" const transfers = await account.getTransfers() console.log('Recent transfers:', transfers) ``` The default limit is 10 transfers. Change outputs are automatically filtered out. Transfers are sorted by block height (newest first). ## Filter by Direction You can filter transfers by direction using the `direction` option in [`account.getTransfers()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Incoming Transfers" const incoming = await account.getTransfers({ direction: 'incoming' }) console.log('Incoming transfers:', incoming) ``` You can retrieve outgoing transfers with a custom limit using [`account.getTransfers()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Outgoing Transfers" const outgoing = await account.getTransfers({ direction: 'outgoing', limit: 5 }) console.log('Outgoing transfers:', outgoing) ``` ## Paginate Results You can paginate through transfer history using the `limit` and `skip` options in [`account.getTransfers()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Paginate Transfers" const page = await account.getTransfers({ direction: 'all', limit: 20, skip: 10 }) console.log('Transfers 11-30:', page) ``` ## Check Transaction Receipts You can check whether a specific transaction has been confirmed using [`account.getTransactionReceipt()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Get Transaction Receipt" const receipt = await account.getTransactionReceipt('abc123...') if (receipt) { console.log('Transaction confirmed') } ``` ## Next Steps Learn how to [sign and verify messages](/sdk/wallet-modules/wallet-btc/guides/sign-verify-messages). *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/guides/handle-errors Description: Handle errors, manage fees, and dispose of sensitive data. This guide explains how to [handle transaction errors](#transaction-errors), [handle connection errors](#connection-errors), and follow [best practices](#best-practices) for fee management and memory cleanup. ## Transaction Errors Transactions sent via [`account.sendTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference) can fail for several reasons. Wrap transaction calls in a `try/catch` block to handle specific error types: ```javascript title="Handle Transaction Errors" try { const result = await account.sendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 100000n }) console.log('Transaction hash:', result.hash) } catch (error) { if (error.message.includes('Insufficient balance')) { console.error('Not enough funds in wallet') } else if (error.message.includes('dust limit')) { console.error('Amount is below the minimum dust limit') } else if (error.message.includes('Invalid address')) { console.error('Recipient address is invalid') } else { console.error('Transaction failed:', error.message) } } ``` ## Connection Errors Network issues with the Electrum server can cause failures across all operations. Handle connection errors at a higher level: ```javascript title="Handle Connection Errors" try { const balance = await account.getBalance() console.log('Balance:', balance, 'satoshis') } catch (error) { if (error.message.includes('ECONNREFUSED') || error.message.includes('timeout')) { console.error('Network error: check Electrum server connection') } else if (error.message.includes('Invalid seed')) { console.error('Invalid seed phrase provided') } else { console.error('Operation failed:', error.message) } } ``` ## Best Practices ### Fee Management You can retrieve current network fee rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Get Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'sat/vB') console.log('Fast fee rate:', feeRates.fast, 'sat/vB') ``` [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-btc/api-reference) fetches rates from the mempool.space API, while [`account.sendTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference) estimates fees from the connected Electrum server. Use [`getFeeRates()`](/sdk/wallet-modules/wallet-btc/api-reference) for display purposes. ### Dispose of Sensitive Data For security, clear sensitive data from memory when a session is complete. Use [`account.dispose()`](/sdk/wallet-modules/wallet-btc/api-reference) and [`wallet.dispose()`](/sdk/wallet-modules/wallet-btc/api-reference) to securely wipe private keys: ```javascript title="Dispose Resources" try { const result = await account.sendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 100000n }) console.log('Transaction hash:', result.hash) } finally { account.dispose() wallet.dispose() } ``` Always call [`dispose()`](/sdk/wallet-modules/wallet-btc/api-reference) when finished with accounts. Private keys are securely wiped from memory using `sodium_memzero`. Electrum connections are automatically closed. Disposal is irreversible. *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/guides/manage-accounts Description: Work with multiple accounts and custom derivation paths. This guide explains how to [retrieve accounts by index](#retrieve-accounts-by-index) and [use custom derivation paths](#retrieve-account-by-custom-derivation-path). ## Retrieve Accounts by Index You can retrieve multiple accounts using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-btc/api-reference) with different index values: ```javascript title="Retrieve Multiple Accounts" const account0 = await wallet.getAccount(0) const address0 = await account0.getAddress() console.log('Account 0 address:', address0) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` You can iterate through multiple accounts using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-btc/api-reference) to inspect addresses and balances in bulk: ```javascript title="Iterate Over Accounts" for (let i = 0; i < 5; i++) { const account = await wallet.getAccount(i) const address = await account.getAddress() const balance = await account.getBalance() console.log(`Account ${i}: ${address} (${balance} satoshis)`) } ``` ## Retrieve Account by Custom Derivation Path You can retrieve an account at a specific derivation path using [`wallet.getAccountByPath()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Custom Derivation Path" const customAccount = await wallet.getAccountByPath("0'/0/5") const customAddress = await customAccount.getAddress() console.log('Custom account address:', customAddress) ``` The default derivation scheme is BIP-84 (Native SegWit): `m/84'/0'/0'/0/{index}` on mainnet. Set `bip: 44` in the wallet configuration for legacy BIP-44 paths: `m/44'/0'/0'/0/{index}`. ## Next Steps With accounts set up, learn how to [check balances](/sdk/wallet-modules/wallet-btc/guides/check-balances). *** ## Send Transactions URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/guides/send-transactions Description: Send BTC and estimate transaction fees. This guide explains how to [send BTC](#send-btc), [extend post-broadcast polling](#extend-post-broadcast-polling), [sign without broadcasting](#sign-without-broadcasting), [estimate fees before sending](#estimate-fees), [use a custom fee rate](#send-with-custom-fee-rate), and [target a specific confirmation time](#send-with-confirmation-target). ## Send BTC You can send Bitcoin to a recipient using [`account.sendTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference#sendtransactionoptions-timeoutms): ```javascript title="Send BTC" const result = await account.sendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 100000n // 0.001 BTC in satoshis }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee, 'satoshis') ``` Bitcoin transactions support a single recipient only. Amounts and fees are always in satoshis (1 BTC = 100,000,000 satoshis). The minimum amount must be above the dust limit (294 satoshis for SegWit, 546 for legacy). ## Extend Post-Broadcast Polling If you want [`account.sendTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference#sendtransactionoptions-timeoutms) to keep polling after broadcast until spent inputs disappear from the unspent-output set, pass the optional `timeoutMs` argument: ```javascript title="Send BTC With Extended Polling" const result = await account.sendTransaction( { to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 100000n, }, 30000 ) console.log('Transaction hash:', result.hash) ``` If you omit `timeoutMs`, the wallet uses the default post-broadcast polling window before returning. ## Sign Without Broadcasting Use [`account.signTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference#signtransactionoptions) when your app needs a signed raw Bitcoin transaction but does not want WDK to broadcast it immediately. ```javascript title="Sign BTC Transaction" const signedTransaction = await account.signTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 100000n, feeRate: 10n }) console.log('Signed transaction:', signedTransaction) ``` `signTransaction()` returns the signed transaction hex. Use `sendTransaction()` when WDK should sign, broadcast, and return the transaction hash. ## Estimate Fees You can estimate the fee for a transaction without broadcasting it using [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference#quotesendtransactionoptions): ```javascript title="Estimate Fee" const quote = await account.quoteSendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 100000n }) console.log('Estimated fee:', quote.fee, 'satoshis') ``` ## Send with Custom Fee Rate You can override automatic fee estimation by providing a `feeRate` in sat/vB to [`account.sendTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference#sendtransactionoptions-timeoutms): ```javascript title="Custom Fee Rate" const result = await account.sendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 100000n, feeRate: 10n // sat/vB }) ``` When `feeRate` is provided, the `confirmationTarget` parameter is ignored. ## Send with Confirmation Target You can target a specific number of blocks for confirmation using the `confirmationTarget` parameter in [`account.sendTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference#sendtransactionoptions-timeoutms): ```javascript title="Confirmation Target" const result = await account.sendTransaction({ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', value: 100000n, confirmationTarget: 6 // target 6 blocks (~1 hour) }) ``` ## Next Steps Learn how to [view transaction history](/sdk/wallet-modules/wallet-btc/guides/get-transaction-history). *** ## Sign and Verify Messages URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/guides/sign-verify-messages Description: Sign messages and verify signatures with Bitcoin accounts. This guide explains how to [sign messages](#sign-a-message) and [verify signatures](#verify-a-signature). ## Sign a Message You can sign a message with the account's private key using [`account.sign()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Sign Message" const message = 'Hello, Bitcoin!' const signature = await account.sign(message) console.log('Signature:', signature) ``` The signature is returned as a base64-encoded string. ## Verify a Signature You can verify that a signature was produced by the corresponding private key using [`account.verify()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Verify Signature" const isValid = await account.verify(message, signature) console.log('Signature valid:', isValid) ``` You can also verify signatures using a [read-only account](/sdk/wallet-modules/wallet-btc/api-reference). Use [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-btc/api-reference) to create one from an owned account, then call [`readOnlyAccount.verify()`](/sdk/wallet-modules/wallet-btc/api-reference): ```javascript title="Verify with Read-Only Account" const readOnlyAccount = await account.toReadOnlyAccount() const isValid = await readOnlyAccount.verify('Hello, Bitcoin!', signature) console.log('Verified with read-only account:', isValid) ``` ## Next Steps Learn how to [handle errors and manage resources](/sdk/wallet-modules/wallet-btc/guides/handle-errors). *** ## Wallet BTC Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-btc/usage Description: Guide to using the @tetherto/wdk-wallet-btc module. # Usage The `@tetherto/wdk-wallet-btc` module provides wallet management for the Bitcoin blockchain. Install the package and create your first wallet. Work with multiple accounts and custom derivation paths. Query native BTC balances for owned and read-only accounts. Send Bitcoin and estimate transaction fees. Retrieve and filter transfer history. Sign messages and verify signatures. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Bitcoin Wallet Configuration Get started with WDK's Bitcoin Wallet API *** ### Need Help? *** ## Wallet EVM Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm Description: Overview of the @tetherto/wdk-wallet-evm module A simple and secure package to manage BIP-44 wallets for EVM (Ethereum Virtual Machine) blockchains. This package provides a clean API for creating, managing, and interacting with Ethereum-compatible wallets using BIP-39 seed phrases and BIP-44 derivation paths. ## Features - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases - **EVM Derivation Paths**: Support for BIP-44 standard derivation paths for Ethereum (m/44'/60') - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase - **EVM Address Support**: Generate and manage Ethereum-compatible addresses using ethers.js - **Message Signing**: Sign and verify messages using EVM cryptography - **Offline Transaction Signing**: Sign EVM transactions with `signTransaction()` without broadcasting them - **Transaction Management**: Send transactions and get fee estimates with EIP-1559 support - **ERC20 Support**: Query native token and ERC20 token balances using smart contract interactions - **Batch Token Balance Queries**: Fetch balances for multiple ERC20 tokens in one call with `getTokenBalances` - **TypeScript Support**: Full TypeScript definitions included - **Memory Safety**: Secure private key management with memory-safe HDNodeWallet implementation - **Provider Flexibility**: Support for JSON-RPC URLs, EIP-1193 browser providers, and ordered failover provider lists - **Gas Optimization**: Support for EIP-1559 maxFeePerGas and maxPriorityFeePerGas - **Fee Estimation**: Dynamic fee calculation with normal (1.1x) and fast (2.0x) multipliers ## Supported Networks This package works with any EVM-compatible blockchain, including: - **Ethereum**: Mainnet, Sepolia - **Polygon**: Mainnet, Amoy - **Binance Smart Chain (BSC)**: Mainnet, Testnet - **Arbitrum**: One, Nova - **Optimism**: Mainnet, Sepolia - **Avalanche C-Chain**: Mainnet, Fuji - **And many more...** ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's EVM Wallet configuration Get started with WDK's EVM Wallet API Get started with WDK's EVM Wallet usage *** ### Need Help? *** ## Wallet EVM ERC-4337 Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337 Description: Overview of the @tetherto/wdk-wallet-evm-erc-4337 module A simple and secure package to manage ERC-4337 compliant wallets for EVM-compatible blockchains. This package provides a clean API for creating, managing, and interacting with account abstraction wallets using BIP-39 seed phrases and EVM-specific derivation paths. ## Features - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases - **EVM Derivation Paths**: Support for BIP-44 standard derivation paths for Ethereum (m/44'/60') - **Multi-Account Management**: Create and manage multiple account abstraction wallets from a single seed phrase - **ERC-4337 Support**: Full implementation of ERC-4337 account abstraction standard - **UserOperation Management**: Create and send UserOperations through bundlers - **Message Signing**: Sign and verify messages using EVM cryptography - **ERC20 Support**: Query native token and ERC20 token balances using smart contract interactions - **TypeScript Support**: Full TypeScript definitions included - **Memory Safety**: Secure private key management with memory-safe HDNodeWallet implementation - **Bundler Integration**: Support for ERC-4337 bundler services - **Gas Optimization**: Paymaster support and gas estimation for UserOperations - **Fee Estimation**: Dynamic fee calculation with bundler-aware estimation - **EIP-712 Typed Data Support**: Sign and verify EIP-712 structured typed data - **Batch Token Balance Queries**: Query multiple ERC20 token balances in a single call ## Supported Networks This package works with any EVM-compatible blockchain, including: - **Ethereum Mainnet** - **Ethereum Testnets** (Sepolia) - **Other EVM Chains** (Polygon, Arbitrum, Avalanche C-chain, Plasma etc.) ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's EVM with ERC-4337 Wallet configuration Get started with WDK's EVM with ERC-4337 Wallet API Get started with WDK's EVM with ERC-4337 Wallet usage *** ## Need Help? *** ## Wallet EVM ERC-4337 API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-evm-erc-4337 ## Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerEvmErc4337](#walletmanagerevmerc4337) | Main class for managing ERC-4337 EVM wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountEvmErc4337](#walletaccountevmerc4337) | Individual ERC-4337 wallet account implementation. Extends `WalletAccountReadOnlyEvmErc4337` and implements `IWalletAccount`. | [Constructor](#constructor-1), [Methods](#methods-1), [Properties](#properties) | | [WalletAccountReadOnlyEvmErc4337](#walletaccountreadonlyevmerc4337) | Read-only ERC-4337 wallet account. Extends `WalletAccountReadOnly` from `@tetherto/wdk-wallet`. | [Constructor](#constructor-2), [Methods](#methods-2) | | [ConfigurationError](#configurationerror) | Error thrown when the wallet configuration is invalid or has missing required fields. | - | ## WalletManagerEvmErc4337 The main class for managing ERC-4337 EVM wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. ### Fee Rate Behavior Internally, `getFeeRates()` applies these multipliers to the base fee: - **Normal**: base fee × 110% - **Fast**: base fee × 200% These multipliers are internal (`protected static`) and cannot be imported or overridden. ### Constructor ```javascript new WalletManagerEvmErc4337(seed, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (EvmErc4337WalletConfig): Configuration object with common fields and a gas payment mode **Common config fields (required for all modes):** - `chainId` (number): The blockchain's ID (e.g., 1 for Ethereum mainnet) - `provider` (string | Eip1193Provider | Array\): RPC endpoint URL, EIP-1193 provider instance, or ordered failover list - `bundlerUrl` (string): The URL of the bundler service - `safeModulesVersion` (string): The Safe modules version (e.g., `'0.3.0'`) **Gas payment mode** (one of the following): Fees are paid using an ERC-20 token through a paymaster service. - `paymasterUrl` (string): The URL of the paymaster service - `paymasterAddress` (string): The address of the paymaster smart contract - `paymasterToken` (object): The paymaster token configuration - `address` (string): The address of the ERC-20 token used for fees - `transferMaxFee` (number | bigint, optional): Maximum fee limit in paymaster token units ```javascript const wallet = new WalletManagerEvmErc4337(seedPhrase, { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', safeModulesVersion: '0.3.0', // Paymaster token mode paymasterUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' // USD₮ }, transferMaxFee: 100000 // Optional: max fee in token units }) ``` Fees are sponsored by a third party via a sponsorship policy. - `isSponsored` (true): Enables sponsorship mode - `paymasterUrl` (string): The URL of the paymaster service - `sponsorshipPolicyId` (string, optional): The sponsorship policy ID ```javascript const wallet = new WalletManagerEvmErc4337(seedPhrase, { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', safeModulesVersion: '0.3.0', // Sponsorship mode isSponsored: true, paymasterUrl: 'https://api.candide.dev/public/v3/ethereum', sponsorshipPolicyId: 'your-policy-id' // Optional }) ``` Fees are paid using the chain's native token (e.g., ETH). - `useNativeCoins` (true): Enables native coin fee payment - `transferMaxFee` (number | bigint, optional): Maximum fee limit in native token units ```javascript const wallet = new WalletManagerEvmErc4337(seedPhrase, { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', safeModulesVersion: '0.3.0', // Native coins mode useNativeCoins: true, transferMaxFee: 100000000000000n // Optional: max fee in wei }) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getRandomSeedPhrase(wordCount?)` | (static) Returns a random BIP-39 seed phrase | `string` | - | | `isValidSeedPhrase(seedPhrase)` | (static) Checks if a seed phrase is valid | `boolean` | - | | `getAccount(index?)` | Returns a wallet account at the specified index | `Promise\` | - | | `getAccountByPath(path)` | Returns a wallet account at the specified BIP-44 derivation path | `Promise\` | - | | `getFeeRates()` | Returns current fee rates for transactions | `Promise\<{normal: bigint, fast: bigint}\>` | If no provider | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` | - | ### Properties | Property | Type | Description | |----------|------|-------------| | `seed` | `Uint8Array` | The wallet's seed phrase as bytes | #### `getRandomSeedPhrase(wordCount?)` (static) Returns a random BIP-39 seed phrase. **Parameters:** - `wordCount` (12 | 24, optional): The number of words in the seed phrase (default: 12) **Returns:** `string` - The seed phrase **Example:** ```javascript const seedPhrase = WalletManagerEvmErc4337.getRandomSeedPhrase() console.log('Seed phrase:', seedPhrase) // 12 words const longSeedPhrase = WalletManagerEvmErc4337.getRandomSeedPhrase(24) console.log('Long seed phrase:', longSeedPhrase) // 24 words ``` #### `isValidSeedPhrase(seedPhrase)` (static) Checks if a seed phrase is valid. **Parameters:** - `seedPhrase` (string): The seed phrase to validate **Returns:** `boolean` - True if the seed phrase is valid **Example:** ```javascript const isValid = WalletManagerEvmErc4337.isValidSeedPhrase('abandon abandon abandon ...') console.log('Valid:', isValid) ``` #### `getAccount(index)` Returns a wallet account at the specified index using BIP-44 derivation. **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise\` - The wallet account **Example:** ```javascript // Get first account (index 0) const account = await wallet.getAccount(0) // Get default account const defaultAccount = await wallet.getAccount() ``` #### `getAccountByPath(path)` Returns a wallet account at the specified BIP-44 derivation path. **Parameters:** - `path` (string): The derivation path (e.g., "0'/0/0") **Returns:** `Promise\` - The wallet account **Example:** ```javascript // Full derivation path: m/44'/60'/0'/0/1 const account = await wallet.getAccountByPath("0'/0/1") ``` #### `getFeeRates()` Returns current fee rates with ERC-4337 specific multipliers. **Returns:** `Promise\<{normal: bigint, fast: bigint}\>` - Fee rates in wei **Throws:** Error if no provider is configured **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'wei') // base fee × 1.1 console.log('Fast fee rate:', feeRates.fast, 'wei') // base fee × 2.0 ``` #### `dispose()` Disposes all wallet accounts, clearing private keys from memory. **Example:** ```javascript // Clean up when done wallet.dispose() ``` ## WalletAccountEvmErc4337 Represents an individual ERC-4337 wallet account. Extends `WalletAccountReadOnlyEvmErc4337` and implements `IWalletAccount`. ### Constants The following constant is used internally for Safe account address derivation: ```javascript // Internal: used by predictSafeAddress() for deterministic address generation const SALT_NONCE = '0x69b348339eea4ed93f9d11931c3b894c8f9d8c7663a053024b11cb7eb4e5a1f6' ``` > **Note:** This constant is not re-exported from the package entry point. Use `predictSafeAddress()` instead of referencing it directly. ### Constructor ```javascript new WalletAccountEvmErc4337(seed, path, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `path` (string): BIP-44 derivation path (e.g., "0'/0/0") - `config` (EvmErc4337WalletConfig): Configuration object (same as [WalletManagerEvmErc4337](#constructor)) **Example:** ```javascript const account = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', safeModulesVersion: '0.3.0', paymasterUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } }) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `predictSafeAddress(owner, config)` | (static) Predicts the Safe address for a given owner | `string` | - | | `getAddress()` | Returns the Safe account's address | `Promise\` | - | | `sign(message)` | Signs a message using the account's private key | `Promise\` | - | | `verify(message, signature)` | Verifies a message signature | `Promise\` | - | | `sendTransaction(tx, config?)` | Sends a transaction via UserOperation | `Promise\<{hash: string, fee: bigint}\>` | If fee exceeds max | | `quoteSendTransaction(tx, config?)` | Estimates the fee for a UserOperation | `Promise\<{fee: bigint}\>` | - | | `transfer(options, config?)` | Transfers ERC20 tokens via UserOperation | `Promise\<{hash: string, fee: bigint}\>` | If fee exceeds max | | `quoteTransfer(options, config?)` | Estimates the fee for an ERC20 transfer | `Promise\<{fee: bigint}\>` | - | | `approve(options)` | Approves a spender to spend ERC20 tokens | `Promise\<{hash: string, fee: bigint}\>` | - | | `getBalance()` | Returns the native token balance (in wei) | `Promise\` | - | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific ERC20 token | `Promise\` | - | | `getPaymasterTokenBalance()` | Returns the paymaster token balance | `Promise\` | - | | `getAllowance(token, spender)` | Returns current token allowance for a spender | `Promise\` | - | | `getTransactionReceipt(hash)` | Returns a transaction receipt | `Promise\` | - | | `getUserOperationReceipt(hash)` | Returns a UserOperation receipt | `Promise\` | - | | `signTypedData(typedData)` | Signs EIP-712 typed structured data | `Promise\` | - | | `verifyTypedData(typedData, signature)` | Verifies an EIP-712 typed data signature | `Promise\` | - | | `getTokenBalances(tokenAddresses)` | Returns balances for multiple ERC20 tokens | `Promise\\>` | - | | `toReadOnlyAccount()` | Returns a read-only copy of the account | `Promise\` | - | | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` | - | ##### `getAddress()` Returns the Safe smart contract wallet address (not the underlying EOA address). **Returns:** `Promise\` - The Safe account's address **Example:** ```javascript const address = await account.getAddress() console.log('Safe account address:', address) // 0x... (Smart contract address) ``` ##### `sign(message)` Signs a message using the underlying EOA private key. **Parameters:** - `message` (string): The message to sign **Returns:** `Promise\` - The message signature **Example:** ```javascript const message = 'Hello, ERC-4337!' const signature = await account.sign(message) console.log('Signature:', signature) ``` ##### `verify(message, signature)` Verifies a message signature against the underlying EOA address. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise\` - True if signature is valid **Example:** ```javascript const isValid = await account.verify(message, signature) console.log('Signature valid:', isValid) ``` ##### `sendTransaction(tx, config?)` Sends a transaction via UserOperation through the bundler. **Parameters:** - `tx` (EvmErc4337Transaction | EvmErc4337Transaction[]): Transaction object or array for batch transactions - `to` (string): Recipient address - `value` (number | bigint): Amount in wei - `data` (string, optional): Transaction data in hex format - `callGasLimit`, `verificationGasLimit`, `preVerificationGas` (number | bigint, optional): Per-call overrides for the UserOperation gas limits - `maxFeePerGas`, `maxPriorityFeePerGas` (number | bigint, optional): Per-call overrides for the EIP-1559 fee pair; set both together. In a batch, only the first transaction's gas overrides apply (see [EvmErc4337Transaction](#evmerc4337transaction)) - `config` (optional): Per-call configuration override. Accepts a partial version of the gas payment mode fields (see [Config Override](#config-override)). **Returns:** `Promise\<{hash: string, fee: bigint}\>` - UserOperation hash and fee **Throws:** Error if fee exceeds `transferMaxFee` **Example:** ```javascript // Single transaction const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n, // 1 ETH data: '0x' }) console.log('UserOperation hash:', result.hash) console.log('Fee paid:', result.fee) // Batch transactions const batchResult = await account.sendTransaction([ { to: '0x...', value: 100000000000000000n }, { to: '0x...', value: 200000000000000000n } ]) // With per-call config override const customResult = await account.sendTransaction({ to: '0x...', value: 1000000000000000000n }, { paymasterToken: { address: '0xNewToken...' } }) // With per-call gas overrides (EIP-1559 fee pair set together) const fastResult = await account.sendTransaction({ to: '0x...', value: 1000000000000000000n, maxFeePerGas: 30000000000n, // 30 gwei maxPriorityFeePerGas: 2000000000n // 2 gwei }) ``` ##### `quoteSendTransaction(tx, config?)` Estimates the fee for a UserOperation without sending it. **Parameters:** - `tx` (EvmErc4337Transaction | EvmErc4337Transaction[]): Transaction object or array (same as sendTransaction) - `config` (optional): Per-call configuration override (see [Config Override](#config-override)) **Returns:** `Promise\<{fee: bigint}\>` - Fee estimate **Example:** ```javascript const quote = await account.quoteSendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n }) console.log('Estimated fee:', quote.fee) ``` ##### `transfer(options, config?)` Transfers ERC20 tokens via UserOperation. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): ERC20 token contract address - `recipient` (string): Recipient address - `amount` (number | bigint): Amount in token base units - `config` (optional): Per-call configuration override (see [Config Override](#config-override)) **Returns:** `Promise\<{hash: string, fee: bigint}\>` - UserOperation hash and fee **Throws:** - Error if fee exceeds `transferMaxFee` - Error if insufficient token balance **Example:** ```javascript const result = await account.transfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USD₮ recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 // 1 USD₮ (6 decimals) }, { transferMaxFee: 50000 // Override max fee for this call }) console.log('Transfer hash:', result.hash) console.log('Transfer fee:', result.fee) ``` ##### `quoteTransfer(options, config?)` Estimates the fee for an ERC20 token transfer. **Parameters:** - `options` (TransferOptions): Transfer options (same as transfer) - `config` (optional): Per-call configuration override (see [Config Override](#config-override)) **Returns:** `Promise\<{fee: bigint}\>` - Fee estimate **Example:** ```javascript const quote = await account.quoteTransfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 }) console.log('Transfer fee estimate:', quote.fee) ``` ##### `getBalance()` Returns the Safe account's native token balance. **Returns:** `Promise\` - Balance in wei **Example:** ```javascript const balance = await account.getBalance() console.log('Native balance:', balance, 'wei') ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific ERC20 token in the Safe account. **Parameters:** - `tokenAddress` (string): The ERC20 token contract address **Returns:** `Promise\` - Token balance in base units **Example:** ```javascript const tokenBalance = await account.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7') console.log('USDT balance:', tokenBalance) // In 6 decimal units ``` ##### `getPaymasterTokenBalance()` Returns the balance of the configured paymaster token used for paying fees. **Returns:** `Promise\` - Paymaster token balance in base units **Example:** ```javascript const paymasterBalance = await account.getPaymasterTokenBalance() console.log('Paymaster token balance:', paymasterBalance) // Check if sufficient for transaction if (paymasterBalance < 10000n) { console.warn('Low paymaster token balance - may not cover fees') } ``` ##### `approve(options)` Approves a spender to spend ERC20 tokens on behalf of the Safe account. **Parameters:** - `options` (ApproveOptions): Approve options - `token` (string): ERC20 token contract address - `spender` (string): The address allowed to spend the tokens - `amount` (number | bigint): Amount to approve in token base units **Returns:** `Promise\<{hash: string, fee: bigint}\>` - Transaction result **Example:** ```javascript const result = await account.approve({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', spender: '0xSpenderContract...', amount: 1000000n // 1 USD₮ }) console.log('Approval hash:', result.hash) ``` ##### `getAllowance(token, spender)` Returns the current token allowance for the given spender. **Parameters:** - `token` (string): ERC20 token contract address - `spender` (string): The spender's address **Returns:** `Promise\` - The current allowance **Example:** ```javascript const allowance = await account.getAllowance( '0xdAC17F958D2ee523a2206206994597C13D831ec7', '0xSpenderContract...' ) console.log('Current allowance:', allowance) ``` ##### `getTransactionReceipt(hash)` Returns a transaction receipt by hash. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise\` - Transaction receipt or null if not mined **Example:** ```javascript const receipt = await account.getTransactionReceipt('0x...') if (receipt) { console.log('Confirmed in block:', receipt.blockNumber) } ``` ##### `getUserOperationReceipt(hash)` Returns a UserOperation receipt by hash. **Parameters:** - `hash` (string): The UserOperation hash **Returns:** `Promise\` - UserOperation receipt or null if not processed **Example:** ```javascript const receipt = await account.getUserOperationReceipt('0x...') if (receipt) { console.log('UserOp receipt:', receipt) } ``` ##### `signTypedData(typedData)` Signs EIP-712 typed structured data using the underlying EOA private key. **Parameters:** - `typedData` (TypedData): The typed data object containing domain, types, primaryType, and message **Returns:** `Promise\` - The typed data signature **Example:** ```javascript const typedData = { domain: { name: 'MyDApp', version: '1', chainId: 1, verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' }, types: { Transfer: [ { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' } ] }, primaryType: 'Transfer', message: { to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000n } } const signature = await account.signTypedData(typedData) console.log('Typed data signature:', signature) ``` ##### `verifyTypedData(typedData, signature)` Verifies an EIP-712 typed data signature against the underlying EOA address. **Parameters:** - `typedData` (TypedData): The original typed data object - `signature` (string): The signature to verify **Returns:** `Promise\` - True if the signature is valid **Example:** ```javascript const isValid = await account.verifyTypedData(typedData, signature) console.log('Typed data signature valid:', isValid) ``` ##### `getTokenBalances(tokenAddresses)` Returns balances for multiple ERC20 tokens in a single call. **Parameters:** - `tokenAddresses` (string[]): Array of ERC20 token contract addresses **Returns:** `Promise\\>` - Map of token address to balance in base units **Example:** ```javascript const balances = await account.getTokenBalances([ '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' // USDC ]) for (const [address, balance] of balances) { console.log(`Token ${address}: ${balance}`) } ``` ##### `toReadOnlyAccount()` Creates a read-only copy of the account with the same Safe address and configuration. **Returns:** `Promise\` - Read-only account instance **Example:** ```javascript const readOnlyAccount = await account.toReadOnlyAccount() // Can check balances but cannot send transactions const balance = await readOnlyAccount.getBalance() // readOnlyAccount.sendTransaction() // Would not be available ``` ##### `dispose()` Disposes the wallet account, clearing private keys from memory. **Example:** ```javascript account.dispose() ``` ### Properties | Property | Type | Description | |----------|------|-------------| | `index` | `number` | The derivation path's index of this account | | `path` | `string` | The full BIP-44 derivation path of this account | | `keyPair` | `{privateKey: Uint8Array \| null, publicKey: Uint8Array}` | The account's key pair (⚠️ Contains sensitive data) | **Example:** ```javascript console.log('Account index:', account.index) // 0, 1, 2, etc. console.log('Account path:', account.path) // m/44'/60'/0'/0/0 // ⚠️ SENSITIVE: Handle with care const { privateKey, publicKey } = account.keyPair console.log('Public key length:', publicKey.length) // 65 bytes console.log('Private key length:', privateKey?.length) // 32 bytes (null after dispose) ``` ⚠️ **Security Note**: The `keyPair` property contains sensitive cryptographic material. Never log, display, or expose the private key. ## WalletAccountReadOnlyEvmErc4337 Represents a read-only ERC-4337 wallet account that can query balances and estimate fees but cannot send transactions. ### Constants The following constant is used internally for Safe account address derivation: ```javascript // Internal: used by predictSafeAddress() for deterministic address generation const SALT_NONCE = '0x69b348339eea4ed93f9d11931c3b894c8f9d8c7663a053024b11cb7eb4e5a1f6' ``` > **Note:** This constant is not re-exported from the package entry point. Use `predictSafeAddress()` instead of referencing it directly. ### Constructor ```javascript new WalletAccountReadOnlyEvmErc4337(address, config) ``` **Parameters:** - `address` (string): The EOA address (owner address) - `config` (`Omit`): Configuration object without `transferMaxFee` **Example:** ```javascript const readOnlyAccount = new WalletAccountReadOnlyEvmErc4337('0x...', { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', safeModulesVersion: '0.3.0', paymasterUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } }) ``` ### Static Methods | Method | Description | Returns | |--------|-------------|---------| | `predictSafeAddress(owner, config)` | Predicts the Safe address for a given owner without instantiating an account | `string` | #### `predictSafeAddress(owner, config)` (static) Predicts the address of a Safe account. **Parameters:** - `owner` (string): The Safe owner's EOA address - `config` (object): Configuration with: - `chainId` (number): The blockchain ID - `safeModulesVersion` (string): The Safe modules version **Returns:** `string` - The predicted Safe address **Example:** ```javascript const safeAddress = WalletAccountReadOnlyEvmErc4337.predictSafeAddress( '0xOwnerEOA...', { chainId: 1, safeModulesVersion: '0.3.0' } ) console.log('Predicted Safe address:', safeAddress) // Also available on WalletAccountEvmErc4337 (inherited) const sameAddress = WalletAccountEvmErc4337.predictSafeAddress( '0xOwnerEOA...', { chainId: 1, safeModulesVersion: '0.3.0' } ) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getAddress()` | Returns the Safe account's address | `Promise\` | - | | `verify(message, signature)` | Verifies a message signature | `Promise\` | - | | `getBalance()` | Returns the native token balance (in wei) | `Promise\` | - | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific ERC20 token | `Promise\` | - | | `getPaymasterTokenBalance()` | Returns the paymaster token balance | `Promise\` | - | | `getAllowance(token, spender)` | Returns current token allowance for a spender | `Promise\` | - | | `quoteSendTransaction(tx, config?)` | Estimates the fee for a UserOperation | `Promise\<{fee: bigint}\>` | If simulation fails | | `quoteTransfer(options, config?)` | Estimates the fee for an ERC20 transfer | `Promise\<{fee: bigint}\>` | If simulation fails | | `getTransactionReceipt(hash)` | Returns a transaction receipt | `Promise\` | - | | `getUserOperationReceipt(hash)` | Returns a UserOperation receipt | `Promise\` | - | | `signTypedData(typedData)` | Signs EIP-712 typed structured data | `Promise\` | - | | `verifyTypedData(typedData, signature)` | Verifies an EIP-712 typed data signature | `Promise\` | - | | `getTokenBalances(tokenAddresses)` | Returns balances for multiple ERC20 tokens | `Promise\\>` | - | ##### `getAddress()` Returns the Safe smart contract wallet address. **Returns:** `Promise\` - The Safe account's address **Example:** ```javascript const address = await readOnlyAccount.getAddress() console.log('Safe address:', address) ``` ##### `verify(message, signature)` Verifies a message signature against the underlying EOA address. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise\` - True if signature is valid **Example:** ```javascript const isValid = await readOnlyAccount.verify(message, signature) console.log('Signature valid:', isValid) ``` ##### `getBalance()` Returns the Safe account's native token balance. **Returns:** `Promise\` - Balance in wei **Example:** ```javascript const balance = await readOnlyAccount.getBalance() console.log('Balance:', balance, 'wei') ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific ERC20 token. **Parameters:** - `tokenAddress` (string): The ERC20 token contract address **Returns:** `Promise\` - Token balance in base units **Example:** ```javascript const tokenBalance = await readOnlyAccount.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7') console.log('USDT balance:', tokenBalance) ``` ##### `getPaymasterTokenBalance()` Returns the balance of the configured paymaster token. **Returns:** `Promise\` - Paymaster token balance in base units **Example:** ```javascript const paymasterBalance = await readOnlyAccount.getPaymasterTokenBalance() console.log('Paymaster token balance:', paymasterBalance) ``` ##### `getAllowance(token, spender)` Returns the current token allowance for the given spender. **Parameters:** - `token` (string): ERC20 token contract address - `spender` (string): The spender's address **Returns:** `Promise\` - The current allowance **Example:** ```javascript const allowance = await readOnlyAccount.getAllowance( '0xdAC17F958D2ee523a2206206994597C13D831ec7', '0xSpenderContract...' ) console.log('Allowance:', allowance) ``` ##### `quoteSendTransaction(tx, config?)` Estimates the fee for a UserOperation. **Parameters:** - `tx` (EvmErc4337Transaction | EvmErc4337Transaction[]): Transaction object or array - `config` (optional): Per-call configuration override (see [Config Override](#config-override)) **Returns:** `Promise\<{fee: bigint}\>` - Fee estimate **Throws:** Error if simulation fails **Example:** ```javascript try { const quote = await readOnlyAccount.quoteSendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n }) console.log('Estimated fee:', quote.fee) } catch (error) { if (error.message.includes('not enough funds')) { console.error('Insufficient paymaster token balance') } } ``` ##### `quoteTransfer(options, config?)` Estimates the fee for an ERC20 token transfer. **Parameters:** - `options` (TransferOptions): Transfer options - `config` (optional): Per-call configuration override (see [Config Override](#config-override)) **Returns:** `Promise\<{fee: bigint}\>` - Fee estimate **Example:** ```javascript const quote = await readOnlyAccount.quoteTransfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 }) console.log('Transfer fee estimate:', quote.fee) ``` ##### `getTransactionReceipt(hash)` Returns a transaction receipt by hash. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise\` - Transaction receipt or null if not mined **Example:** ```javascript const receipt = await readOnlyAccount.getTransactionReceipt('0x...') if (receipt) { console.log('Transaction confirmed in block:', receipt.blockNumber) console.log('Status:', receipt.status) // 1 = success, 0 = failed } else { console.log('Transaction not yet mined') } ``` ##### `getUserOperationReceipt(hash)` Returns a UserOperation receipt by hash. **Parameters:** - `hash` (string): The UserOperation hash **Returns:** `Promise\` - UserOperation receipt or null if not processed **Example:** ```javascript const receipt = await readOnlyAccount.getUserOperationReceipt('0x...') if (receipt) { console.log('UserOp receipt:', receipt) } ``` ##### `signTypedData(typedData)` Signs EIP-712 typed structured data using the underlying EOA address. **Parameters:** - `typedData` (TypedData): The typed data object containing domain, types, primaryType, and message **Returns:** `Promise\` - The typed data signature **Example:** ```javascript const typedData = { domain: { name: 'MyDApp', version: '1', chainId: 1, verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' }, types: { Transfer: [ { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' } ] }, primaryType: 'Transfer', message: { to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000n } } const signature = await readOnlyAccount.signTypedData(typedData) console.log('Typed data signature:', signature) ``` ##### `verifyTypedData(typedData, signature)` Verifies an EIP-712 typed data signature against the underlying EOA address. **Parameters:** - `typedData` (TypedData): The original typed data object - `signature` (string): The signature to verify **Returns:** `Promise\` - True if the signature is valid **Example:** ```javascript const isValid = await readOnlyAccount.verifyTypedData(typedData, signature) console.log('Typed data signature valid:', isValid) ``` ##### `getTokenBalances(tokenAddresses)` Returns balances for multiple ERC20 tokens in a single call. **Parameters:** - `tokenAddresses` (string[]): Array of ERC20 token contract addresses **Returns:** `Promise\\>` - Map of token address to balance in base units **Example:** ```javascript const balances = await readOnlyAccount.getTokenBalances([ '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' // USDC ]) for (const [address, balance] of balances) { console.log(`Token ${address}: ${balance}`) } ``` ## Types ### EvmErc4337WalletConfig The configuration is a union type combining common fields with one of three gas payment modes: ```typescript // Common fields (required for all modes) interface EvmErc4337WalletCommonConfig { chainId: number; // Blockchain ID provider: string | Eip1193Provider | Array; // RPC provider or failover list bundlerUrl: string; // Bundler service URL safeModulesVersion: string; // Safe modules version (e.g., '0.3.0') } // Mode 1: Paymaster Token interface EvmErc4337WalletPaymasterTokenConfig { isSponsored?: false; useNativeCoins?: false; paymasterUrl: string; // Paymaster service URL paymasterAddress: string; // Paymaster contract address paymasterToken: { address: string }; // ERC-20 token for fees transferMaxFee?: number | bigint; // Maximum fee limit } // Mode 2: Sponsorship Policy interface EvmErc4337WalletSponsorshipPolicyConfig { isSponsored: true; useNativeCoins?: false; paymasterUrl: string; // Paymaster service URL sponsorshipPolicyId?: string; // Sponsorship policy ID } // Mode 3: Native Coins interface EvmErc4337WalletNativeCoinsConfig { isSponsored?: false; useNativeCoins: true; transferMaxFee?: number | bigint; // Maximum fee limit } // Full config type type EvmErc4337WalletConfig = EvmErc4337WalletCommonConfig & (EvmErc4337WalletPaymasterTokenConfig | EvmErc4337WalletSponsorshipPolicyConfig | EvmErc4337WalletNativeCoinsConfig); ``` ### Config Override The `config` parameter on `sendTransaction`, `quoteSendTransaction`, `transfer`, and `quoteTransfer` allows per-call overrides of gas payment settings: ```typescript type ConfigOverride = Partial< EvmErc4337WalletPaymasterTokenConfig | EvmErc4337WalletSponsorshipPolicyConfig | EvmErc4337WalletNativeCoinsConfig >; ``` **Available override fields:** - `isSponsored` (boolean): Switch to sponsorship mode - `useNativeCoins` (boolean): Switch to native coin mode - `paymasterUrl` (string): Override paymaster URL - `paymasterAddress` (string): Override paymaster contract - `paymasterToken` (\{address: string\}): Override paymaster token - `sponsorshipPolicyId` (string): Set sponsorship policy - `transferMaxFee` (number | bigint): Override maximum fee ### EvmErc4337Transaction The transaction shape accepted by `sendTransaction`, `quoteSendTransaction`, and `signTransaction`. Beyond the call fields (`to`, `value`, `data`), it accepts optional gas overrides that are applied to the resulting UserOperation. ```typescript interface EvmErc4337Transaction { to: string; // Recipient address value: number | bigint; // Amount of native coin in wei data?: string; // Call data in hex format (optional) callGasLimit?: number | bigint; // Override the UserOperation call gas limit (optional) verificationGasLimit?: number | bigint; // Override the UserOperation verification gas limit (optional) preVerificationGas?: number | bigint; // Override the UserOperation pre-verification gas (optional) maxFeePerGas?: number | bigint; // Override the UserOperation max fee per gas — EIP-1559 cap (optional) maxPriorityFeePerGas?: number | bigint; // Override the UserOperation max priority fee per gas (optional) } ``` The gas-override fields are optional. When omitted, the gas limits fall back to AbstractionKit's estimation and the fee pair (`maxFeePerGas` / `maxPriorityFeePerGas`) falls back to the bundler-fetched gas price. Setting either fee field disables the bundler-fetched fee fallback for both, so set them together. In a batched call (`tx` passed as an array), only the gas overrides on the first transaction are honored — a UserOperation carries a single set of gas fields regardless of how many calls it batches. ### TransferOptions ```typescript interface TransferOptions { token: string; // ERC20 token contract address recipient: string; // Recipient address amount: number | bigint; // Amount in token base units } ``` ### ApproveOptions ```typescript interface ApproveOptions { token: string; // ERC20 token contract address spender: string; // Address allowed to spend tokens amount: number | bigint; // Amount to approve in base units } ``` ### TransactionResult ```typescript interface TransactionResult { hash: string; // UserOperation hash fee: bigint; // Fee paid } ``` ### TransferResult ```typescript interface TransferResult { hash: string; // UserOperation hash fee: bigint; // Fee paid } ``` ### TypedData ```typescript interface TypedData { domain: TypedDataDomain; // EIP-712 domain separator types: Record; // Type definitions primaryType: string; // Primary type name message: Record; // Structured message data } ``` ### TypedDataDomain ```typescript interface TypedDataDomain { name?: string; // DApp or protocol name version?: string; // Domain version chainId?: number; // Blockchain ID verifyingContract?: string; // Contract address salt?: string; // Optional salt } ``` ### TypedDataField ```typescript interface TypedDataField { name: string; // Field name type: string; // Solidity type (e.g., 'address', 'uint256') } ``` ### UserOperationReceipt ```typescript interface UserOperationReceipt { userOpHash: string; // UserOperation hash sender: string; // Sender address nonce: bigint; // Nonce actualGasUsed: bigint; // Gas used actualGasCost: bigint; // Gas cost success: boolean; // Whether the operation succeeded receipt: EvmTransactionReceipt; // The underlying transaction receipt } ``` ### ConfigurationError ```typescript class ConfigurationError extends Error { // Thrown when the wallet configuration is invalid // e.g., missing required fields for the selected gas payment mode } ``` ### FeeRates ```typescript interface FeeRates { normal: bigint; // Fee rate for normal priority fast: bigint; // Fee rate for fast priority } ``` ### KeyPair ```typescript interface KeyPair { publicKey: Uint8Array; // The public key privateKey: Uint8Array | null; // The private key (null after dispose) } ``` ### Internal Constants The following constants are used internally by the SDK and are **not importable** from the package entry point. ```typescript // Used by predictSafeAddress() for deterministic address generation // Not re-exported from '@tetherto/wdk-wallet-evm-erc-4337' const SALT_NONCE: string = '0x69b348339eea4ed93f9d11931c3b894c8f9d8c7663a053024b11cb7eb4e5a1f6'; // Fee rate multipliers (protected static on WalletManagerEvm) // Applied internally by getFeeRates() const _FEE_RATE_NORMAL_MULTIPLIER: bigint; // ~110% const _FEE_RATE_FAST_MULTIPLIER: bigint; // ~200% ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's EVM with ERC-4337 Wallet Usage Get started with WDK's EVM with ERC-4337 Wallet Configuration *** ## Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-evm-erc-4337 ## Wallet Configuration The `WalletManagerEvmErc4337` requires a complete ERC-4337 configuration object with all required parameters: ```javascript import WalletManagerEvmErc4337 from '@tetherto/wdk-wallet-evm-erc-4337' const config = { // Required parameters chainId: 1, provider: 'https://rpc.mevblocker.io/fast', safeModulesVersion: '0.3.0', // '0.2.0' and '0.3.0' are valid bundlerUrl: `https://api.pimlico.io/v1/ethereum/rpc?apikey=${PIMLICO_API_KEY}`, paymasterUrl: `https://api.pimlico.io/v2/ethereum/rpc?apikey=${PIMLICO_API_KEY}`, paymasterAddress: '0x777777777777AeC03fd955926DbF81597e66834C', transferMaxFee: 100000000000000, paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } } const wallet = new WalletManagerEvmErc4337(seedPhrase, config) ``` ## Account Configuration Both `WalletAccountEvmErc4337` and `WalletAccountReadOnlyEvmErc4337` use the same configuration structure: ```javascript import { WalletAccountEvmErc4337, WalletAccountReadOnlyEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337' // Full access account const account = new WalletAccountEvmErc4337( seedPhrase, "0'/0/0", // BIP-44 derivation path config // Same config as wallet manager ) // Read-only account (transferMaxFee not needed) const readOnlyAccount = new WalletAccountReadOnlyEvmErc4337( '0x...', // Smart contract wallet address { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } // Note: transferMaxFee omitted for read-only accounts } ) ``` ## Configuration Options ### Chain ID The `chainId` option specifies the blockchain network ID. **Required** for fee estimation and smart account initialization. **Type:** `number` **Required:** Yes **Examples:** ```javascript // Ethereum Mainnet const config = { chainId: 1 } // Polygon Mainnet const config = { chainId: 137 } // Arbitrum One const config = { chainId: 42161 } // Avalanche C-Chain const config = { chainId: 43114 } ``` ### Provider The `provider` option specifies the RPC endpoint or EIP-1193 provider instance for blockchain interactions. **Required** for all operations. You can also pass an array of endpoints or providers to enable automatic failover: when a request to one provider fails, the wallet retries the next provider in the list. **Type:** `string | Eip1193Provider | Array` **Required:** Yes **Examples:** ```javascript // Using RPC URL const config = { provider: 'https://rpc.mevblocker.io/fast' } // Using browser provider (MetaMask) const config = { provider: window.ethereum } // Using custom ethers provider import { JsonRpcProvider } from 'ethers' const config = { provider: new JsonRpcProvider('https://rpc.mevblocker.io/fast') } // Using multiple providers for automatic failover const config = { provider: [ 'https://rpc.mevblocker.io/fast', 'https://eth.llamarpc.com' ], retries: 3 // Optional: additional retry attempts after the initial call fails } ``` ### Retries The `retries` option sets the number of additional retry attempts after the initial call fails. It only applies when `provider` is an array of endpoints or providers. Total attempts equal `1 + retries`. If `retries` exceeds the number of providers, the failover loops back and retries already-failed providers in round-robin order. **Type:** `number` **Required:** No (optional) **Default:** `3` ```javascript const config = { provider: [ 'https://rpc.mevblocker.io/fast', 'https://eth.llamarpc.com' ], retries: 5 } ``` ### Bundler URL The `bundlerUrl` option specifies the URL of the ERC-4337 bundler service that handles UserOperation bundling and submission to the mempool. **Required** for transaction processing. **Type:** `string` **Required:** Yes **Example:** ```javascript const config = { bundlerUrl: 'https://api.candide.dev/public/v3/ethereum' } ``` ### Paymaster URL The `paymasterUrl` option specifies the URL of the paymaster service that sponsors transaction fees using ERC-20 tokens or sponsorship policies. **Type:** `string` **Required:** Yes, for Paymaster Token mode and Sponsorship Policy mode. Not used in Native Coins mode. **Example:** ```javascript const config = { paymasterUrl: 'https://api.candide.dev/public/v3/ethereum' } ``` ### Paymaster Address The `paymasterAddress` option specifies the address of the paymaster smart contract. **Type:** `string` **Required:** Yes, for Paymaster Token mode only. Not used in Sponsorship Policy or Native Coins modes. **Example:** ```javascript const config = { paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba' } ``` ### On-Chain Identifier The `onChainIdentifier` option appends a 50-byte project marker to every UserOperation's call data. Pass a string to use it as the project name, or pass an object for more control over the platform and tool fields. **Type:** `string | OnChainIdentifier` **Required:** No (optional) **Properties (object form):** - `project` (string): The project name included in the marker - `platform` (`'Web' | 'Mobile' | 'Safe App' | 'Widget'`, optional): The platform type (default: `'Web'`) - `tool` (string, optional): The tool name used to create the UserOperation - `toolVersion` (string, optional): Semver-style tool version string (e.g., `'1.0.0'`) ```javascript // String form const config = { onChainIdentifier: 'my-project' } // Object form const config = { onChainIdentifier: { project: 'my-project', platform: 'Mobile', tool: 'my-wallet', toolVersion: '1.0.0' } } ``` ### Safe Modules Version The `safeModulesVersion` option specifies the Safe modules version for smart contract wallet implementation. **Required** for smart account initialization. **Type:** `string` **Required:** Yes **Example:** ```javascript const config = { safeModulesVersion: '0.3.0' } ``` ### Paymaster Token The `paymasterToken` option specifies the ERC-20 token used for paying transaction fees through the paymaster. **Type:** `object` **Required:** Yes, for Paymaster Token mode only. Not used in Sponsorship Policy or Native Coins modes. **Properties:** - `address` (string): The ERC-20 token contract address **Example:** ```javascript const config = { paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' // USD₮ } } ``` ### Sponsorship Policy ID The `sponsorshipPolicyId` option specifies the sponsorship policy identifier for sponsored transactions. **Type:** `string` **Required:** No (optional), only used in Sponsorship Policy mode. **Example:** ```javascript const config = { isSponsored: true, sponsorshipPolicyId: 'sp_my_policy_id' } ``` ### Gas Payment Mode Flags These boolean flags control which gas payment mode is used. Only one mode should be active at a time. #### `isSponsored` Enables Sponsorship Policy mode, where a sponsor covers transaction fees. **Type:** `boolean` **Default:** `false` ```javascript const config = { isSponsored: true, paymasterUrl: 'https://api.candide.dev/public/v3/ethereum' } ``` #### `useNativeCoins` Enables Native Coins mode, where the user pays fees in the chain's native currency (ETH, MATIC, etc.). **Type:** `boolean` **Default:** `false` ```javascript const config = { useNativeCoins: true, transferMaxFee: 100000000000000n // Optional: max fee in wei } ``` ### Transfer Max Fee The `transferMaxFee` option sets the maximum fee amount **in paymaster token units** for transfer operations. This prevents transactions with unexpectedly high fees. **Optional** parameter. **Type:** `number | bigint` **Required:** No (optional) **Unit:** Paymaster token base units **Example:** ```javascript const config = { transferMaxFee: 100000 // 100,000 paymaster token units (e.g., 0.1 USD₮ if 6 decimals) } // Usage with error handling try { const result = await account.transfer({ token: '0x...', recipient: '0x...', amount: 1000000 }) } catch (error) { if (error.message.includes('Exceeded maximum fee')) { console.error('Transfer cancelled: Fee too high') } } ``` ## Network-Specific Configurations ### Ethereum Mainnet **Supported Paymaster Tokens** The following tokens are supported for gas payments on Ethereum Mainnet: * **USD₮**: `0xdAC17F958D2ee523a2206206994597C13D831ec7` * **USA₮**: `0x07041776f5007aca2a54844f50503a18a72a8b68` * **XAU₮**: `0x68749665ff8d2d112fa859aa293f07a622782f38` ```javascript const ethereumConfig = { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' // USDT }, transferMaxFee: 100000 // 100,000 paymaster token units (e.g., 0.1 USDT if 6 decimals) } ``` ### Polygon Mainnet ```javascript const polygonConfig = { chainId: 137, provider: 'https://polygon-rpc.com', bundlerUrl: 'https://api.candide.dev/public/v3/polygon', paymasterUrl: 'https://api.candide.dev/public/v3/polygon', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F' // USDT on Polygon }, transferMaxFee: 100000 } ``` ### Arbitrum One ```javascript const arbitrumConfig = { chainId: 42161, provider: 'https://arb1.arbitrum.io/rpc', bundlerUrl: 'https://public.pimlico.io/v2/42161/rpc', paymasterUrl: 'https://public.pimlico.io/v2/42161/rpc', paymasterAddress: '0x777777777777AeC03fd955926DbF81597e66834C', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' // USDT on Arbitrum }, transferMaxFee: 100000 } ``` ### Avalanche C-Chain ``` javascript const avalancheConfig = { chainId: 43114, provider: 'https://avalanche-c-chain-rpc.publicnode.com', bundlerUrl: "https://public.pimlico.io/v2/43114/rpc", paymasterUrl: "https://public.pimlico.io/v2/43114/rpc", paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', safeModulesVersion: '0.3.0', paymasterToken: { address: '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7' // USDT }, transferMaxFee: 100000 // 100,000 paymaster token units (e.g., 0.1 USDT if 6 decimals) } ``` ### Plasma ```javascript // Plasma (example Layer 2) const plasmaConfig = { chainId: 9745, provider: 'https://plasma.drpc.org', // For ERC-4337 support, optional fields: bundlerUrl: 'https://api.candide.dev/public/v3/9745', paymasterUrl: 'https://api.candide.dev/public/v3/9745', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb' // USDT }, transferMaxFee: 100000 // 100,000 paymaster token units (e.g., 0.1 USDT if 6 decimals) } ``` ### Sepolia Testnet (USD₮ ERC-20 mock/testnet only) ````javascript // Pimlico const sepoliaConfigPimlico = { chainId: 11155111, provider: 'https://sepolia.drpc.org', bundlerUrl: 'https://public.pimlico.io/v2/11155111/rpc', paymasterUrl: 'https://public.pimlico.io/v2/11155111/rpc', paymasterAddress: '0x777777777777AeC03fd955926DbF81597e66834C', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xd077a400968890eacc75cdc901f0356c943e4fdb' // USDT Sepolia }, transferMaxFee: 100000 // 0.1 USDT (6 decimals) } // Candide const sepoliaConfigCandide = { chainId: 11155111, provider: 'https://sepolia.drpc.org', bundlerUrl: 'https://api.candide.dev/public/v3/11155111', paymasterUrl: 'https://api.candide.dev/public/v3/11155111', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xd077a400968890eacc75cdc901f0356c943e4fdb' // USDT Sepolia }, transferMaxFee: 100000 } ```` **Important** Ethereum Sepolia is a testnet. The USD₮ tokens available at the links below are not real and do not entitle the holder to anything. In particular, they cannot be redeemed with Tether International, S.A. de C.V. ("Tether International") and are not Tether Tokens as described in [Tether International's Terms of Service](https://tether.to/en/legal). The USD₮ tokens available at the links below on this testnet are intended for testing WDK on Ethereum Sepolia. The links below are links to third-party websites and are Third-Party Information as described in Tether Operations, S.A. de [C.V.'s Website Terms](https://tether.io/terms/) **USD₮ on Sepolia contract:** [0xd077a400968890eacc75cdc901f0356c943e4fdb](https://sepolia.etherscan.io/address/0xd077a400968890eacc75cdc901f0356c943e4fdb) **Get test USD₮:** - [Pimlico faucet](https://dashboard.pimlico.io/test-erc20-faucet) - [Candide faucet](https://dashboard.candide.dev/faucet) Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's EVM with ERC-4337 Wallet Usage Get started with WDK's EVM with ERC-4337 Wallet API *** ## Need Help? *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/guides/check-balances Description: Query native, ERC-20, and paymaster token balances. This guide explains how to check [native token balances](#native-token-balance), [ERC-20 token balances](#erc-20-token-balance), [multiple token balances](#multiple-token-balances), [paymaster token balances](#paymaster-token-balance), and [read-only account balances](#read-only-account-balances). ## Native Token Balance You can retrieve the native token balance (e.g., ETH) using [`account.getBalance()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Get Native Balance" const balance = await account.getBalance() console.log('Native balance:', balance, 'wei') ``` ## ERC-20 Token Balance You can check the balance of a specific ERC-20 token using [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Get ERC-20 Balance" const tokenBalance = await account.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7') // USDT console.log('USDT balance:', tokenBalance) ``` ## Multiple Token Balances You can check balances for multiple ERC-20 tokens in a single call using [`account.getTokenBalances()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Get Multiple Token Balances" const tokenBalances = await account.getTokenBalances([ '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT '0x68749665FF8D2d112Fa859AA293F07A622782F38' // XAUT ]) console.log('Multi-token balances:', tokenBalances) ``` ## Paymaster Token Balance You can check the paymaster token balance used for paying gas fees using [`account.getPaymasterTokenBalance()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Get Paymaster Token Balance" const paymasterBalance = await account.getPaymasterTokenBalance() console.log('Paymaster token balance:', paymasterBalance) ``` The paymaster token balance determines how many gasless transactions you can execute. Ensure the paymaster has sufficient token balance before initiating gasless operations. ## Read-Only Account Balances You can check balances for any smart account address without a seed phrase using [`WalletAccountReadOnlyEvmErc4337`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Read-Only Balance" import { WalletAccountReadOnlyEvmErc4337 } from '@tetherto/wdk-wallet-evm-erc-4337' const readOnlyAccount = new WalletAccountReadOnlyEvmErc4337('0x...', { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' } }) const balance = await readOnlyAccount.getBalance() console.log('Read-only account balance:', balance, 'wei') ``` ## Next Steps With balance checks in place, learn how to [send gasless transactions](/sdk/wallet-modules/wallet-evm-erc-4337/guides/send-transactions). *** ## Get Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/guides/get-started Description: Install and create your first ERC-4337 smart account wallet. This guide explains how to [install the package](#1-install-the-package), [create a wallet](#2-create-a-wallet), [get your first account](#3-get-your-first-account), and optionally [convert to read-only](#4-optional-convert-to-read-only). ## 1. Install the Package ### Prerequisites * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ```bash title="Install @tetherto/wdk-wallet-evm-erc-4337" npm install @tetherto/wdk-wallet-evm-erc-4337 ``` ## 2. Create a Wallet You can create a new wallet instance using the [`WalletManagerEvmErc4337`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) constructor with a BIP-39 seed phrase and ERC-4337 configuration: ```javascript title="Create ERC-4337 Wallet" import WalletManagerEvmErc4337 from '@tetherto/wdk-wallet-evm-erc-4337' const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const wallet = new WalletManagerEvmErc4337(seedPhrase, { chainId: 1, provider: 'https://rpc.mevblocker.io/fast', bundlerUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterUrl: 'https://api.candide.dev/public/v3/ethereum', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', safeModulesVersion: '0.3.0', paymasterToken: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' // USDT } }) ``` **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. To use test/mock tokens instead of real funds, see the [testnet configuration section](/sdk/wallet-modules/wallet-evm-erc-4337/configuration#network-specific-configurations). ## 3. Get Your First Account You can retrieve a smart account at a given index using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Get Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Smart account address:', address) ``` ## 4. (optional) Convert to Read-Only You can convert an owned account to a read-only account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Convert to Read-Only" const readOnlyAccount = await account.toReadOnlyAccount() ``` ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-evm-erc-4337/guides/manage-accounts). *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/guides/handle-errors Description: Handle errors, manage fees, and dispose of sensitive data. This guide explains how to [handle transaction errors](#transaction-errors), [handle transfer errors](#transfer-errors), and follow [best practices](#best-practices) for fee management and memory cleanup. ## Transaction Errors Transactions sent via [`account.sendTransaction()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) can fail when the paymaster token balance is insufficient. Wrap calls in a `try/catch` block: ```javascript title="Handle Transaction Errors" try { const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000n }) console.log('Transaction hash:', result.hash) } catch (error) { if (error.message.includes('not enough funds')) { console.error('Insufficient paymaster token balance') } else { console.error('Transaction failed:', error.message) } } ``` ## Transfer Errors Token transfers via [`account.transfer()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) can fail due to insufficient balance or exceeding the maximum fee limit: ```javascript title="Handle Transfer Errors" try { const result = await account.transfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 }) console.log('Transfer hash:', result.hash) } catch (error) { if (error.message.includes('Exceeded maximum fee')) { console.error('Transfer cancelled: fee exceeds the configured limit') } else if (error.message.includes('not enough funds')) { console.error('Insufficient paymaster token balance') } else { console.error('Transfer failed:', error.message) } } ``` ## Best Practices ### Fee Management You can retrieve current network fee rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Get Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal) console.log('Fast fee rate:', feeRates.fast) ``` ### Dispose of Sensitive Data For security, clear sensitive data from memory when a session is complete. Use [`account.dispose()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) and [`wallet.dispose()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) to securely wipe private keys: ```javascript title="Dispose Resources" try { const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000n }) console.log('Transaction hash:', result.hash) } finally { account.dispose() wallet.dispose() } ``` Always call [`dispose()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) when finished with accounts. Private keys are securely wiped from memory. Disposal is irreversible. *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/guides/manage-accounts Description: Work with multiple smart accounts and custom derivation paths. This guide explains how to [retrieve accounts by index](#retrieve-accounts-by-index) and [use custom derivation paths](#retrieve-account-by-custom-derivation-path). ## Retrieve Accounts by Index You can retrieve multiple smart accounts using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) with different index values: ```javascript title="Retrieve Multiple Accounts" const account0 = await wallet.getAccount(0) const address0 = await account0.getAddress() console.log('Account 0 address:', address0) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` ## Retrieve Account by Custom Derivation Path You can retrieve an account at a specific derivation path using [`wallet.getAccountByPath()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Custom Derivation Path" const customAccount = await wallet.getAccountByPath("0'/0/5") const customAddress = await customAccount.getAddress() console.log('Custom account address:', customAddress) ``` ## Next Steps With accounts set up, learn how to [check balances](/sdk/wallet-modules/wallet-evm-erc-4337/guides/check-balances). *** ## Send Transactions URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/guides/send-transactions Description: Send gasless transactions and estimate fees. This guide explains how to [send a gasless transaction](#send-a-gasless-transaction), [estimate fees](#estimate-fees), and [use a custom paymaster token](#send-with-custom-paymaster-token). ## Send a Gasless Transaction You can send a transaction with gas fees paid in the paymaster token using [`account.sendTransaction()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Send Gasless Transaction" const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000n // 0.001 ETH in wei }) console.log('Transaction hash:', result.hash) console.log('Fee paid in paymaster token:', result.fee) ``` ERC-4337 transactions are gasless for the end user. Gas fees are paid through the configured paymaster using the specified paymaster token (e.g., USD₮). ## Estimate Fees You can estimate the fee for a transaction without broadcasting it using [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Estimate Fee" const quote = await account.quoteSendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000n }) console.log('Estimated fee:', quote.fee) ``` ## Send with Custom Paymaster Token You can override the default paymaster token for a specific transaction by passing a config object to [`account.sendTransaction()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Custom Paymaster Token" const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000n }, { paymasterToken: { address: '0x68749665FF8D2d112Fa859AA293F07A622782F38' // XAUT } }) ``` ## Next Steps Learn how to [transfer ERC-20 tokens](/sdk/wallet-modules/wallet-evm-erc-4337/guides/transfer-tokens). *** ## Sign and Verify Messages URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/guides/sign-verify-messages Description: Sign messages and EIP-712 typed data with smart accounts. This guide explains how to [sign messages](#sign-a-message), [verify signatures](#verify-a-signature), and [sign EIP-712 typed data](#sign-typed-data-eip-712). ## Sign a Message You can sign a message with the account's private key using [`account.sign()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Sign Message" const signature = await account.sign('Hello, ERC-4337!') console.log('Signature:', signature) ``` ## Verify a Signature You can verify a signature using a read-only account. Use [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) to create one, then call [`readOnlyAccount.verify()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Verify Signature" const readOnlyAccount = await account.toReadOnlyAccount() const isValid = await readOnlyAccount.verify('Hello, ERC-4337!', signature) console.log('Signature valid:', isValid) ``` ## Sign Typed Data (EIP-712) You can sign EIP-712 structured data using [`account.signTypedData()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Sign Typed Data" const typedData = { domain: { name: 'MyDApp', version: '1', chainId: 1, verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' }, types: { Mail: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'contents', type: 'string' } ] }, message: { from: '0x1234567890abcdef1234567890abcdef12345678', to: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd', contents: 'Hello!' } } const typedDataSignature = await account.signTypedData(typedData) console.log('Typed data signature:', typedDataSignature) ``` You can verify typed data signatures using [`readOnlyAccount.verifyTypedData()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Verify Typed Data" const readOnlyAccount = await account.toReadOnlyAccount() const isValid = await readOnlyAccount.verifyTypedData(typedData, typedDataSignature) console.log('Typed data signature valid:', isValid) ``` ## Next Steps Learn how to [handle errors and manage resources](/sdk/wallet-modules/wallet-evm-erc-4337/guides/handle-errors). *** ## Transfer Tokens URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/guides/transfer-tokens Description: Transfer ERC-20 tokens with gasless transactions. This guide explains how to [transfer ERC-20 tokens](#transfer-erc-20-tokens), [estimate transfer fees](#estimate-transfer-fees), and [set a maximum fee limit](#transfer-with-maximum-fee-limit). ## Transfer ERC-20 Tokens You can transfer ERC-20 tokens using gasless transactions with [`account.transfer()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Transfer ERC-20 Tokens" const result = await account.transfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 // 1 USDT (6 decimals) }) console.log('Transfer hash:', result.hash) console.log('Transfer fee:', result.fee) ``` ## Estimate Transfer Fees You can estimate the fee for a token transfer without executing it using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Estimate Transfer Fee" const quote = await account.quoteTransfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 }) console.log('Estimated transfer fee:', quote.fee) ``` ## Transfer with Maximum Fee Limit You can set a maximum fee for a specific transfer by passing a `transferMaxFee` in the config object to [`account.transfer()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference): ```javascript title="Transfer with Fee Limit" const result = await account.transfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 }, { transferMaxFee: 100000 }) ``` If the estimated fee exceeds `transferMaxFee`, the transfer is cancelled with an "Exceeded maximum fee" error. ## Next Steps Learn how to [sign and verify messages](/sdk/wallet-modules/wallet-evm-erc-4337/guides/sign-verify-messages). *** ## Wallet EVM ERC-4337 Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm-erc-4337/usage Description: Guide to using the @tetherto/wdk-wallet-evm-erc-4337 module. # Usage The `@tetherto/wdk-wallet-evm-erc-4337` module provides account abstraction wallet management for EVM-compatible blockchains using the ERC-4337 standard. Install the package and create your first smart account. Work with multiple smart accounts and custom derivation paths. Query native, ERC-20, and paymaster token balances. Send gasless transactions and estimate fees. Transfer ERC-20 tokens with gasless transactions. Sign messages and EIP-712 typed data. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's EVM ERC-4337 Wallet Configuration Get started with WDK's EVM ERC-4337 Wallet API *** ### Need Help? *** ## Wallet EVM API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-evm ## Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerEvm](#walletmanagerevm) | Main class for managing EVM wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountEvm](#walletaccountevm) | Individual EVM wallet account implementation. Extends `WalletAccountReadOnlyEvm` and implements `IWalletAccount` from `@tetherto/wdk-wallet`. | [Constructor](#constructor-1), [Methods](#methods-1), [Properties](#properties) | | [WalletAccountReadOnlyEvm](#walletaccountreadonlyevm) | Read-only EVM wallet account. Extends `WalletAccountReadOnly` from `@tetherto/wdk-wallet`. | [Constructor](#constructor-2), [Methods](#methods-2) | ## WalletManagerEvm The main class for managing EVM wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. ### Constructor ```javascript new WalletManagerEvm(seed, config?) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (object, optional): Configuration object - `provider` (`string | Eip1193Provider | Array`, optional): RPC endpoint URL, EIP-1193 provider instance, or ordered failover list - `retries` (number, optional): Additional retry attempts when `provider` is an array - `chainId` (number, optional): Network chain ID. When provided, skips automatic chain ID detection. - `transferMaxFee` (number | bigint, optional): Maximum fee amount for transfer operations (in wei) **Example:** ```javascript const wallet = new WalletManagerEvm(seedPhrase, { provider: 'https://rpc.mevblocker.io/fast', transferMaxFee: 100000000000000 // Maximum fee in wei }) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getRandomSeedPhrase(wordCount?)` | (static) Returns a random BIP-39 seed phrase | `string` | - | | `isValidSeedPhrase(seedPhrase)` | (static) Checks if a seed phrase is valid | `boolean` | - | | `getAccount(index?)` | Returns a wallet account at the specified index | `Promise` | - | | `getAccountByPath(path)` | Returns a wallet account at the specified BIP-44 derivation path | `Promise` | - | | `getFeeRates()` | Returns current fee rates for transactions | `Promise<{normal: bigint, fast: bigint}>` | If no provider is set | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` | - | ### Properties | Property | Type | Description | |----------|------|-------------| | `seed` | `Uint8Array` | The wallet's seed bytes | #### `getRandomSeedPhrase(wordCount?)` (static) Returns a random BIP-39 seed phrase. **Parameters:** - `wordCount` (12 | 24, optional): The number of words in the seed phrase (default: 12) **Returns:** `string` - The seed phrase **Example:** ```javascript const seedPhrase = WalletManagerEvm.getRandomSeedPhrase() console.log('Seed phrase:', seedPhrase) // 12 words const longSeedPhrase = WalletManagerEvm.getRandomSeedPhrase(24) console.log('Long seed phrase:', longSeedPhrase) // 24 words ``` #### `isValidSeedPhrase(seedPhrase)` (static) Checks if a seed phrase is valid. **Parameters:** - `seedPhrase` (string): The seed phrase to validate **Returns:** `boolean` - True if the seed phrase is valid **Example:** ```javascript const isValid = WalletManagerEvm.isValidSeedPhrase('abandon abandon abandon ...') console.log('Valid:', isValid) ``` #### `getAccount(index?)` Returns a wallet account at the specified index following BIP-44 standard. **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise` - The wallet account **Example:** ```javascript // Get first account (index 0) const account = await wallet.getAccount(0) // Get second account (index 1) const account1 = await wallet.getAccount(1) // Get first account (default) const defaultAccount = await wallet.getAccount() ``` #### `getAccountByPath(path)` Returns a wallet account at the specified BIP-44 derivation path. **Parameters:** - `path` (string): The derivation path (e.g., "0'/0/0") **Returns:** `Promise` - The wallet account **Example:** ```javascript // Full path: m/44'/60'/0'/0/1 const account = await wallet.getAccountByPath("0'/0/1") // Custom path: m/44'/60'/0'/0/5 const customAccount = await wallet.getAccountByPath("0'/0/5") ``` #### `getFeeRates()` Returns current fee rates based on network conditions with predefined multipliers. **Returns:** `Promise<{normal: bigint, fast: bigint}>` - Fee rates in wei - `normal`: Base fee × 1.1 (10% above base) - `fast`: Base fee × 2.0 (100% above base) **Throws:** Error if no provider is configured **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'wei') console.log('Fast fee rate:', feeRates.fast, 'wei') // Use in transaction const result = await account.sendTransaction({ to: '0x...', value: 1000000000000000000n, maxFeePerGas: feeRates.fast }) ``` #### `dispose()` Disposes all wallet accounts, clearing private keys from memory. **Example:** ```javascript // Clean up when done wallet.dispose() ``` ## WalletAccountEvm Represents an individual wallet account. Extends `WalletAccountReadOnlyEvm` and implements `IWalletAccount` from `@tetherto/wdk-wallet`. ### Constructor ```javascript new WalletAccountEvm(seed, path, config?) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `path` (string): BIP-44 derivation path (e.g., "0'/0/0") - `config` (object, optional): Configuration object - `provider` (`string | Eip1193Provider | Array`, optional): RPC endpoint URL, EIP-1193 provider instance, or ordered failover list - `retries` (number, optional): Additional retry attempts when `provider` is an array - `chainId` (number, optional): Network chain ID. When provided, skips automatic chain ID detection. - `transferMaxFee` (number | bigint, optional): Maximum fee amount for transfer operations (in wei) **Throws:** - Error if seed phrase is invalid (BIP-39 validation fails) **Example:** ```javascript const account = new WalletAccountEvm(seedPhrase, "0'/0/0", { provider: 'https://rpc.mevblocker.io/fast', transferMaxFee: 100000000000000 }) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getAddress()` | Returns the account's address | `Promise` | - | | `sign(message)` | Signs a message using the account's private key | `Promise` | - | | `signTypedData(typedData)` | Signs typed data according to EIP-712 | `Promise` | - | | `signTransaction(tx)` | Signs an EVM transaction without broadcasting it | `Promise` | If transaction signing fails | | `verify(message, signature)` | Verifies a message signature | `Promise` | - | | `verifyTypedData(typedData, signature)` | Verifies a typed data signature (EIP-712) | `Promise` | - | | `sendTransaction(tx)` | Sends an EVM transaction | `Promise<{hash: string, fee: bigint}>` | If no provider | | `quoteSendTransaction(tx)` | Estimates the fee for an EVM transaction | `Promise<{fee: bigint}>` | If no provider | | `transfer(options)` | Transfers ERC20 tokens to another address | `Promise<{hash: string, fee: bigint}>` | If no provider or fee exceeds max | | `quoteTransfer(options)` | Estimates the fee for an ERC20 transfer | `Promise<{fee: bigint}>` | If no provider | | `getBalance()` | Returns the native token balance (in wei) | `Promise` | If no provider | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific ERC20 token | `Promise` | If no provider | | `getTokenBalances(tokenAddresses)` | Returns balances for multiple ERC20 tokens | `Promise>` | If no provider | | `approve(options)` | Approves a spender to spend tokens | `Promise<{hash: string, fee: bigint}>` | If no provider | | `getAllowance(token, spender)` | Returns current allowance for a spender | `Promise` | If no provider | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise` | If no provider | | `toReadOnlyAccount()` | Returns a read-only copy of the account | `Promise` | - | | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` | - | #### `getAddress()` Returns the account's Ethereum address. **Returns:** `Promise` - Checksummed Ethereum address **Example:** ```javascript const address = await account.getAddress() console.log('Account address:', address) // 0x... ``` #### `sign(message)` Signs a message using the account's private key. **Parameters:** - `message` (string): The message to sign **Returns:** `Promise` - The message signature **Example:** ```javascript const message = 'Hello, Ethereum!' const signature = await account.sign(message) console.log('Signature:', signature) ``` #### `signTypedData(typedData)` Signs typed data according to [EIP-712](https://eips.ethereum.org/EIPS/eip-712). **Parameters:** - `typedData` (TypedData): The typed data to sign - `domain` (TypedDataDomain): The domain separator (name, version, chainId, verifyingContract) - `types` (Record\): The type definitions - `message` (Record\): The message data **Returns:** `Promise` - The typed data signature **Example:** ```javascript const typedData = { domain: { name: 'MyDApp', version: '1', chainId: 1, verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' }, types: { Mail: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'contents', type: 'string' } ] }, message: { from: '0xAlice...', to: '0xBob...', contents: 'Hello Bob!' } } const signature = await account.signTypedData(typedData) console.log('EIP-712 Signature:', signature) ``` #### `signTransaction(tx)` Signs an EVM transaction and returns the signed raw transaction as a hex string. This method does not broadcast the transaction. **Parameters:** - `tx` (EvmTransaction): The transaction object - `to` (string): Recipient address - `value` (number | bigint): Amount in wei - `data` (string, optional): Transaction data in hex format - `gasLimit` (number | bigint, optional): Maximum gas units - `gasPrice` (number | bigint, optional): Legacy gas price in wei - `maxFeePerGas` (number | bigint, optional): EIP-1559 max fee per gas in wei - `maxPriorityFeePerGas` (number | bigint, optional): EIP-1559 max priority fee per gas in wei - `type` (number, optional): Transaction type, such as `4` for ERC-7702 - `nonce` (number, optional): Transaction nonce - `chainId` (number | bigint, optional): Network chain ID - `authorizationList` (AuthorizationLike[], optional): ERC-7702 authorization list for type 4 transactions **Returns:** `Promise` - Signed raw transaction hex string **Example:** ```javascript const signedTransaction = await account.signTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n, chainId: 1 }) console.log('Signed transaction:', signedTransaction) ``` #### `verify(message, signature)` Verifies a message signature against the account's address. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise` - True if signature is valid **Example:** ```javascript const message = 'Hello, Ethereum!' const signature = await account.sign(message) const isValid = await account.verify(message, signature) console.log('Signature valid:', isValid) // true ``` #### `verifyTypedData(typedData, signature)` Verifies a typed data signature according to [EIP-712](https://eips.ethereum.org/EIPS/eip-712). **Parameters:** - `typedData` (TypedData): The typed data that was signed - `signature` (string): The signature to verify **Returns:** `Promise` - True if signature is valid **Example:** ```javascript const isValid = await account.verifyTypedData(typedData, signature) console.log('Typed data signature valid:', isValid) // true ``` #### `sendTransaction(tx)` Sends an EVM transaction and returns the result with hash and fee. **Parameters:** - `tx` (EvmTransaction): The transaction object - `to` (string): Recipient address - `value` (number | bigint): Amount in wei - `data` (string, optional): Transaction data in hex format - `gasLimit` (number | bigint, optional): Maximum gas units - `gasPrice` (number | bigint, optional): Legacy gas price in wei - `maxFeePerGas` (number | bigint, optional): EIP-1559 max fee per gas in wei - `maxPriorityFeePerGas` (number | bigint, optional): EIP-1559 max priority fee per gas in wei - `type` (number, optional): Transaction type, such as `4` for ERC-7702 - `nonce` (number, optional): Transaction nonce - `chainId` (number | bigint, optional): Network chain ID - `authorizationList` (AuthorizationLike[], optional): ERC-7702 authorization list for type 4 transactions **Returns:** `Promise<{hash: string, fee: bigint}>` - Transaction result **Throws:** Error if no provider is configured **Example:** ```javascript // EIP-1559 transaction const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000, // 1 ETH in wei maxFeePerGas: 30000000000, maxPriorityFeePerGas: 2000000000 }) // Legacy transaction const legacyResult = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000, gasPrice: 20000000000, gasLimit: 21000 }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee, 'wei') ``` #### `quoteSendTransaction(tx)` Estimates the fee for an EVM transaction without sending it. **Parameters:** - `tx` (EvmTransaction): The transaction object (same format as sendTransaction) **Returns:** `Promise<{fee: bigint}>` - Fee estimate in wei **Throws:** Error if no provider is configured **Example:** ```javascript const quote = await account.quoteSendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000 }) console.log('Estimated fee:', quote.fee, 'wei') ``` #### `transfer(options)` Transfers ERC20 tokens to another address using the standard transfer function. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): Token contract address - `recipient` (string): Recipient address - `amount` (number | bigint): Amount in token base units **Returns:** `Promise<{hash: string, fee: bigint}>` - Transfer result **Throws:** - Error if no provider is configured - Error if fee exceeds `transferMaxFee` (if configured) **Example:** ```javascript const result = await account.transfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 // 1 USDT (6 decimals) }) console.log('Transfer hash:', result.hash) console.log('Transfer fee:', result.fee, 'wei') ``` #### `quoteTransfer(options)` Estimates the fee for an ERC20 token transfer. **Parameters:** - `options` (TransferOptions): Transfer options (same as transfer) **Returns:** `Promise<{fee: bigint}>` - Fee estimate in wei **Throws:** Error if no provider is configured **Example:** ```javascript const quote = await account.quoteTransfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 }) console.log('Transfer fee estimate:', quote.fee, 'wei') ``` #### `getBalance()` Returns the native token balance (ETH, MATIC, BNB, etc.). **Returns:** `Promise` - Balance in wei **Throws:** Error if no provider is configured **Example:** ```javascript const balance = await account.getBalance() console.log('Balance:', balance, 'wei') console.log('Balance in ETH:', balance / 1000000000000000000) ``` #### `getTokenBalance(tokenAddress)` Returns the balance of a specific ERC20 token using the balanceOf function. **Parameters:** - `tokenAddress` (string): The ERC20 token contract address **Returns:** `Promise` - Token balance in base units **Throws:** Error if no provider is configured **Example:** ```javascript // Get USDT balance const usdtBalance = await account.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7') console.log('USDT balance:', usdtBalance) // In 6 decimal places console.log('USDT balance formatted:', usdtBalance / 1000000, 'USDT') ``` #### `getTokenBalances(tokenAddresses)` Returns balances for multiple ERC20 tokens in one call. **Parameters:** - `tokenAddresses` (string[]): List of ERC20 token contract addresses **Returns:** `Promise>` - Object mapping each token address to its balance in base units **Throws:** Error if no provider is configured **Example:** ```javascript const balances = await account.getTokenBalances([ '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT '0x68749665FF8D2d112Fa859AA293F07A622782F38' // XAUT ]) console.log('USDT:', balances['0xdAC17F958D2ee523a2206206994597C13D831ec7']) console.log('XAUT:', balances['0x68749665FF8D2d112Fa859AA293F07A622782F38']) ``` #### `approve(options)` Approves a specific amount of tokens to a spender. **Parameters:** - `options` (ApproveOptions): Approve options - `token` (string): Token contract address - `spender` (string): Spender address - `amount` (number | bigint): Amount to approve **Returns:** `Promise<{hash: string, fee: bigint}>` - Transaction result **Throws:** - Error if no provider is configured - Error if trying to re-approve USDT on Ethereum without resetting to 0 first **Example:** ```javascript const result = await account.approve({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT spender: '0xSpenderAddress...', amount: 1000000n }) console.log('Approve hash:', result.hash) ``` #### `getAllowance(token, spender)` Returns the current token allowance for the given spender. **Parameters:** - `token` (string): ERC20 token contract address - `spender` (string): The spender's address **Returns:** `Promise` - The current allowance **Throws:** Error if no provider is configured **Example:** ```javascript const allowance = await account.getAllowance( '0xdAC17F958D2ee523a2206206994597C13D831ec7', '0xSpenderContract...' ) console.log('Current allowance:', allowance) ``` #### `getTransactionReceipt(hash)` Returns a transaction receipt by hash. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise` - Transaction receipt or null if not mined **Throws:** Error if no provider is configured **Example:** ```javascript const receipt = await account.getTransactionReceipt('0x...') if (receipt) { console.log('Confirmed in block:', receipt.blockNumber) console.log('Status:', receipt.status) // 1 = success, 0 = failed } ``` #### `toReadOnlyAccount()` Creates a read-only copy of the account with the same configuration. **Returns:** `Promise` - Read-only account instance **Example:** ```javascript const readOnlyAccount = await account.toReadOnlyAccount() // Can check balances but cannot send transactions const balance = await readOnlyAccount.getBalance() // readOnlyAccount.sendTransaction() // Would throw error ``` #### `dispose()` Disposes the wallet account, erasing the private key from memory. **Example:** ```javascript // Clean up when done account.dispose() ``` ### Properties | Property | Type | Description | |----------|------|-------------| | `index` | `number` | The derivation path's index of this account | | `path` | `string` | The full BIP-44 derivation path of this account | | `keyPair` | `{privateKey: Uint8Array \| null, publicKey: Uint8Array}` | The account's key pair (⚠️ Contains sensitive data). The returned arrays are bound to the account — treat them as a read-only view and do not modify their contents. `privateKey` is `null` after `dispose()` is called. | | `address` | `string` | The account's Ethereum address (inherited from `WalletAccountReadOnlyEvm`) | **Example:** ```javascript console.log('Account index:', account.index) // 0, 1, 2, etc. console.log('Account path:', account.path) // m/44'/60'/0'/0/0 // ⚠️ SENSITIVE: Handle with care const { privateKey, publicKey } = account.keyPair console.log('Public key length:', publicKey.length) // 65 bytes if (privateKey !== null) { console.log('Private key length:', privateKey.length) // 32 bytes } ``` ⚠️ **Security Note**: The `keyPair` property contains sensitive cryptographic material. Never log, display, or expose the private key. The byte arrays are bound to the wallet account — do not modify their contents. ## WalletAccountReadOnlyEvm Represents a read-only wallet account that can query balances and estimate fees but cannot send transactions. ### Constructor ```javascript new WalletAccountReadOnlyEvm(address, config?) ``` **Parameters:** - `address` (string): The account's Ethereum address - `config` (`Omit`, optional): Configuration object (same as `EvmWalletConfig` but without `transferMaxFee`, since read-only accounts cannot send transactions) - `provider` (`string | Eip1193Provider | Array`, optional): RPC endpoint URL, EIP-1193 provider instance, or ordered failover list - `retries` (number, optional): Additional retry attempts when `provider` is an array - `chainId` (number, optional): Network chain ID. When provided, skips automatic chain ID detection. **Example:** ```javascript const readOnlyAccount = new WalletAccountReadOnlyEvm('0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', { provider: 'https://rpc.mevblocker.io/fast' }) ``` ### Properties | Property | Type | Description | |----------|------|-------------| | `address` | `string` | The account's Ethereum address | ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getAddress()` | Returns the account's address | `Promise` | - | | `getBalance()` | Returns the native token balance (in wei) | `Promise` | If no provider | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific ERC20 token | `Promise` | If no provider | | `getTokenBalances(tokenAddresses)` | Returns balances for multiple ERC20 tokens | `Promise>` | If no provider | | `quoteSendTransaction(tx)` | Estimates the fee for an EVM transaction | `Promise<{fee: bigint}>` | If no provider | | `quoteTransfer(options)` | Estimates the fee for an ERC20 transfer | `Promise<{fee: bigint}>` | If no provider | | `verify(message, signature)` | Verifies a message signature | `Promise` | - | | `verifyTypedData(typedData, signature)` | Verifies a typed data signature (EIP-712) | `Promise` | - | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise` | If no provider | | `getAllowance(token, spender)` | Returns current allowance for a spender | `Promise` | If no provider | #### `getAddress()` Returns the account's Ethereum address. **Returns:** `Promise` - Checksummed Ethereum address **Example:** ```javascript const address = await readOnlyAccount.getAddress() console.log('Account address:', address) // 0x... ``` #### `getBalance()` Returns the account's native token balance. **Returns:** `Promise` - Balance in wei **Throws:** Error if no provider is configured **Example:** ```javascript const balance = await readOnlyAccount.getBalance() console.log('Balance:', balance, 'wei') ``` #### `getTokenBalance(tokenAddress)` Returns the balance of a specific ERC20 token. **Parameters:** - `tokenAddress` (string): The ERC20 token contract address **Returns:** `Promise` - Token balance in base units **Throws:** Error if no provider is configured **Example:** ```javascript const tokenBalance = await readOnlyAccount.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7') console.log('USDT balance:', tokenBalance) ``` #### `getTokenBalances(tokenAddresses)` Returns balances for multiple ERC20 tokens. **Parameters:** - `tokenAddresses` (string[]): List of ERC20 token contract addresses **Returns:** `Promise>` - Object mapping each token address to its balance in base units **Throws:** Error if no provider is configured **Example:** ```javascript const balances = await readOnlyAccount.getTokenBalances([ '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT '0x68749665FF8D2d112Fa859AA293F07A622782F38' // XAUT ]) console.log('Balances:', balances) ``` #### `quoteSendTransaction(tx)` Estimates the fee for an EVM transaction. **Parameters:** - `tx` (EvmTransaction): The transaction object **Returns:** `Promise<{fee: bigint}>` - Fee estimate in wei **Throws:** Error if no provider is configured **Example:** ```javascript const quote = await readOnlyAccount.quoteSendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000 }) console.log('Estimated fee:', quote.fee, 'wei') ``` #### `quoteTransfer(options)` Estimates the fee for an ERC20 token transfer. **Parameters:** - `options` (TransferOptions): Transfer options **Returns:** `Promise<{fee: bigint}>` - Fee estimate in wei **Throws:** Error if no provider is configured **Example:** ```javascript const quote = await readOnlyAccount.quoteTransfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000 }) console.log('Transfer fee estimate:', quote.fee, 'wei') ``` #### `verify(message, signature)` Verifies a message signature against the account's address. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise` - True if signature is valid **Example:** ```javascript const message = 'Hello, Ethereum!' const signature = await account.sign(message) const readOnlyAccount = new WalletAccountReadOnlyEvm('0x...', { provider: '...' }) const isValid = await readOnlyAccount.verify(message, signature) console.log('Signature valid:', isValid) // true ``` #### `verifyTypedData(typedData, signature)` Verifies a typed data signature according to [EIP-712](https://eips.ethereum.org/EIPS/eip-712). **Parameters:** - `typedData` (TypedData): The typed data that was signed - `signature` (string): The signature to verify **Returns:** `Promise` - True if signature is valid **Example:** ```javascript const isValid = await readOnlyAccount.verifyTypedData(typedData, signature) console.log('Typed data signature valid:', isValid) // true ``` #### `getTransactionReceipt(hash)` Returns a transaction's receipt if it has been mined. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise` - Transaction receipt or null if not yet mined **Throws:** Error if no provider is configured **Example:** ```javascript const receipt = await readOnlyAccount.getTransactionReceipt('0x...') if (receipt) { console.log('Transaction confirmed in block:', receipt.blockNumber) console.log('Gas used:', receipt.gasUsed) console.log('Status:', receipt.status) // 1 = success, 0 = failed } else { console.log('Transaction not yet mined') } ``` #### `getAllowance(token, spender)` Returns the current allowance for the given token and spender. **Parameters:** - `token` (string): The token's address - `spender` (string): The spender's address **Returns:** `Promise` - The allowance **Example:** ```javascript const allowance = await readOnlyAccount.getAllowance( '0xdAC17F958D2ee523a2206206994597C13D831ec7', '0xSpenderAddress...' ) console.log('Allowance:', allowance) ``` ## Types ### EvmTransaction ```typescript interface EvmTransaction { to: string; // The transaction's recipient address value: number | bigint; // The amount of ethers to send (in wei) data?: string; // The transaction's data in hex format (optional) gasLimit?: number | bigint; // Maximum amount of gas this transaction can use (optional) gasPrice?: number | bigint; // Legacy gas price in wei (optional) maxFeePerGas?: number | bigint; // EIP-1559 max fee per gas in wei (optional) maxPriorityFeePerGas?: number | bigint; // EIP-1559 priority fee in wei (optional) type?: number; // Transaction type, such as 4 for ERC-7702 (optional) nonce?: number; // Transaction nonce (optional) chainId?: number | bigint; // Network chain ID (optional) authorizationList?: AuthorizationLike[]; // ERC-7702 authorization list for type 4 transactions (optional) } ``` ### TransferOptions ```typescript interface TransferOptions { token: string; // ERC20 token contract address recipient: string; // Recipient's Ethereum address amount: number | bigint; // Amount in token's base units } ``` ### TransactionResult ```typescript interface TransactionResult { hash: string; // Transaction hash fee: bigint; // Transaction fee paid in wei } ``` ### TransferResult ```typescript interface TransferResult { hash: string; // Transfer transaction hash fee: bigint; // Transfer fee paid in wei } ``` ### FeeRates ```typescript interface FeeRates { normal: bigint; // Normal priority fee rate (base fee × 1.1) fast: bigint; // Fast priority fee rate (base fee × 2.0) } ``` ### KeyPair ```typescript interface KeyPair { privateKey: Uint8Array | null; // Private key as Uint8Array (32 bytes, null after dispose) publicKey: Uint8Array; // Public key as Uint8Array (65 bytes) } ``` ### TypedData ```typescript interface TypedData { domain: TypedDataDomain; // The domain separator types: Record; // The type definitions message: Record; // The message data } ``` ### TypedDataDomain ```typescript interface TypedDataDomain { name?: string; // The domain name (e.g., the DApp name) version?: string; // The domain version chainId?: number | bigint; // The chain ID verifyingContract?: string; // The verifying contract address salt?: string; // An optional salt } ``` ### TypedDataField ```typescript interface TypedDataField { name: string; // The field name type: string; // The field type (e.g., 'address', 'uint256', 'string') } ``` ### EvmWalletConfig ```typescript interface EvmWalletConfig { provider?: string | Eip1193Provider | Array; // RPC URL, EIP-1193 provider, or ordered failover list retries?: number; // Additional retry attempts for provider arrays chainId?: number; // Network chain ID. Skips automatic detection when provided. transferMaxFee?: number | bigint; // Maximum fee for transfers in wei } ``` ### ApproveOptions ```typescript interface ApproveOptions { token: string; // ERC20 token contract address spender: string; // Address allowed to spend tokens amount: number | bigint; // Amount to approve in base units } ``` ### EvmTransactionReceipt ```typescript interface EvmTransactionReceipt { to: string; // Recipient address from: string; // Sender address contractAddress: string | null; // Contract address if contract creation transactionIndex: number; // Transaction index in block gasUsed: bigint; // Gas actually used logsBloom: string; // Bloom filter for logs blockHash: string; // Block hash containing transaction transactionHash: string; // Transaction hash logs: Array; // Event logs blockNumber: number; // Block number confirmations: number; // Number of confirmations cumulativeGasUsed: bigint; // Cumulative gas used in block effectiveGasPrice: bigint; // Effective gas price paid status: number; // Transaction status (1 = success, 0 = failed) type: number; // Transaction type (0 = legacy, 2 = EIP-1559) } ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's EVM Wallet Usage Get started with WDK's EVM Wallet Configuration *** ### Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-evm ## Wallet Configuration The `WalletManagerEvm` accepts a configuration object that defines how the wallet interacts with the blockchain: ```javascript import WalletManagerEvm from '@tetherto/wdk-wallet-evm' const config = { // Recommended: RPC endpoint URL, EIP-1193 provider, or ordered failover list provider: 'https://eth.drpc.org', // Optional: Skip automatic chain ID detection when the network is known chainId: 1, // Optional: Additional failover attempts when provider is an array retries: 2, // Optional: Maximum fee for transfer operations (in wei) transferMaxFee: 100000000000000 // 0.0001 ETH } const wallet = new WalletManagerEvm(seedPhrase, config) ``` ## Account Configuration Both `WalletAccountEvm` and `WalletAccountReadOnlyEvm` share similar configuration options: ```javascript import { WalletAccountEvm, WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm' // Full access account const account = new WalletAccountEvm( seedPhrase, "0'/0/0", // BIP-44 derivation path { provider: 'https://eth.drpc.org', transferMaxFee: 100000000000000 } ) // Read-only account const readOnlyAccount = new WalletAccountReadOnlyEvm( '0x...', // Ethereum address { provider: 'https://eth.drpc.org' } ) ``` ## Configuration Options ### Provider The `provider` option specifies how to connect to the blockchain. It can be a URL string, an EIP-1193 compatible provider instance, or an ordered array of URL strings and EIP-1193 providers for automatic failover. **Type:** `string | Eip1193Provider | Array` **Examples:** ```javascript // Option 1: Using RPC URL const config = { provider: 'https://eth.drpc.org' } // Option 2: Using browser provider (e.g., MetaMask) const config = { provider: window.ethereum } // Option 3: Using a custom EIP-1193 provider // Works in Node.js, Bare, and browsers - zero external dependencies function createFetchProvider(rpcUrl) { let requestId = 0 return { request: async ({ method, params }) => { const response = await fetch(rpcUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: ++requestId, method, params: params || [] }) }) const data = await response.json() if (data.error) throw new Error(data.error.message) return data.result } } } const config = { provider: createFetchProvider('https://eth.drpc.org') } // Option 4: Using ordered provider failover const config = { provider: [ 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY', 'https://eth.drpc.org', createFetchProvider('https://ethereum.publicnode.com') ], retries: 2 } ``` When `provider` is an array, the wallet uses the candidates in order and retries connection failures against the next provider. If `retries` is greater than the number of providers, the failover loop wraps around in round-robin order. ### Retries The `retries` option controls how many additional attempts can happen after the first provider call fails. It only applies when `provider` is an array. **Type:** `number` (optional) **Default:** `3` **Example:** ```javascript const config = { provider: [ 'https://primary.example', 'https://secondary.example' ], retries: 1 } ``` ### Chain ID The `chainId` option pins the provider to a known EVM chain ID. Use it when you already know the target network and want to skip automatic chain ID detection during provider setup. **Type:** `number` (optional) **Example:** ```javascript const config = { provider: 'https://polygon-rpc.com', chainId: 137 } ``` ### Transfer Max Fee The `transferMaxFee` option sets a maximum limit for transaction fees to prevent unexpectedly high costs. **Type:** `number | bigint` (optional) **Unit:** Wei (1 ETH = 1000000000000000000 Wei) **Examples:** ```javascript const config = { // Set maximum fee to 0.0001 ETH transferMaxFee: 100000000000000n, } // Usage example try { const result = await account.transfer({ token: '0x...', // ERC20 address recipient: '0x...', amount: 1000000n }) } catch (error) { if (error.message.includes('Exceeded maximum fee')) { console.error('Transfer cancelled: Fee too high') } } ``` ### Fee Rate Multipliers The wallet manager uses predefined multipliers for fee calculations: ```javascript // Normal fee rate = base fee × 1.1 const normalFee = await wallet.getFeeRates() console.log('Normal fee:', normalFee.normal) // Fast fee rate = base fee × 2.0 const fastFee = await wallet.getFeeRates() console.log('Fast fee:', fastFee.fast) ``` ## Network Support The configuration works with any EVM-compatible network. Just change the provider URL: ```javascript // Ethereum Mainnet const mainnetConfig = { provider: 'https://eth.drpc.org' } // Polygon (Matic) const polygonConfig = { provider: 'https://polygon-rpc.com' } // Arbitrum const arbitrumConfig = { provider: 'https://arb1.arbitrum.io/rpc' } // BSC (Binance Smart Chain) const bscConfig = { provider: 'https://bsc-dataseed.binance.org' } // Avalanche C-Chain const avalancheConfig = { provider: 'https://avalanche-c-chain-rpc.publicnode.com', } // Plasma const plasmaConfig = { provider: 'https://plasma.drpc.org', } // Stable (uses USD₮ as native gas token) // No need for ERC-4337 paymaster/bundler setup. const stableConfig = { provider: 'https://rpc.stable.xyz', } // Sepolia Testnet const sepoliaConfig = { provider: 'https://sepolia.drpc.org', } ``` ## Next Steps Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's EVM Wallet Usage Get started with WDK's EVM Wallet API *** ### Need Help? *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/guides/check-balances Description: Query native and ERC-20 token balances on EVM chains. This guide explains how to check native token and ERC-20 token balances for both owned and read-only accounts. ## Owned Account Balances Use an account retrieved from [`WalletManagerEvm`](/sdk/wallet-modules/wallet-evm/api-reference) to query balances. ### Native Token Balance You can retrieve the native token balance from an `Account` object using [`account.getBalance()`](/sdk/wallet-modules/wallet-evm/api-reference): ```javascript title="Get Native Balance" const balance = await account.getBalance() console.log('Native balance:', balance, 'wei') ``` ### Single ERC-20 Token Balance You can retrieve a single ERC-20 token balance from an `Account` object using [`account.getTokenBalance(tokenAddress)`](/sdk/wallet-modules/wallet-evm/api-reference#gettokenbalancetokenaddress): ```javascript title="Get ERC-20 Balance" const tokenAddress = '0xdAC17F958D2ee523a2206206994597C13D831ec7' // USDT const tokenBalance = await account.getTokenBalance(tokenAddress) console.log('Token balance:', tokenBalance) ``` ### Multiple ERC-20 Token Balances You can retrieve multiple ERC-20 token balances from an `Account` object using [`account.getTokenBalances(tokenAddresses)`](/sdk/wallet-modules/wallet-evm/api-reference#gettokenbalancestokenaddresses), where `tokenAddresses` is an array of ERC-20 tokens: ```javascript title="Get Multiple Token Balances" const tokenBalances = await account.getTokenBalances([ '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT '0x68749665FF8D2d112Fa859AA293F07A622782F38' // XAUT ]) console.log('Multi-token balances:', tokenBalances) ``` ## Read-Only Account Balances Use [`WalletAccountReadOnlyEvm`](/sdk/wallet-modules/wallet-evm/api-reference) to check balances for any public address without a seed phrase. ### Native Balance ```javascript title="Read-Only Native Balance" import { WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm' const readOnlyAccount = new WalletAccountReadOnlyEvm('0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', { provider: 'https://rpc.mevblocker.io/fast', }) const balance = await readOnlyAccount.getBalance() console.log('Native balance:', balance, 'wei') ``` ### Single Token Balance ```javascript title="Read-Only Token Balance" const tokenBalance = await readOnlyAccount.getTokenBalance('0xdAC17F958D2ee523a2206206994597C13D831ec7') console.log('Token balance:', tokenBalance) ``` ### Multiple Token Balances ```javascript title="Read-Only Multiple Token Balances" const tokenBalances = await readOnlyAccount.getTokenBalances([ '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT '0x68749665FF8D2d112Fa859AA293F07A622782F38' // XAUT ]) console.log('Multi-token balances:', tokenBalances) ``` You can also create a read-only account from an existing owned account using [`await account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-evm/api-reference). ## Next Steps With balance checks in place, learn how to [send transactions](/sdk/wallet-modules/wallet-evm/guides/send-transactions). *** ## Error Handling URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/guides/error-handling Description: Handle errors, manage fees, and dispose of sensitive data in EVM wallets. This guide covers best practices for handling transaction errors, managing fee limits, and cleaning up sensitive data from memory. ## Handle Transaction Errors Wrap transactions in `try/catch` blocks to handle common failure scenarios such as insufficient funds or exceeded fee limits. ```javascript title="Transaction Error Handling" try { const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n }) console.log('Transaction successful:', result.hash) } catch (error) { console.error('Transaction failed:', error.message) if (error.message.includes('insufficient funds')) { console.log('Please add more funds to your wallet') } if (error.message.includes('Exceeded maximum fee')) { console.log('Transfer fee too high') } } ``` ## Handle Token Transfer Errors Token transfers can fail for additional reasons such as invalid addresses or insufficient token balances. ```javascript title="Token Transfer Error Handling" try { const result = await account.transfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000000000000000n }) console.log('Transfer completed:', result.hash) } catch (error) { console.error('Transfer failed:', error.message) if (error.message.includes('Exceeded maximum fee')) { console.log('Transfer fee too high') } } ``` ## Manage Fee Limits Set `transferMaxFee` when creating the wallet to prevent transactions from exceeding a maximum gas cost. Retrieve current network rates with [`getFeeRates()`](/sdk/wallet-modules/wallet-evm/api-reference) to make informed decisions. ```javascript title="Fee Management" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'wei') console.log('Fast fee rate:', feeRates.fast, 'wei') ``` ## Dispose of Sensitive Data Call [`dispose()`](/sdk/wallet-modules/wallet-evm/api-reference) on accounts and wallet managers to clear private keys and sensitive data from memory when they are no longer needed. ```javascript title="Memory Cleanup" account.dispose() wallet.dispose() ``` Always call [`dispose()`](/sdk/wallet-modules/wallet-evm/api-reference) in a `finally` block or cleanup handler to ensure sensitive data is cleared even if an error occurs. *** ## Getting Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/guides/getting-started Description: Install and create your first EVM wallet. This guide explains how to install the [`@tetherto/wdk-wallet-evm`](https://www.npmjs.com/package/@tetherto/wdk-wallet-evm) package and create a new wallet instance. ## 1. Installation ### Prerequisites Before you begin, ensure you have the following installed: * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ### Install Package ```bash title="Install @tetherto/wdk-wallet-evm" npm install @tetherto/wdk-wallet-evm ``` ## 2. Create a Wallet Import the module and create a [`WalletManagerEvm`](/sdk/wallet-modules/wallet-evm/api-reference) instance with a BIP-39 seed phrase and an RPC provider. ```javascript title="Create EVM Wallet" import WalletManagerEvm, { WalletAccountEvm, WalletAccountReadOnlyEvm } from '@tetherto/wdk-wallet-evm' const seedPhrase = 'your twelve word seed phrase here' const wallet = new WalletManagerEvm(seedPhrase, { provider: 'https://rpc.mevblocker.io/fast', transferMaxFee: 100000000000000 // Optional: maximum fee in wei }) ``` **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. You can also pass an EIP-1193 provider (e.g., from a browser wallet) instead of an RPC URL: ```javascript title="Use EIP-1193 Provider" const wallet = new WalletManagerEvm(seedPhrase, { provider: window.ethereum, transferMaxFee: 100000000000000 }) ``` ## 3. Get Your First Account Retrieve an account from the wallet and inspect its address. ```javascript title="Get Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Wallet address:', address) const readOnlyAccount = await account.toReadOnlyAccount() ``` **RPC Providers:** The examples use public RPC endpoints for demonstration. We do not endorse any specific provider. * **Testnets:** You can find public RPCs for Ethereum and other EVM chains on [Chainlist](https://chainlist.org). * **Mainnet:** For production environments, we recommend using reliable, paid RPC providers to ensure stability. To use test/mock tokens instead of real funds, see the [configuration section](/sdk/wallet-modules/wallet-evm/configuration#network-support). ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-evm/guides/manage-accounts). *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/guides/manage-accounts Description: Work with multiple EVM accounts and custom derivation paths. This guide explains how to retrieve multiple accounts from your EVM wallet and use custom derivation paths. ## Retrieve Accounts by Index Use `getAccount()` with a zero-based index to access accounts derived from the default BIP-44 path (`m/44'/60'/0'/0/{index}`). ```javascript title="Get Accounts by Index" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Account 0 address:', address) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` ## Retrieve Account by Custom Derivation Path Use `getAccountByPath()` when you need a specific hierarchy beyond the default sequential index. ```javascript title="Custom Derivation Path" const customAccount = await wallet.getAccountByPath("0'/0/5") const customAddress = await customAccount.getAddress() console.log('Custom account address:', customAddress) ``` ## Iterate Over Multiple Accounts You can loop through accounts to inspect addresses and balances in bulk. ```javascript title="Multi-Account Iteration" async function listAccounts(wallet) { const accounts = [] for (let i = 0; i < 5; i++) { const account = await wallet.getAccount(i) const address = await account.getAddress() const balance = await account.getBalance() accounts.push({ index: i, path: `m/44'/60'/0'/0/${i}`, address, balance }) console.log(`Account ${i}:`, { address, balance: balance.toString() }) } return accounts } ``` ## Next Steps Now that you can access your accounts, learn how to [check balances](/sdk/wallet-modules/wallet-evm/guides/check-balances). *** ## Send Transactions URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/guides/send-transactions Description: Send native tokens on EVM chains with EIP-1559 or legacy gas settings. This guide explains how to [send EVM transactions with EIP-1559 gas parameters](#send-with-eip-1559-gas-parameters), [send legacy gas transactions](#send-with-legacy-gas-parameters), [sign without broadcasting](#sign-without-broadcasting), [estimate fees](#estimate-transaction-fees), and [use dynamic fee rates](#use-dynamic-fee-rates). **BigInt Usage:** Always use `BigInt` (the `n` suffix) for monetary values to avoid precision loss with large numbers. ## Send with EIP-1559 Gas Parameters You can use [`account.sendTransaction()`](/sdk/wallet-modules/wallet-evm/api-reference#sendtransactiontx) to send an EIP-1559 transaction. EIP-1559 transactions provide more predictable gas fees and faster inclusion times. ```javascript title="EIP-1559 Transaction" const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n, // 1 ETH in wei maxFeePerGas: 30000000000, maxPriorityFeePerGas: 2000000000 }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee, 'wei') ``` ## Send with Legacy Gas Parameters You can also use [`account.sendTransaction()`](/sdk/wallet-modules/wallet-evm/api-reference#sendtransactiontx) with legacy gas settings for chains that do not support EIP-1559. ```javascript title="Legacy Transaction" const legacyResult = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n, gasPrice: 20000000000n, gasLimit: 21000 }) console.log('Transaction hash:', legacyResult.hash) ``` ## Sign Without Broadcasting Use [`account.signTransaction()`](/sdk/wallet-modules/wallet-evm/api-reference#signtransactiontx) when you need a signed raw transaction but want to submit it through a separate relay, service, or review flow. ```javascript title="Sign EVM Transaction" const signedTransaction = await account.signTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n, chainId: 1, maxFeePerGas: 30000000000n, maxPriorityFeePerGas: 2000000000n }) console.log('Signed transaction:', signedTransaction) ``` `signTransaction()` returns the signed transaction payload and does not broadcast it. Use `sendTransaction()` when WDK should sign, broadcast, and return the transaction hash. ## Estimate Transaction Fees Use [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-evm/api-reference#quotesendtransactiontx) to get a fee estimate before sending. ```javascript title="Quote Transaction Fee" const quote = await account.quoteSendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n }) console.log('Estimated fee:', quote.fee, 'wei') ``` ## Use Dynamic Fee Rates Retrieve current fee rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-evm/api-reference#getfeerates) and apply them to your transaction. ```javascript title="Dynamic Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'wei') console.log('Fast fee rate:', feeRates.fast, 'wei') const result = await account.sendTransaction({ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', value: 1000000000000000000n, data: '0x', gasLimit: 21000, maxFeePerGas: feeRates.fast, maxPriorityFeePerGas: 2000000000n }) console.log('Transaction sent:', result.hash) console.log('Fee paid:', result.fee, 'wei') ``` **Gas Estimation:** The `maxFeePerGas` and `maxPriorityFeePerGas` fields enable EIP-1559 transactions, ensuring more predictable gas fees and faster inclusion times. ## Next Steps To transfer ERC-20 tokens instead of native tokens, see [Transfer ERC-20 Tokens](/sdk/wallet-modules/wallet-evm/guides/transfer-tokens). *** ## Sign and Verify Messages URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/guides/sign-verify-messages Description: Sign messages and verify signatures with EVM accounts. This guide explains how to sign arbitrary messages with an owned account and verify signatures using a read-only account. ## Sign a Message Use [`account.sign()`](/sdk/wallet-modules/wallet-evm/api-reference#signmessage) to produce a cryptographic signature for any string message. ```javascript title="Sign a Message" const message = 'Hello, Ethereum!' const signature = await account.sign(message) console.log('Signature:', signature) ``` ## Verify a Signature You can get a [read-only account](/sdk/wallet-modules/wallet-evm/api-reference#walletaccountreadonlyevm) from any `Account` object by calling [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-evm/api-reference#toreadonlyaccount). Use a read-only account to [`verify()`](/sdk/wallet-modules/wallet-evm/api-reference#verifymessage-signature-1) that a signature was produced by the corresponding private key. ```javascript title="Verify a Signature" const readOnlyAccount = await account.toReadOnlyAccount() const isValid = await readOnlyAccount.verify(message, signature) console.log('Signature valid:', isValid) ``` You can also create a [`WalletAccountReadOnlyEvm`](/sdk/wallet-modules/wallet-evm/api-reference) from any public address to verify signatures without access to the private key. ## Next Steps For best practices on handling errors, managing fees, and cleaning up memory, see [Error Handling](/sdk/wallet-modules/wallet-evm/guides/error-handling). *** ## Transfer ERC-20 Tokens URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/guides/transfer-tokens Description: Transfer ERC-20 tokens and estimate transfer fees on EVM chains. This guide explains how to transfer ERC-20 tokens (such as USD₮ or XAU₮), estimate fees, and validate inputs before executing. ## Transfer Tokens Use [`account.transfer()`](/sdk/wallet-modules/wallet-evm/api-reference#transferoptions) to send ERC-20 tokens to a recipient address. ```javascript title="Transfer ERC-20 Tokens" const transferResult = await account.transfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000000000000000n // 1 token in base units }) console.log('Transfer hash:', transferResult.hash) console.log('Transfer fee:', transferResult.fee, 'wei') ``` ## Estimate Transfer Fees Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-evm/api-reference#quotetransferoptions) to get a fee estimate before executing the transfer. ```javascript title="Quote Token Transfer" const transferQuote = await account.quoteTransfer({ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', amount: 1000000000000000000n }) console.log('Transfer fee estimate:', transferQuote.fee, 'wei') ``` ## Transfer with Validation You can use `transferTokenWithValidation()` to validate addresses and check balances before transferring to catch errors early. ### 1. Validate Addresses ```javascript title="Address Validation" if (!tokenAddress.startsWith('0x') || tokenAddress.length !== 42) { throw new Error('Invalid token address') } if (!recipient.startsWith('0x') || recipient.length !== 42) { throw new Error('Invalid recipient address') } ``` ### 2. Check Balances Use [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-evm/api-reference#gettokenbalancetokenaddress) and [`account.getBalance()`](/sdk/wallet-modules/wallet-evm/api-reference#getbalance) to verify sufficient funds: ```javascript title="Balance Check" const balance = await account.getTokenBalance(tokenAddress) if (balance < amount) { throw new Error('Insufficient token balance') } const nativeBalance = await account.getBalance() if (nativeBalance === 0n) { throw new Error('Need ETH for gas fees') } ``` ### 3. Quote and Execute Transfer Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-evm/api-reference#quotetransferoptions) to estimate fees, then [`account.transfer()`](/sdk/wallet-modules/wallet-evm/api-reference#transferoptions) to execute: ```javascript title="Quote and Execute" const quote = await account.quoteTransfer({ token: tokenAddress, recipient, amount }) console.log('Transfer fee estimate:', quote.fee, 'wei') const result = await account.transfer({ token: tokenAddress, recipient, amount }) console.log('Transfer completed:', result.hash) console.log('Fee paid:', result.fee, 'wei') ``` ## Next Steps Learn how to [sign and verify messages](/sdk/wallet-modules/wallet-evm/guides/sign-verify-messages) with your EVM account. *** ## Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-evm/usage Description: Guide to using the @tetherto/wdk-wallet-evm module. The `@tetherto/wdk-wallet-evm` module provides wallet management for Ethereum and EVM-compatible blockchains. Install the package and create your first wallet. Work with multiple accounts and custom derivation paths. Query native and ERC-20 token balances. Send native tokens with EIP-1559 or legacy gas settings. Transfer ERC-20 tokens and estimate fees. Sign messages and verify signatures. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's EVM Wallet Configuration Get started with WDK's EVM Wallet API --- ### Need Help? *** ## Wallet Solana Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana Description: Overview of the @tetherto/wdk-wallet-solana module A simple and secure package to manage SLIP-0010 wallets for the Solana blockchain. This package provides a clean API for creating, managing, and interacting with Solana wallets using BIP-39 seed phrases and Solana-specific derivation paths. **Default Derivation Path Change in v1.0.0-beta.4+** The default derivation path was updated in v1.0.0-beta.4 to match ecosystem conventions: - **Before** (up to v1.0.0-beta.3): `m/44'/501'/0'/0/{index}` - **After** (v1.0.0-beta.4+): `m/44'/501'/{index}'/0'` If you're upgrading from an earlier version, existing wallets created with the old path will generate different addresses. Make sure to migrate any existing wallets or use the old path explicitly if needed for compatibility. Use [`getAccountByPath`](/sdk/wallet-modules/wallet-solana/api-reference#getaccountbypathpath) to supply an explicit derivation path when importing or recreating legacy wallets. On Solana, every child segment in a custom path must be hardened. ## Features - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases - **Solana Derivation Paths**: Support for SLIP-0010 derivation paths for Solana (m/44'/501') - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase - **Solana Address Support**: Generate and manage Solana public keys and addresses - **Message Signing**: Sign and verify messages using Ed25519 cryptography - **Transaction Management**: Sign, send, and quote Solana transactions - **SPL Token Support**: Query native SOL plus single or batch SPL token balances - **TypeScript Support**: Full TypeScript definitions included - **Memory Safety**: Secure private key management with memory-safe implementation - **Provider Flexibility**: Support for a single Solana RPC endpoint, plus runtime failover support for ordered `provider` lists - **Transaction Message Support**: Quote or send prebuilt `TransactionMessage` flows with recent blockhash or durable nonce lifetimes - **Fee Estimation**: Dynamic fee calculation with recent blockhash - **Program Interaction**: Support for interacting with Solana programs ## Supported Networks This package works with the Solana blockchain, including: - **Solana Mainnet** - **Solana Devnet** - **Solana Testnet** - **Localnet** ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's Solana Wallet configuration Get started with WDK's Solana Wallet API Get started with WDK's with Solana Wallet usage *** ### Need Help? *** ## Wallet Solana API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-solana ### Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerSolana](#walletmanagersolana) | Main class for managing Solana wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountSolana](#walletaccountsolana) | Individual Solana wallet account implementation. Extends `WalletAccountReadOnlySolana` and implements `IWalletAccount`. | [Constructor](#constructor-1), [Methods](#methods-1), [Properties](#properties) | | [WalletAccountReadOnlySolana](#walletaccountreadonlysolana) | Read-only Solana wallet account. | [Constructor](#constructor-2), [Methods](#methods-2) | ### WalletManagerSolana The main class for managing Solana wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. #### Constructor ```javascript new WalletManagerSolana(seed, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (object): Configuration object - `provider` (string | string[], optional): Solana RPC endpoint URL or an ordered list of endpoints for failover - `rpcUrl` (string | string[], optional): Deprecated alias for `provider`. If both are set, `provider` takes precedence - `commitment` (string, optional): Commitment level ('processed', 'confirmed', or 'finalized') - `retries` (number, optional): Additional retry attempts for ordered provider failover (default: 3) - `transferMaxFee` (number, optional): Maximum fee amount for transfer operations (in lamports) **Example:** ```javascript const wallet = new WalletManagerSolana(seedPhrase, { provider: 'https://api.mainnet-beta.solana.com', commitment: 'confirmed', transferMaxFee: 5000 // Maximum fee in lamports }) ``` #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAccount(index)` | Returns a wallet account at the specified index | `Promise` | | `getAccountByPath(path)` | Returns a wallet account at the specified SLIP-0010 derivation path | `Promise` | | `getFeeRates()` | Returns current fee rates for transactions | `Promise<{normal: bigint, fast: bigint}>` | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` | ##### `getAccount(index)` Returns a wallet account at the specified index. **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise` - The wallet account **Example:** ```javascript const account = await wallet.getAccount(0) ``` ##### `getAccountByPath(path)` Returns a wallet account at the specified SLIP-0010 derivation path. **Parameters:** - `path` (string): The derivation path (e.g., "0'/0'/0'"). On Solana, every child segment must be hardened. **Returns:** `Promise` - The wallet account **Example:** ```javascript const account = await wallet.getAccountByPath("0'/0'/1'") ``` ##### `getFeeRates()` Returns current fee rates for transactions based on recent prioritization fees. **Returns:** `Promise<{normal: bigint, fast: bigint}>` - Object containing fee rates in lamports **Throws:** Error if wallet is not connected to a provider **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'lamports') console.log('Fast fee rate:', feeRates.fast, 'lamports') ``` ##### `dispose()` Disposes all wallet accounts, clearing private keys from memory. **Example:** ```javascript wallet.dispose() ``` ### WalletAccountSolana Represents an individual Solana wallet account. Extends `WalletAccountReadOnlySolana` and implements `IWalletAccount`. #### Constructor ```javascript new WalletAccountSolana(seed, path, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `path` (string): SLIP-0010 derivation path (e.g., "0'/0'/0'") - `config` (SolanaWalletConfig, optional): Configuration object In v1.0.0-beta.9 the constructor was made public. The static factory method `WalletAccountSolana.at()` still works but is deprecated; use the constructor directly instead. #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's Solana address | `Promise` | | `sign(message)` | Signs a message using the account's private key | `Promise` | | `signTransaction(tx)` | Signs a Solana transaction without broadcasting it | `Promise` | | `verify(message, signature)` | Verifies a message signature | `Promise` | | `sendTransaction(tx)` | Sends a Solana transaction | `Promise<{hash: string, fee: bigint}>` | | `quoteSendTransaction(tx)` | Estimates the fee for a transaction | `Promise<{fee: bigint}>` | | `transfer(options)` | Transfers SPL tokens to another address | `Promise<{hash: string, fee: bigint}>` | | `quoteTransfer(options)` | Estimates the fee for an SPL token transfer | `Promise<{fee: bigint}>` | | `getBalance()` | Returns the native SOL balance (in lamports) | `Promise` | | `getTokenBalance(tokenMint)` | Returns the balance of a specific SPL token | `Promise` | | `getTokenBalances(tokenAddresses)` | Returns balances for multiple SPL tokens | `Promise>` | | `getTransactionReceipt(hash)` | Gets the transaction receipt for a given transaction hash | `Promise` | | `toReadOnlyAccount()` | Returns a read-only copy of the account | `Promise` | | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` | ##### `getAddress()` Returns the account's Solana address. **Returns:** `Promise` - The account's base58-encoded Solana address **Example:** ```javascript const address = await account.getAddress() console.log('Account address:', address) ``` ##### `sign(message)` Signs a message using the account's private key. **Parameters:** - `message` (string): The message to sign **Returns:** `Promise` - The message signature (hex-encoded) **Example:** ```javascript const signature = await account.sign('Hello, Solana!') console.log('Signature:', signature) ``` ##### `signTransaction(tx)` Signs a Solana transaction without broadcasting it. Use this method when a relay, review flow, or separate submission path needs a fully signed transaction. **Parameters:** - `tx` (SolanaTransaction): A simple transfer object or a prebuilt `TransactionMessage` When `tx` is a `TransactionMessage`, WDK preserves an existing recent blockhash or durable nonce lifetime. If no lifetime is present, WDK fetches the latest blockhash before signing. If you set an explicit `feePayer`, it must match the wallet address. **Returns:** `Promise` - The signed transaction **Throws:** Error if wallet is not connected to a provider **Example:** ```javascript const signedTransaction = await account.signTransaction({ to: '11111111111111111111111111111112', value: 1000000000 // 1 SOL in lamports }) console.log('Signed transaction:', signedTransaction) ``` ##### `verify(message, signature)` Verifies a message signature against the account's address. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify (hex-encoded) **Returns:** `Promise` - True if the signature is valid **Example:** ```javascript const isValid = await account.verify('Hello, Solana!', signature) console.log('Signature valid:', isValid) ``` ##### `sendTransaction(tx)` Sends a Solana transaction. **Parameters:** - `tx` (SolanaTransaction): A simple transfer object or a prebuilt `TransactionMessage` - `to` (string): Recipient's Solana address (base58-encoded) - `value` (number | bigint): Amount in lamports When `tx` is a `TransactionMessage`, WDK preserves an existing recent blockhash or durable nonce lifetime. If no lifetime is present, WDK fetches the latest blockhash before quoting or sending. If you set an explicit `feePayer`, it must match the wallet address. **Returns:** `Promise<{hash: string, fee: bigint}>` - Object containing transaction hash and fee (in lamports) **Throws:** Error if wallet is not connected to a provider **Example:** ```javascript const result = await account.sendTransaction({ to: '11111111111111111111111111111112', value: 1000000000 // 1 SOL in lamports }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee, 'lamports') ``` ##### `quoteSendTransaction(tx)` Estimates the fee for a Solana transaction. **Parameters:** - `tx` (SolanaTransaction): The transaction object (same as `sendTransaction`) **Returns:** `Promise<{fee: bigint}>` - Object containing fee estimate (in lamports) **Example:** ```javascript const quote = await account.quoteSendTransaction({ to: '11111111111111111111111111111112', value: 1000000000 }) console.log('Estimated fee:', quote.fee, 'lamports') ``` ##### `transfer(options)` Transfers SPL tokens to another address. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): Token mint address (base58-encoded) - `recipient` (string): Recipient's Solana address (base58-encoded) - `amount` (number | bigint): Amount in token's base units **Returns:** `Promise<{hash: string, fee: bigint}>` - Object containing transaction hash and fee (in lamports) **Throws:** Error if wallet is not connected to a provider or if fee exceeds maximum **Example:** ```javascript const result = await account.transfer({ token: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', // USDT mint recipient: '11111111111111111111111111111112', amount: 1000000 // 1 USDT (6 decimals) }) console.log('Transfer hash:', result.hash) console.log('Transfer fee:', result.fee, 'lamports') ``` ##### `quoteTransfer(options)` Estimates the fee for an SPL token transfer. **Parameters:** - `options` (TransferOptions): Transfer options (same as transfer) **Returns:** `Promise<{fee: bigint}>` - Object containing fee estimate (in lamports) **Example:** ```javascript const quote = await account.quoteTransfer({ token: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', recipient: '11111111111111111111111111111112', amount: 1000000 }) console.log('Transfer fee estimate:', quote.fee, 'lamports') ``` ##### `getBalance()` Returns the native SOL balance (in lamports). **Returns:** `Promise` - Balance in lamports **Example:** ```javascript const balance = await account.getBalance() console.log('SOL balance:', balance, 'lamports') ``` ##### `getTokenBalance(tokenMint)` Returns the balance of a specific SPL token. **Parameters:** - `tokenMint` (string): Token mint address (base58-encoded) **Returns:** `Promise` - Token balance in base units **Example:** ```javascript const tokenBalance = await account.getTokenBalance('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB') console.log('USDT balance:', tokenBalance) ``` ##### `getTokenBalances(tokenAddresses)` Returns balances for multiple SPL tokens. The wallet batches associated token account lookups with `getMultipleAccounts`, returns balances in base units, and reports `0n` for token accounts that do not exist. **Parameters:** - `tokenAddresses` (string[]): Token mint addresses (base58-encoded) **Returns:** `Promise>` - Mapping of token mint address to token balance in base units **Example:** ```javascript const tokenBalances = await account.getTokenBalances([ 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', 'So11111111111111111111111111111111111111112' ]) console.log('Token balances:', tokenBalances) ``` ##### `getTransactionReceipt(hash)` Gets the transaction receipt for a given transaction hash. **Parameters:** - `hash` (string): Transaction hash **Returns:** `Promise` - Transaction receipt details, or null if not found **Example:** ```javascript const receipt = await account.getTransactionReceipt('5....') console.log('Transaction receipt:', receipt) ``` ##### `toReadOnlyAccount()` Returns a read-only copy of the account. After the first call, subsequent calls reuse the same read-only account instance. **Returns:** `Promise` - The read-only account **Example:** ```javascript const readOnlyAccount = await account.toReadOnlyAccount() ``` ##### `dispose()` Disposes the wallet account, clearing private keys from memory. **Example:** ```javascript account.dispose() ``` #### Properties | Property | Type | Description | |----------|------|-------------| | `index` | `number` | The derivation path's index of this account | | `path` | `string` | The full derivation path of this account | | `keyPair` | `{publicKey: Uint8Array, privateKey: Uint8Array \| null}` | The account's Ed25519 key pair. `privateKey` is `null` after `dispose()` is called. | ⚠️ **Security Note**: The `keyPair` property contains sensitive cryptographic material. Never log, display, or expose the private key. ### WalletAccountReadOnlySolana Represents a read-only Solana wallet account. #### Constructor ```javascript new WalletAccountReadOnlySolana(publicKey, config) ``` **Parameters:** - `publicKey` (string): The account's public key (base58-encoded) - `config` (SolanaWalletConfig, optional): Configuration object #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's Solana address | `Promise` | | `getBalance()` | Returns the native SOL balance (in lamports) | `Promise` | | `getTokenBalance(tokenMint)` | Returns the balance of a specific SPL token | `Promise` | | `getTokenBalances(tokenAddresses)` | Returns balances for multiple SPL tokens | `Promise>` | | `verify(message, signature)` | Verifies a message signature | `Promise` | | `quoteSendTransaction(tx)` | Estimates the fee for a transaction | `Promise<{fee: bigint}>` | | `quoteTransfer(options)` | Estimates the fee for an SPL token transfer | `Promise<{fee: bigint}>` | ##### `getAddress()` Returns the account's Solana address. **Returns:** `Promise` - The account's base58-encoded Solana address **Example:** ```javascript const address = await readOnlyAccount.getAddress() console.log('Account address:', address) ``` ##### `getBalance()` Returns the native SOL balance (in lamports). **Returns:** `Promise` - Balance in lamports **Example:** ```javascript const balance = await readOnlyAccount.getBalance() console.log('SOL balance:', balance, 'lamports') ``` ##### `getTokenBalance(tokenMint)` Returns the balance of a specific SPL token. **Parameters:** - `tokenMint` (string): Token mint address (base58-encoded) **Returns:** `Promise` - Token balance in base units **Example:** ```javascript const tokenBalance = await readOnlyAccount.getTokenBalance('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB') console.log('USDT balance:', tokenBalance) ``` ##### `getTokenBalances(tokenAddresses)` Returns balances for multiple SPL tokens from the read-only account address. **Parameters:** - `tokenAddresses` (string[]): Token mint addresses (base58-encoded) **Returns:** `Promise>` - Mapping of token mint address to token balance in base units **Example:** ```javascript const tokenBalances = await readOnlyAccount.getTokenBalances([ 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', 'So11111111111111111111111111111111111111112' ]) console.log('Read-only token balances:', tokenBalances) ``` ##### `verify(message, signature)` Verifies a message signature. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify (hex-encoded) **Returns:** `Promise` - True if the signature is valid **Example:** ```javascript const isValid = await readOnlyAccount.verify('Hello, Solana!', signature) console.log('Signature valid:', isValid) ``` ##### `quoteSendTransaction(tx)` Estimates the fee for a transaction. **Parameters:** - `tx` (SolanaTransaction): The transaction object - `to` (string): Recipient's Solana address (base58-encoded) - `value` (number): Amount in lamports **Returns:** `Promise<{fee: bigint}>` - Object containing fee estimate (in lamports) **Example:** ```javascript const quote = await readOnlyAccount.quoteSendTransaction({ to: '11111111111111111111111111111112', value: 1000000000 }) console.log('Estimated fee:', quote.fee, 'lamports') ``` ##### `quoteTransfer(options)` Estimates the fee for an SPL token transfer. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): Token mint address (base58-encoded) - `recipient` (string): Recipient's Solana address (base58-encoded) - `amount` (number): Amount in token's base units **Returns:** `Promise<{fee: bigint}>` - Object containing fee estimate (in lamports) **Example:** ```javascript const quote = await readOnlyAccount.quoteTransfer({ token: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', recipient: '11111111111111111111111111111112', amount: 1000000 }) console.log('Transfer fee estimate:', quote.fee, 'lamports') ``` ## Types ### SolanaWalletConfig ```typescript interface SolanaWalletConfig { provider?: string | string[]; /** Deprecated alias for provider. provider takes precedence when both are set. */ rpcUrl?: string | string[]; commitment?: 'processed' | 'confirmed' | 'finalized'; retries?: number; transferMaxFee?: number | bigint; } ``` ### TransferOptions ```typescript interface TransferOptions { token: string; recipient: string; amount: number | bigint; } ``` ### KeyPair ```typescript interface KeyPair { publicKey: Uint8Array privateKey: Uint8Array } ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Solana Wallet Usage Get started with WDK's Solana Wallet Configuration *** ### Need Help? *** ## Wallet Solana Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-solana ## Wallet Configuration The `WalletManagerSolana` accepts an optional configuration object that defines how the wallet interacts with the Solana blockchain: ```javascript import WalletManagerSolana from '@tetherto/wdk-wallet-solana' const config = { provider: 'https://api.mainnet-beta.solana.com', // Recommended: Solana RPC endpoint transferMaxFee: 10000000 // Optional: Maximum fee in lamports } const wallet = new WalletManagerSolana(seedPhrase, config) ``` ## Account Configuration Accounts are obtained through the `WalletManagerSolana` instance using `getAccount()` or `getAccountByPath()`: ```javascript import WalletManagerSolana from '@tetherto/wdk-wallet-solana' const accountConfig = { provider: 'https://api.mainnet-beta.solana.com', transferMaxFee: 10000000 // Optional: Maximum fee in lamports } const wallet = new WalletManagerSolana(seedPhrase, accountConfig) // Get account by index const account = await wallet.getAccount(0) // Or get account by custom derivation path const customAccount = await wallet.getAccountByPath("0'/0'/5'") ``` ## Configuration Options ### provider The `provider` option specifies one Solana RPC endpoint or an ordered list of endpoints for blockchain interactions. **Type:** `string | string[]` (optional) **Default:** If not provided, wallet functionality that requires RPC will throw an error **Examples:** ```javascript // Single endpoint const config = { provider: 'https://api.mainnet-beta.solana.com' } // Devnet const config = { provider: 'https://api.devnet.solana.com' } // Failover across multiple endpoints const config = { provider: [ 'https://api.mainnet-beta.solana.com', 'https://rpc.ankr.com/solana' ] } // Custom RPC const config = { provider: 'https://your-custom-rpc-endpoint.com' } ``` When `provider` is an array, WDK initializes a failover provider and tries the next RPC when the current provider fails. ### rpcUrl The `rpcUrl` option is a deprecated alias for `provider`. Existing apps can keep using `rpcUrl`, but new code should use `provider`. If both keys are set, `provider` takes precedence. **Type:** `string | string[]` (optional) ### retries The `retries` option controls additional retry attempts after the initial failover request fails. It applies when `provider` is an ordered array of RPC endpoints. Total attempts are `1 + retries`; if retries exceeds the provider count, WDK loops back through the provider list in round-robin order. **Type:** `number` (optional) **Default:** `3` **Example:** ```javascript const config = { provider: [ 'https://api.mainnet-beta.solana.com', 'https://rpc.ankr.com/solana' ], retries: 5 } ``` ### transferMaxFee The `transferMaxFee` option sets the maximum allowed fee (in lamports) for transfer operations. This helps prevent unexpectedly high transaction fees. **Type:** `number` (optional) **Unit:** Lamports (1 SOL = 1,000,000,000 lamports) **Example:** ```javascript const config = { transferMaxFee: 10000000 // 0.01 SOL in lamports } ``` ## Complete Configuration Example ```javascript import WalletManagerSolana from '@tetherto/wdk-wallet-solana' const config = { // Recommended for operations that query or submit transactions provider: 'https://api.mainnet-beta.solana.com', // Optional: Fee protection transferMaxFee: 10000000 // 0.01 SOL maximum fee } const wallet = new WalletManagerSolana(seedPhrase, config) ``` ## Network Endpoints ### Mainnet - RPC: `https://api.mainnet-beta.solana.com` - WebSocket: `wss://api.mainnet-beta.solana.com/` ### Devnet - RPC: `https://api.devnet.solana.com` - WebSocket: `wss://api.devnet.solana.com/` ### Testnet - RPC: `https://api.testnet.solana.com` - WebSocket: `wss://api.testnet.solana.com/` ## Derivation Paths Solana wallets use SLIP-0010 derivation paths. The default derivation path follows ecosystem conventions: - Default path: `m/44'/501'/{index}'/0'` (where `{index}` is the account index) - Custom child paths must keep every segment hardened, for example `0'/0'/5'` **Default Derivation Path Change in v1.0.0-beta.4+** The default derivation path was updated in v1.0.0-beta.4 to match ecosystem conventions: - **Before** (up to v1.0.0-beta.3): `m/44'/501'/0'/0/{index}` - **After** (v1.0.0-beta.4+): `m/44'/501'/{index}'/0'` If you're upgrading from an earlier version, existing wallets created with the old path will generate different addresses. Make sure to migrate any existing wallets or use the old path explicitly if needed for compatibility. Use [`getAccountByPath`](/sdk/wallet-modules/wallet-solana/api-reference#getaccountbypathpath) to supply an explicit derivation path when importing or recreating legacy wallets. ## Security Considerations - Always use HTTPS URLs for RPC endpoints - Use RPC endpoints that serve the same Solana network when enabling failover - Set appropriate `transferMaxFee` limits for your use case - Consider using environment variables for configuration in production - Use trusted RPC providers or run your own Solana validator for production applications Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Solana Wallet Usage Get started with WDK's Solana Wallet API *** ### Need Help? *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/guides/account-management Description: Work with multiple Solana accounts and custom derivation paths. This guide explains how to retrieve multiple accounts from your Solana wallet and use custom derivation paths. ## Retrieve Accounts by Index Use [`getAccount()`](/sdk/wallet-modules/wallet-solana/api-reference#getaccountindex) with a zero-based index to access accounts derived from the default derivation path. ```javascript title="Get Accounts by Index" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Account 0 address:', address) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` ## Retrieve Account by Custom Derivation Path Use [`getAccountByPath()`](/sdk/wallet-modules/wallet-solana/api-reference#getaccountbypathpath) when you need a specific hierarchy beyond the default sequential index. ```javascript title="Custom Derivation Path" const customAccount = await wallet.getAccountByPath("0'/0'/5'") const customAddress = await customAccount.getAddress() console.log('Custom account address:', customAddress) ``` Solana uses SLIP-0010 derivation paths. Every child segment in a custom path must be hardened, for example `0'/0'/5'`. All accounts inherit the provider configuration from the wallet manager. ## Iterate Over Multiple Accounts You can loop through accounts to inspect addresses and balances in bulk. ```javascript title="Multi-Account Iteration" async function listAccounts(wallet) { const accounts = [] for (let i = 0; i < 5; i++) { const account = await wallet.getAccount(i) const address = await account.getAddress() const balance = await account.getBalance() accounts.push({ index: i, address, balance }) } return accounts } ``` ## Next Steps Now that you can access your accounts, learn how to [check balances](/sdk/wallet-modules/wallet-solana/guides/check-balances). *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/guides/check-balances Description: Query SOL and SPL token balances on Solana. This guide explains how to check [owned account balances](#owned-account-balances), [batch SPL token balances](#batch-spl-token-balances), and [read-only account balances](#read-only-account-balances). ## Owned Account Balances Use an account retrieved from [`WalletManagerSolana`](/sdk/wallet-modules/wallet-solana/api-reference#walletmanagersolana) to query balances. ### Native SOL Balance You can retrieve the native SOL balance from an `Account` object using [`account.getBalance()`](/sdk/wallet-modules/wallet-solana/api-reference#getbalance): ```javascript title="Get SOL Balance" const balance = await account.getBalance() console.log('Native SOL balance:', balance, 'lamports') ``` ### SPL Token Balance You can retrieve an SPL token balance from an `Account` object using [`account.getTokenBalance(tokenMint)`](/sdk/wallet-modules/wallet-solana/api-reference#gettokenbalancetokenmint): ```javascript title="Get SPL Token Balance" const splTokenAddress = 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' // USDT mint address const splTokenBalance = await account.getTokenBalance(splTokenAddress) console.log('SPL token balance:', splTokenBalance) ``` Token balances are returned in the token's smallest units. Adjust for the token's decimals when displaying (e.g., USD₮ has 6 decimals). ### Batch SPL Token Balances You can retrieve multiple SPL token balances in one batch using [`account.getTokenBalances()`](/sdk/wallet-modules/wallet-solana/api-reference#gettokenbalancestokenaddresses): ```javascript title="Get Batch SPL Token Balances" const tokenBalances = await account.getTokenBalances([ 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', 'So11111111111111111111111111111111111111112' ]) console.log('USDT balance:', tokenBalances['Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB']) console.log('Wrapped SOL balance:', tokenBalances['So11111111111111111111111111111111111111112']) ``` The returned object maps each token mint address to a `bigint` balance in base units. Missing associated token accounts return `0n`. ## Read-Only Account Balances Use [`WalletAccountReadOnlySolana`](/sdk/wallet-modules/wallet-solana/api-reference#walletaccountreadonlysolana) to check balances for any public key without a seed phrase. ### Native Balance ```javascript title="Read-Only SOL Balance" import { WalletAccountReadOnlySolana } from '@tetherto/wdk-wallet-solana' const readOnlyAccount = new WalletAccountReadOnlySolana('publicKey', { provider: 'https://api.mainnet-beta.solana.com', commitment: 'confirmed' }) const balance = await readOnlyAccount.getBalance() console.log('Native balance:', balance, 'lamports') ``` ### Token Balance ```javascript title="Read-Only Token Balance" const tokenBalance = await readOnlyAccount.getTokenBalance('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB') console.log('Token balance:', tokenBalance) ``` You can also batch read SPL token balances from a read-only account using [`readOnlyAccount.getTokenBalances()`](/sdk/wallet-modules/wallet-solana/api-reference#gettokenbalancestokenaddresses): ```javascript title="Read-Only Batch Token Balances" const tokenBalances = await readOnlyAccount.getTokenBalances([ 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', 'So11111111111111111111111111111111111111112' ]) console.log('Read-only token balances:', tokenBalances) ``` You can also create a read-only account from an existing owned account using [`await account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-solana/api-reference#toreadonlyaccount). ## Next Steps With balance checks in place, learn how to [send SOL](/sdk/wallet-modules/wallet-solana/guides/send-transactions). *** ## Error Handling URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/guides/error-handling Description: Handle errors, manage fees, and dispose of sensitive data in Solana wallets. This guide covers best practices for handling transaction errors, managing fee limits, and cleaning up sensitive data from memory. ## Handle Transaction Errors Wrap transactions in `try/catch` blocks to handle common failure scenarios such as insufficient balance, invalid addresses, or exceeded fee limits. ```javascript title="Transaction Error Handling" try { const result = await account.transfer({ token: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', // USDT mint address recipient: '11111111111111111111111111111112', amount: 1000000n }) console.log('Transfer submitted:', result.hash) } catch (error) { console.error('Transfer failed:', error.message) if (error.message.toLowerCase().includes('insufficient')) { console.log('Please add more tokens to your wallet') } else if (error.message.toLowerCase().includes('fee')) { console.log('The transfer fee exceeds your configured maximum') } } ``` ## Handle SOL Transfer Errors Native SOL transfers can fail for reasons including insufficient balance or invalid recipient addresses. ```javascript title="SOL Transfer Error Handling" async function safeTransfer(account, wallet) { try { const solBalance = await account.getBalance() const transferAmount = 1000000000n // 1 SOL if (solBalance < transferAmount) { throw new Error('Insufficient SOL balance') } const quote = await account.quoteSendTransaction({ to: '11111111111111111111111111111112', value: transferAmount }) console.log('Estimated fee:', quote.fee, 'lamports') const result = await account.sendTransaction({ to: '11111111111111111111111111111112', value: transferAmount }) console.log('Transaction successful:', result.hash) return result } catch (error) { if (error.message.includes('Insufficient SOL')) { console.error('Please add more SOL to your wallet') } else if (error.message.includes('invalid address')) { console.error('The recipient address is invalid') } else if (error.message.includes('max fee')) { console.error('The transfer fee exceeds your configured maximum') } else { console.error('Transaction failed:', error.message) } throw error } finally { account.dispose() wallet.dispose() } } ``` ## Handle Prebuilt TransactionMessage Errors If you pass a prebuilt `TransactionMessage`, make sure it already has a recent blockhash or durable nonce lifetime, or let WDK inject the latest blockhash for you. If you set `feePayer`, it must match the wallet address. Durable nonce flows still need a valid nonce account and signer setup in the message you provide. WDK preserves that lifetime instead of replacing it. ## Manage Fee Limits Set `transferMaxFee` when creating the wallet to prevent transactions from exceeding a maximum cost. Retrieve current network rates with [`getFeeRates()`](/sdk/wallet-modules/wallet-solana/api-reference) to make informed decisions. ```javascript title="Fee Management" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'lamports') console.log('Fast fee rate:', feeRates.fast, 'lamports') ``` ## Dispose of Sensitive Data Call [`dispose()`](/sdk/wallet-modules/wallet-solana/api-reference) on accounts and wallet managers to clear private keys and sensitive data from memory when they are no longer needed. ```javascript title="Memory Cleanup" account.dispose() wallet.dispose() ``` Always call [`dispose()`](/sdk/wallet-modules/wallet-solana/api-reference) in a `finally` block or cleanup handler to ensure sensitive data is cleared even if an error occurs. *** ## Getting Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/guides/getting-started Description: Install and create your first Solana wallet. This guide explains how to install the [`@tetherto/wdk-wallet-solana`](https://www.npmjs.com/package/@tetherto/wdk-wallet-solana) package and create a new wallet instance. ## 1. Installation ### Prerequisites Before you begin, ensure you have the following installed: * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ### Install Package ```bash title="Install @tetherto/wdk-wallet-solana" npm install @tetherto/wdk-wallet-solana ``` ## 2. Create a Wallet Import the module and create a [`WalletManagerSolana`](/sdk/wallet-modules/wallet-solana/api-reference#walletmanagersolana) instance with a BIP-39 seed phrase and a Solana RPC endpoint. ```javascript title="Create Solana Wallet" import WalletManagerSolana, { WalletAccountSolana, WalletAccountReadOnlySolana } from '@tetherto/wdk-wallet-solana' const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const wallet = new WalletManagerSolana(seedPhrase, { provider: 'https://api.mainnet-beta.solana.com', commitment: 'confirmed' // Optional: commitment level }) ``` To enable RPC failover, pass [`provider`](/sdk/wallet-modules/wallet-solana/configuration#provider) as an ordered array of endpoints and set [`retries`](/sdk/wallet-modules/wallet-solana/configuration#retries) to control additional failover attempts. `rpcUrl` remains available as a deprecated alias for `provider`. **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. ## 3. Get Your First Account Retrieve an account from the wallet and inspect its address. ```javascript title="Get Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Wallet address:', address) const readOnlyAccount = await account.toReadOnlyAccount() ``` All Solana addresses are base58-encoded public keys. Accounts inherit the provider configuration from the wallet manager. ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-solana/guides/account-management). *** ## Send SOL URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/guides/send-transactions Description: Send native SOL and estimate transaction fees on Solana. This guide explains how to [send native SOL](#send-native-sol), [sign a transaction without broadcasting](#sign-a-transaction-without-broadcasting), [estimate transaction fees](#estimate-transaction-fees), [quote or send a TransactionMessage](#quote-or-send-a-transactionmessage), [use dynamic fee rates](#use-dynamic-fee-rates), and [run a complete SOL transfer flow](#complete-example). **BigInt Usage:** Always use `BigInt` (the `n` suffix) for monetary values to avoid precision loss with large numbers. On Solana, values are expressed in lamports (1 SOL = 10^9 lamports). Fees are calculated based on the recent blockhash and instruction count. ## Send Native SOL Use [`account.sendTransaction()`](/sdk/wallet-modules/wallet-solana/api-reference#sendtransactiontx) to transfer SOL to a recipient address. ```javascript title="Send SOL" const result = await account.sendTransaction({ to: 'publicKey', // Recipient's base58-encoded public key value: 1000000000n // 1 SOL in lamports }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee, 'lamports') ``` ## Sign a Transaction Without Broadcasting Use [`account.signTransaction()`](/sdk/wallet-modules/wallet-solana/api-reference#signtransactiontx) when you need a fully signed transaction but want another process to review, relay, or submit it. ```javascript title="Sign SOL Transaction" const signedTransaction = await account.signTransaction({ to: '11111111111111111111111111111112', value: 1000000000n }) console.log('Signed transaction:', signedTransaction) ``` ## Estimate Transaction Fees Use [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-solana/api-reference#quotesendtransactiontx) to get a fee estimate before sending. ```javascript title="Quote Transaction Fee" const quote = await account.quoteSendTransaction({ to: 'publicKey', value: 1000000000n }) console.log('Estimated fee:', quote.fee, 'lamports') ``` ## Quote or Send a TransactionMessage Use a prebuilt `TransactionMessage` when you need custom instructions or a durable nonce flow. If the transaction message already includes a recent blockhash or durable nonce lifetime, WDK preserves it. If it does not, WDK fetches the latest blockhash before quoting or sending. When you set `feePayer`, it must match the wallet address. ```javascript title="Quote and Send a TransactionMessage" const quote = await account.quoteSendTransaction(txMessage) console.log('Estimated fee:', quote.fee, 'lamports') const result = await account.sendTransaction(txMessage) console.log('Transaction hash:', result.hash) ``` ## Use Dynamic Fee Rates Retrieve current fee rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-solana/api-reference#getfeerates). Rates are calculated based on the recent blockhash and compute unit prices. ```javascript title="Dynamic Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'lamports') console.log('Fast fee rate:', feeRates.fast, 'lamports') ``` ## Complete Example ```javascript title="Full SOL Transfer Flow" async function sendSOLTransfer(account, wallet) { const solBalance = await account.getBalance() const transferAmount = 1000000000n // 1 SOL if (solBalance < transferAmount) { throw new Error('Insufficient SOL balance') } const quote = await account.quoteSendTransaction({ to: '11111111111111111111111111111112', value: transferAmount }) console.log('Estimated fee:', quote.fee, 'lamports') const result = await account.sendTransaction({ to: '11111111111111111111111111111112', value: transferAmount }) console.log('Transaction hash:', result.hash) console.log('Fee paid:', result.fee, 'lamports') return result } ``` ## Next Steps To transfer SPL tokens instead of native SOL, see [Transfer SPL Tokens](/sdk/wallet-modules/wallet-solana/guides/transfer-tokens). *** ## Sign and Verify Messages URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/guides/sign-verify-messages Description: Sign messages and verify signatures with Solana accounts using Ed25519. This guide explains how to sign arbitrary messages with an owned account and verify signatures using a read-only account. Solana uses Ed25519 cryptography for signing. ## Sign a Message Use [`account.sign()`](/sdk/wallet-modules/wallet-solana/api-reference#signmessage) to produce an Ed25519 signature for any string message. ```javascript title="Sign a Message" const message = 'Hello, Solana!' const signature = await account.sign(message) console.log('Signature:', signature) ``` ## Verify a Signature You can get a [read-only account](/sdk/wallet-modules/wallet-solana/api-reference#walletaccountreadonlysolana) from any `Account` object by calling [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-solana/api-reference#toreadonlyaccount). Use a read-only account to [`verify()`](/sdk/wallet-modules/wallet-solana/api-reference#verifymessage-signature) that a signature was produced by the corresponding private key. ```javascript title="Verify a Signature" const readOnlyAccount = await account.toReadOnlyAccount() const isValid = await readOnlyAccount.verify(message, signature) console.log('Signature valid:', isValid) ``` You can also create a [`WalletAccountReadOnlySolana`](/sdk/wallet-modules/wallet-solana/api-reference) from any public key to verify signatures without access to the private key. ## Next Steps For best practices on handling errors, managing fees, and cleaning up memory, see [Error Handling](/sdk/wallet-modules/wallet-solana/guides/error-handling). *** ## Transfer SPL Tokens URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/guides/transfer-tokens Description: Transfer SPL tokens and estimate transfer fees on Solana. This guide explains how to transfer SPL tokens (such as USD₮), estimate fees, and validate inputs before executing. ## Transfer Tokens Use [`account.transfer()`](/sdk/wallet-modules/wallet-solana/api-reference#transferoptions) to send SPL tokens to a recipient address. If the recipient does not have a token account, one is created automatically. ```javascript title="Transfer SPL Tokens" const transferResult = await account.transfer({ token: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', // USDT mint address recipient: 'publicKey', // Recipient's base58-encoded public key amount: 1000000n // Amount in token's base units (6 decimals for USDT) }) console.log('Transfer hash:', transferResult.hash) console.log('Transfer fee:', transferResult.fee, 'lamports') ``` ## Estimate Transfer Fees Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-solana/api-reference#quotetransferoptions) to get a fee estimate before executing the transfer. ```javascript title="Quote Token Transfer" const transferQuote = await account.quoteTransfer({ token: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', recipient: 'publicKey', amount: 1000000n }) console.log('Transfer fee estimate:', transferQuote.fee, 'lamports') ``` ## Transfer with Validation You can validate addresses and check balances before transferring to catch errors early. ### 1. Validate Addresses ```javascript title="Address Validation" if (typeof splTokenMint !== 'string' || splTokenMint.length < 32) { throw new Error('Invalid SPL token mint address') } if (typeof recipient !== 'string' || recipient.length < 32) { throw new Error('Invalid recipient address') } ``` ### 2. Check Balance Use [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-solana/api-reference#gettokenbalancetokenmint) to verify sufficient funds: ```javascript title="Balance Check" const balance = await account.getTokenBalance(splTokenMint) if (balance < amount) { throw new Error('Insufficient SPL token balance') } ``` ### 3. Quote and Execute Transfer Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-solana/api-reference#quotetransferoptions) to estimate fees, then [`account.transfer()`](/sdk/wallet-modules/wallet-solana/api-reference#transferoptions) to execute: ```javascript title="Quote and Execute" const quote = await account.quoteTransfer({ token: splTokenMint, recipient, amount }) console.log('Transfer fee estimate:', quote.fee, 'lamports') const result = await account.transfer({ token: splTokenMint, recipient, amount }) console.log('Transfer hash:', result.hash) console.log('Actual fee:', result.fee, 'lamports') ``` ## Next Steps Learn how to [sign and verify messages](/sdk/wallet-modules/wallet-solana/guides/sign-verify-messages) with your Solana account. *** ## Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-solana/usage Description: Guide to using the @tetherto/wdk-wallet-solana module. The `@tetherto/wdk-wallet-solana` module provides wallet management for the Solana blockchain. Install the package and create your first wallet. Work with multiple accounts and custom derivation paths. Query SOL and SPL token balances. Send native SOL and estimate transaction fees. Transfer SPL tokens and estimate fees. Sign messages and verify Ed25519 signatures. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Solana Wallet Configuration Get started with WDK's Solana Wallet API --- ## Need Help? *** ## Wallet Spark Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark Description: Overview of the @tetherto/wdk-wallet-spark module # @tetherto/wdk-wallet-spark Overview A simple and secure package to manage BIP-32 wallets for the Spark blockchain. This package provides a clean API for creating, managing, and interacting with Spark wallets using [BIP-39 seed phrases](/resources/concepts#bip-39-mnemonic-seed-phrases), [BIP-44 derivation paths](/resources/concepts#bip-44-multi-account-hierarchy), and the Spark SDK for Bitcoin [layer 2](/resources/concepts#layer-2-solutions) functionality including [Lightning Network](/resources/concepts#lightning-network) integration. ## Features - **Spark Blockchain Support**: Full integration with the Spark Bitcoin [layer 2](/resources/concepts#layer-2-solutions) network - **Lightning Network Integration**: Create and pay [Lightning Network](/resources/concepts#lightning-network) invoices directly - **Spark Invoices**: Create and pay Spark invoices for receiving sats and tokens directly on the Spark network - **SparkScan Balance Polling**: Use SparkScan-backed balance polling in [`getBalance()`](/sdk/wallet-modules/wallet-spark/api-reference) when `sparkscan` is configured - **Sync and Retry**: Optionally sync wallet state and retry failed [`sendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference) and [`payLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference) calls once - **Token Transfers**: Transfer tokens to other Spark addresses - **Bitcoin Layer 1 Bridge**: Deposit and withdraw Bitcoin between layer 1 and Spark - **Static Deposit Addresses**: Reusable deposit addresses for Bitcoin layer 1 deposits - **Deposit Refunds**: Refund static deposits back to Bitcoin addresses - **Withdrawal Fee Quotes**: Get fee quotes before withdrawing funds - **[BIP-44 Derivation Paths](/resources/concepts#bip-44-multi-account-hierarchy)**: Support for standard BIP-44 derivation paths with Spark-specific coin type (998) - **[BIP-39 Seed Phrase Support](/resources/concepts#bip-39-mnemonic-seed-phrases)**: Generate and validate BIP-39 mnemonic seed phrases - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase - **Single-Use Deposit Addresses**: Generate unique deposit addresses for Bitcoin layer 1 deposits - **Fee-Free Transactions**: Spark transactions are fee-free on the layer 2 network - **Transaction History**: Complete transaction history with incoming/outgoing transfers - **Message Signing**: Sign and verify messages using Spark identity keys - **Memory Safety**: Secure private key management with automatic memory cleanup - **TypeScript Support**: Full TypeScript definitions included - **Network Support**: Support for Spark [mainnet](/resources/concepts#mainnet) and [regtest](/resources/concepts#regtest) networks ## Supported Networks This package supports the following Spark networks: - **Spark Mainnet**: Production Spark network - **Spark Signet**: Spark testing network - **Spark Regtest**: Local Spark network for testing **Regtest Faucet** You can obtain test funds for your Regtest environment from the [Lightspark Regtest Faucet](https://app.lightspark.com/regtest-faucet). ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's Spark Wallet configuration Get started with WDK's Spark Wallet API Get started with WDK's with Spark Wallet usage *** ### Need Help? *** ## Wallet Spark API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-spark ### Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerSpark](#walletmanagerspark) | Main class for managing Spark wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountSpark](#walletaccountspark) | Individual Spark wallet account implementation. Implements `IWalletAccount`. | [Constructor](#constructor-1), [Methods](#methods-1), [Properties](#properties) | | [WalletAccountReadOnlySpark](#walletaccountreadonlyspark) | Read-only Spark wallet account. | [Constructor](#constructor-1), [Methods](#methods-2) | ## WalletManagerSpark The main class for managing Spark wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. #### Constructor ```javascript new WalletManagerSpark(seed, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (object, optional): Configuration object - `network` (string, optional): 'MAINNET', 'SIGNET', or 'REGTEST' (default: 'MAINNET') - `sparkscan` (`SparkScanConfig`, optional): SparkScan configuration for balance polling - `syncAndRetry` (boolean, optional): When true, failed sends and Lightning payments sync wallet state and retry once ### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAccount(index)` | Returns a wallet account at the specified index | `Promise` | | `getAccountByPath(path)` | Returns a wallet account at a specific BIP-44 derivation path | `Promise` | | `getFeeRates()` | Returns current fee rates for transactions (always zero for Spark) | `Promise<{normal: bigint, fast: bigint}>` | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` | ##### `getAccount(index)` Returns a wallet account at the specified index using BIP-44 derivation path. **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise` - The wallet account **Example:** ```javascript const account = await wallet.getAccount(0) const account1 = await wallet.getAccount(1) ``` **Note:** Uses derivation path pattern `m/44'/998'/{networkNumber}'/0/{index}` where 998 is the coin type for Spark and networkNumber is 0 for MAINNET, 2 for SIGNET, or 3 for REGTEST. ##### `getFeeRates()` Returns current fee rates for transactions. On Spark network, transactions have zero fees. **Returns:** `Promise<{normal: bigint, fast: bigint}>` - Object containing fee rates (always `{normal: 0n, fast: 0n}`) **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal) // Always 0n console.log('Fast fee rate:', feeRates.fast) // Always 0n ``` ##### `dispose()` Disposes all wallet accounts and clears sensitive data from memory. **Returns:** `void` **Example:** ```javascript wallet.dispose() ``` ##### `getAccountByPath(path)` Returns a wallet account at a specific BIP-44 derivation path. **Parameters:** - `path` (string): The derivation path segment (e.g. `"0'/0/0"`) **Returns:** `Promise` - The wallet account **Example:** ```javascript const account = await wallet.getAccountByPath("0'/0/0") const address = await account.getAddress() console.log('Account address:', address) ``` **Important Notes:** - All Spark transactions have zero fees - Network configuration is limited to predefined values ## WalletAccountSpark Represents an individual Spark wallet account. Implements `IWalletAccount` from `@tetherto/wdk-wallet`. **Note**: WalletAccountSpark instances are created internally by `WalletManagerSpark.getAccount()` and are not intended to be constructed directly. ### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's Spark address | `Promise` | | `sign(message)` | Signs a message using the account's identity key | `Promise` | | `signTransaction(tx)` | Always throws because Spark does not support standalone signed transaction payloads | `Promise` | | `getIdentityKey()` | Returns the account's identity public key | `Promise` | | `verify(message, signature)` | Verifies a message signature | `Promise` | | `sendTransaction(tx)` | Sends a Spark transaction | `Promise<{hash: string, fee: bigint}>` | | `quoteSendTransaction(tx)` | Estimates transaction fee (always 0) | `Promise<{fee: bigint}>` | | `transfer(options)` | Transfers tokens to another address | `Promise<{hash: string, fee: bigint}>` | | `quoteTransfer(options)` | Quotes the costs of a transfer operation | `Promise<{fee: bigint}>` | | `getBalance()` | Returns the native token balance in satoshis | `Promise` | | `getTokenBalance(tokenAddress)` | Returns the balance for a specific token | `Promise` | | `getTransactionReceipt(hash)` | Returns a Spark transfer by its ID | `Promise` | | `getTransfers(options?)` | Returns the account's transfer history | `Promise` | | `getSingleUseDepositAddress()` | Generates a single-use Bitcoin deposit address | `Promise` | | `getUnusedDepositAddresses(options?)` | Returns unused single-use deposit addresses | `Promise<{depositAddresses: DepositAddressQueryResult[], offset: number}>` | | `getStaticDepositAddress()` | Gets or creates a reusable static deposit address | `Promise` | | `getStaticDepositAddresses()` | Returns all existing static deposit addresses | `Promise` | | `getUtxosForDepositAddress(options)` | Returns confirmed UTXOs for a deposit address | `Promise<{utxos: {txid: string, vout: number}[], offset: number}>` | | `claimDeposit(txId)` | Claims a Bitcoin deposit to the wallet | `Promise` | | `claimStaticDeposit(txId)` | Claims a static Bitcoin deposit to the wallet | `Promise` | | `refundStaticDeposit(options)` | Refunds a static deposit back to a Bitcoin address | `Promise` | | `quoteWithdraw(options)` | Gets a fee quote for withdrawing funds | `Promise` | | `withdraw(options)` | Withdraws funds to a Bitcoin address | `Promise` | | `createLightningInvoice(options)` | Creates a Lightning invoice | `Promise` | | `getLightningReceiveRequest(invoiceId)` | Gets Lightning receive request by id | `Promise` | | `getLightningSendRequest(requestId)` | Gets Lightning send request by id | `Promise` | | `payLightningInvoice(options)` | Pays a Lightning invoice | `Promise` | | `quotePayLightningInvoice(options)` | Gets fee estimate for Lightning payments | `Promise` | | `createSparkSatsInvoice(options)` | Creates a Spark invoice for receiving sats | `Promise` | | `createSparkTokensInvoice(options)` | Creates a Spark invoice for receiving tokens | `Promise` | | `paySparkInvoice(invoices)` | Pays one or more Spark invoices | `Promise` | | `syncWalletBalance()` | Reconciles wallet state and waits for any triggered optimization to complete | `Promise` | | `getSparkInvoices(params)` | Queries the status of Spark invoices | `Promise<{invoiceStatuses: InvoiceResponse[], offset: number}>` | | `toReadOnlyAccount()` | Creates a read-only version of this account | `Promise` | | `cleanupConnections()` | Cleans up network connections and resources | `Promise` | | `dispose()` | Disposes the wallet account, clearing private keys | `void` | ##### `getAddress()` Returns the account's Spark network address. **Returns:** `Promise` - The Spark address **Example:** ```javascript const address = await account.getAddress() console.log('Spark address:', address) ``` ##### `sign(message)` Signs a message using the account's identity key. **Parameters:** - `message` (string): The message to sign **Returns:** `Promise` - The message signature **Example:** ```javascript const signature = await account.sign('Hello, Spark!') console.log('Signature:', signature) ``` ##### `signTransaction(tx)` Exposes the base `IWalletAccount.signTransaction(tx)` method, but standalone Spark transaction signing is not supported. Spark transfers are signed collaboratively with Spark operators through the FROST / Statechain protocol, so this method always throws. Use [`sendTransaction()`](#sendtransactionto-value) to send Spark transactions. **Parameters:** - `tx` ([`SparkTransaction`](#sparktransaction)): The transaction request **Returns:** `Promise` - Always throws **Example:** ```javascript try { await account.signTransaction({ to: 'spark1...', value: 1000000 }) } catch (error) { console.error(error.message) // Method 'signTransaction(tx)' not supported on spark. } ``` ##### `getIdentityKey()` Returns the account's identity public key (hex-encoded). **Returns:** `Promise` - The identity public key **Example:** ```javascript const identityKey = await account.getIdentityKey() console.log('Identity key:', identityKey) // 02eda8... ``` ##### `sendTransaction({to, value})` Sends a Spark transaction. When `syncAndRetry` is enabled, the wallet syncs state and retries once after a failure. **Parameters:** - `to` (string): Recipient's Spark address - `value` (number): Amount in satoshis **Returns:** `Promise<{hash: string, fee: bigint}>` (fee is always 0) **Example:** ```javascript const result = await account.sendTransaction({ to: 'spark1...', value: 1000000 }) console.log('Transaction hash:', result.hash) console.log('Fee:', Number(result.fee)) // Always 0 ``` ##### `quoteSendTransaction({to, value})` Estimates the fee for a Spark transaction (always returns 0). **Parameters:** - `to` (string): Recipient's Spark address - `value` (number): Amount in satoshis **Returns:** `Promise<{fee: bigint}>` - Fee estimate (always 0) **Example:** ```javascript const quote = await account.quoteSendTransaction({ to: 'spark1...', value: 1000000 }) console.log('Estimated fee:', Number(quote.fee)) // Always 0 ``` ##### `transfer(options)` Transfers tokens to another address. **Parameters:** - `options` (object): Transfer options - `token` (string): Token identifier (Bech32m token identifier, e.g., `btkn1...`) - `amount` (bigint): Amount of tokens to transfer - `recipient` (string): Recipient Spark address **Returns:** `Promise<{hash: string, fee: bigint}>` - Transfer result **Example:** ```javascript const result = await account.transfer({ token: 'btkn1...', amount: BigInt(1000000), recipient: 'spark1...' }) console.log('Transfer hash:', result.hash) ``` ##### `quoteTransfer(options)` Quotes the costs of a transfer operation. **Parameters:** - `options` (object): Transfer options (same as `transfer`) **Returns:** `Promise<{fee: bigint}>` - Transfer fee quote **Example:** ```javascript const quote = await account.quoteTransfer({ token: 'btkn1...', amount: BigInt(1000000), recipient: 'spark1...' }) console.log('Transfer fee:', Number(quote.fee)) ``` ##### `getBalance()` Returns the account's native token balance in satoshis. When `sparkscan` is configured, this uses SparkScan's `btcSoftBalanceSats` value. **Returns:** `Promise` - Balance in satoshis **Example:** ```javascript const balance = await account.getBalance() console.log('Balance:', balance, 'satoshis') console.log('Balance in BTC:', Number(balance) / 1e8) ``` ##### `getTokenBalance(tokenAddress)` Returns the balance for a specific token. **Parameters:** - `tokenAddress` (string): Token contract address **Returns:** `Promise` - Token balance in base unit **Example:** ```javascript const tokenBalance = await account.getTokenBalance('token_address...') console.log('Token balance:', tokenBalance) ``` ##### `getTransactionReceipt(hash)` Returns a Spark transfer by its ID. Only returns Spark transfers, not on-chain Bitcoin transactions. **Parameters:** - `hash` (string): The Spark transfer ID **Returns:** `Promise` - The Spark transfer, or null if not found **Example:** ```javascript const transfer = await account.getTransactionReceipt('transfer_id...') console.log('Transfer details:', transfer) ``` ##### `getTransfers(options?)` Returns the Spark transfer history of the account. Only returns Spark transfers, not on-chain Bitcoin transactions. **Parameters:** - `options` (GetTransfersOptions, optional): Filter options - `direction` (string): 'all', 'incoming', or 'outgoing' (default: 'all') - `limit` (number): Maximum transfers to return (default: 10) - `skip` (number): Number of transfers to skip (default: 0) **Returns:** `Promise` - Array of Spark transfers **Example:** ```javascript const transfers = await account.getTransfers({ direction: 'incoming', limit: 5 }) console.log('Recent incoming transfers:', transfers) ``` ##### `getSingleUseDepositAddress()` Generates a single-use Bitcoin deposit address for funding the Spark wallet. **Returns:** `Promise` - Bitcoin deposit address **Example:** ```javascript const depositAddress = await account.getSingleUseDepositAddress() console.log('Send Bitcoin to:', depositAddress) ``` ##### `getUnusedDepositAddresses(options?)` Returns unused single-use deposit addresses for the account. **Parameters:** - `options` (Omit\, optional): Query options **Returns:** `Promise<{depositAddresses: DepositAddressQueryResult[], offset: number}>` - The unused deposit addresses with pagination offset **Example:** ```javascript const result = await account.getUnusedDepositAddresses() console.log('Unused addresses:', result.depositAddresses) console.log('Offset:', result.offset) ``` ##### `getStaticDepositAddress()` Returns a static deposit address for Bitcoin deposits from layer 1, generating one if it does not already exist. This address can be reused. **Returns:** `Promise` - The static deposit address **Example:** ```javascript const depositAddress = await account.getStaticDepositAddress() console.log('Static deposit address:', depositAddress) ``` ##### `getStaticDepositAddresses()` Returns all existing static deposit addresses for the account. **Returns:** `Promise` - The static deposit addresses **Example:** ```javascript const addresses = await account.getStaticDepositAddresses() console.log('Static deposit addresses:', addresses) ``` ##### `getUtxosForDepositAddress(options)` Returns confirmed UTXOs for a specific deposit address. **Parameters:** - `options` (GetUtxosParams): Query options **Returns:** `Promise<{utxos: {txid: string, vout: number}[], offset: number}>` - The confirmed UTXOs with pagination offset **Example:** ```javascript const result = await account.getUtxosForDepositAddress({ depositAddress: 'bc1q...' }) console.log('UTXOs:', result.utxos) ``` ##### `claimDeposit(txId)` Claims a Bitcoin deposit to add funds to the Spark wallet. **Parameters:** - `txId` (string): Bitcoin transaction ID of the deposit **Returns:** `Promise` - Wallet leaves created from the deposit **Example:** ```javascript const leaves = await account.claimDeposit('bitcoin_tx_id...') console.log('Claimed deposit:', leaves) ``` ##### `claimStaticDeposit(txId)` Claims a static Bitcoin deposit to add funds to the Spark wallet. **Parameters:** - `txId` (string): Bitcoin transaction ID of the deposit **Returns:** `Promise` - Wallet leaves created from the deposit **Example:** ```javascript const leaves = await account.claimStaticDeposit('bitcoin_tx_id...') console.log('Claimed static deposit:', leaves) ``` ##### `refundStaticDeposit(options)` Refunds a deposit made to a static deposit address back to a specified Bitcoin address. The minimum fee is 300 satoshis. **Parameters:** - `options` (object): Refund options - `depositTransactionId` (string): The transaction ID of the original deposit - `outputIndex` (number): The output index of the deposit - `destinationAddress` (string): The Bitcoin address to send the refund to - `satsPerVbyteFee` (number): The fee rate in sats per vbyte for the refund transaction **Returns:** `Promise` - The refund transaction as a hex string that needs to be broadcast **Example:** ```javascript const refundTx = await account.refundStaticDeposit({ depositTransactionId: 'txid...', outputIndex: 0, destinationAddress: 'bc1q...', satsPerVbyteFee: 10 }) console.log('Refund transaction (hex):', refundTx) // Note: This transaction needs to be broadcast to the Bitcoin network ``` ##### `quoteWithdraw(options)` Gets a fee quote for withdrawing funds from Spark cooperatively to an on-chain Bitcoin address. **Parameters:** - `options` (object): Withdrawal quote options - `withdrawalAddress` (string): The Bitcoin address where the funds should be sent - `amountSats` (number): The amount in satoshis to withdraw **Returns:** `Promise` - The withdrawal fee quote **Example:** ```javascript const feeQuote = await account.quoteWithdraw({ withdrawalAddress: 'bc1q...', amountSats: 1000000 }) console.log('Withdrawal fee quote:', feeQuote) ``` ##### `withdraw(options)` Initiates a withdrawal to move funds from the Spark network to an on-chain Bitcoin address. **Parameters:** - `options` (WithdrawOptions): Withdrawal options object (`Omit`) - `onchainAddress` (string): Bitcoin address to withdraw to - `amountSats` (number): Amount in satoshis to withdraw **Returns:** `Promise` - The withdrawal request details, or null/undefined if the request cannot be completed **Example:** ```javascript const withdrawal = await account.withdraw({ onchainAddress: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', amountSats: 100000 }) console.log('Withdrawal request:', withdrawal) ``` ##### `createLightningInvoice(options)` Creates a Lightning invoice for receiving payments. **Parameters:** - `options` (CreateLightningInvoiceParams): Invoice options object - `amountSats` (number, optional): Amount in satoshis - `memo` (string, optional): Invoice description - Additional options from `CreateLightningInvoiceParams` may be supported **Returns:** `Promise` - Lightning invoice details **Example:** ```javascript const invoice = await account.createLightningInvoice({ amountSats: 100000, memo: 'Payment for services' }) console.log('Invoice:', invoice.invoice) ``` ##### `getLightningReceiveRequest(invoiceId)` Gets details of a previously created Lightning receive request. **Parameters:** - `invoiceId` (string): Invoice ID **Returns:** `Promise` - Invoice details, or null if not found **Example:** ```javascript const request = await account.getLightningReceiveRequest(invoiceId) if (request) { console.log('Invoice status:', request.status) } ``` ##### `getLightningSendRequest(requestId)` Gets a Lightning send request by id. **Parameters:** - `requestId` (string): The id of the Lightning send request **Returns:** `Promise` - The Lightning send request **Example:** ```javascript const request = await account.getLightningSendRequest(requestId) if (request) { console.log('Lightning send request:', request) } ``` ##### `payLightningInvoice(options)` Pays a Lightning invoice. When `syncAndRetry` is enabled, the wallet syncs state and retries once after a failure. **Parameters:** - `options` (PayLightningInvoiceParams): Payment options object - `encodedInvoice` (string): BOLT11 Lightning invoice - `maxFeeSats` (number, optional): Maximum fee willing to pay in satoshis - Additional options from `PayLightningInvoiceParams` may be supported **Returns:** `Promise` - Payment details **Example:** ```javascript const payment = await account.payLightningInvoice({ encodedInvoice: 'lnbc...', maxFeeSats: 1000 }) console.log('Payment result:', payment) ``` ##### `quotePayLightningInvoice(options)` Estimates the fee for paying a Lightning invoice. **Parameters:** - `options` (LightningSendFeeEstimateInput): Fee estimation options - `encodedInvoice` (string): BOLT11 Lightning invoice - Additional options may be supported **Returns:** `Promise` - Estimated fee in satoshis **Example:** ```javascript const feeEstimate = await account.quotePayLightningInvoice({ encodedInvoice: 'lnbc...' }) console.log('Estimated Lightning fee:', Number(feeEstimate), 'satoshis') ``` ##### `verify(message, signature)` Verifies a message signature against the account's identity key. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise` - True if the signature is valid **Example:** ```javascript const isValid = await account.verify('Hello, Spark!', signature) console.log('Signature valid:', isValid) ``` ##### `createSparkSatsInvoice(options)` Creates a Spark invoice for receiving a sats payment. **Parameters:** - `options` (object): Invoice options - `amount` (number, optional): The amount of sats to receive (optional for open invoices) - `memo` (string, optional): Optional memo/description for the payment - `senderSparkAddress` (SparkAddressFormat, optional): Optional Spark address of the expected sender - `expiryTime` (Date, optional): Optional expiry time for the invoice **Returns:** `Promise` - A Spark invoice that can be paid by another Spark wallet **Example:** ```javascript const invoice = await account.createSparkSatsInvoice({ amount: 100000, memo: 'Payment for services' }) console.log('Spark invoice:', invoice) ``` ##### `createSparkTokensInvoice(options)` Creates a Spark invoice for receiving a token payment. **Parameters:** - `options` (object): Invoice options - `tokenIdentifier` (string, optional): The Bech32m token identifier (e.g., `btkn1...`) - `amount` (bigint, optional): The amount of tokens to receive - `memo` (string, optional): Optional memo/description for the payment - `senderSparkAddress` (SparkAddressFormat, optional): Optional Spark address of the expected sender - `expiryTime` (Date, optional): Optional expiry time for the invoice **Returns:** `Promise` - A Spark invoice that can be paid by another Spark wallet **Example:** ```javascript const invoice = await account.createSparkTokensInvoice({ tokenIdentifier: 'btkn1...', amount: BigInt(1000), memo: 'Token payment' }) console.log('Spark token invoice:', invoice) ``` ##### `paySparkInvoice(invoices)` Fulfills one or more Spark invoices by paying them. **Parameters:** - `invoices` (SparkInvoice[]): Array of invoices to fulfill - Each invoice has: - `invoice` (SparkAddressFormat): The Spark invoice to pay - `amount` (bigint, optional): Amount to pay (required for invoices without encoded amount) **Returns:** `Promise` - Response containing transaction results and errors **Example:** ```javascript const result = await account.paySparkInvoice([ { invoice: 'spark1...', amount: BigInt(100000) } ]) console.log('Payment result:', result) ``` ##### `syncWalletBalance()` Reconciles the wallet's internal state with the server and waits for any triggered optimization to complete. **Returns:** `Promise` **Example:** ```javascript await account.syncWalletBalance() ``` ##### `getSparkInvoices(params)` Queries the status of Spark invoices. **Parameters:** - `params` (QuerySparkInvoicesParams): The query parameters **Returns:** `Promise<{invoiceStatuses: InvoiceResponse[], offset: number}>` - The invoice statuses with pagination offset **Example:** ```javascript const result = await account.getSparkInvoices({ sparkAddress: await account.getAddress() }) console.log('Invoice statuses:', result.invoiceStatuses) ``` ##### `toReadOnlyAccount()` Returns a read-only version of this account that can query data but not sign transactions. After the first call, subsequent calls reuse the same read-only account instance. **Returns:** `Promise` - Read-only account instance **Example:** ```javascript const readOnlyAccount = await account.toReadOnlyAccount() const balance = await readOnlyAccount.getBalance() ``` ##### `cleanupConnections()` Cleans up network connections and resources. **Returns:** `Promise` **Example:** ```javascript await account.cleanupConnections() ``` ##### `dispose()` Disposes the wallet account, securely erasing private keys from memory. **Returns:** `void` **Example:** ```javascript account.dispose() // Private keys are now cleared from memory ``` #### Properties | Property | Type | Description | |----------|------|-------------| | `index` | `number` | The derivation path index of this account | | `path` | `string` | The full BIP-44 derivation path | | `keyPair` | `KeyPair` | The account's key pair (⚠️ Contains sensitive data). The returned arrays are bound to the account — treat them as a read-only view and do not modify their contents. `privateKey` is `null` after `dispose()` is called. | ## WalletAccountReadOnlySpark Represents a read-only wallet account. Implements `WalletAccountReadOnly` from `@tetherto/wdk-wallet`. ### Constructor ```javascript new WalletAccountReadOnlySpark(address, config) ``` **Parameters:** - `address` (string): The account's Spark address - `config` (SparkWalletConfig, optional): Configuration object ### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's Spark address | `Promise` | | `getIdentityKey()` | Returns the account's identity public key | `Promise` | | `getBalance()` | Returns the native token balance in satoshis | `Promise` | | `getTokenBalance(tokenAddress)` | Returns the balance for a specific token | `Promise` | | `getTransactionReceipt(hash)` | Returns a Spark transfer by its ID | `Promise` | | `getTransfers(options?)` | Returns the account's Spark transfer history | `Promise` | | `getUnusedDepositAddresses(options?)` | Returns unused single-use deposit addresses | `Promise<{depositAddresses: DepositAddressQueryResult[], offset: number}>` | | `getStaticDepositAddresses()` | Returns all existing static deposit addresses | `Promise` | | `getUtxosForDepositAddress(options)` | Returns confirmed UTXOs for a deposit address | `Promise<{utxos: {txid: string, vout: number}[], offset: number}>` | | `getSparkInvoices(params)` | Queries the status of Spark invoices | `Promise<{invoiceStatuses: InvoiceResponse[], offset: number}>` | | `quoteSendTransaction(tx)` | Estimates transaction fee (always 0) | `Promise<{fee: bigint}>` | | `quoteTransfer(options)` | Quotes the costs of a transfer operation | `Promise<{fee: bigint}>` | ##### `getAddress()` Returns the account's Spark network address. **Returns:** `Promise` - The Spark address **Example:** ```javascript const address = await readOnlyAccount.getAddress() console.log('Spark address:', address) ``` ##### `getIdentityKey()` Returns the account's identity public key (hex-encoded). **Returns:** `Promise` - The identity public key **Example:** ```javascript const identityKey = await readOnlyAccount.getIdentityKey() console.log('Identity key:', identityKey) // 02eda8... ``` ##### `getBalance()` Returns the account's native token balance in satoshis. When `sparkscan` is configured, this uses SparkScan's `btcSoftBalanceSats` value. **Returns:** `Promise` - Balance in satoshis **Example:** ```javascript const balance = await readOnlyAccount.getBalance() console.log('Balance:', balance, 'satoshis') ``` ##### `getTokenBalance(tokenAddress)` Returns the balance for a specific token. **Parameters:** - `tokenAddress` (string): Token contract address **Returns:** `Promise` - Token balance in base unit **Example:** ```javascript const tokenBalance = await readOnlyAccount.getTokenBalance('token_address...') console.log('Token balance:', tokenBalance) ``` ##### `getTransactionReceipt(hash)` Returns a Spark transfer by its ID. Only returns Spark transfers, not on-chain Bitcoin transactions. **Parameters:** - `hash` (string): The Spark transfer ID **Returns:** `Promise` - The Spark transfer, or null if not found **Example:** ```javascript const transfer = await readOnlyAccount.getTransactionReceipt('transfer_id...') console.log('Transfer details:', transfer) ``` ##### `getTransfers(options?)` Returns the Spark transfer history of the account. Only returns Spark transfers, not on-chain Bitcoin transactions. **Parameters:** - `options` (GetTransfersOptions, optional): Filter options - `direction` (string): 'all', 'incoming', or 'outgoing' (default: 'all') - `limit` (number): Maximum transfers to return (default: 10) - `skip` (number): Number of transfers to skip (default: 0) **Returns:** `Promise` - Array of Spark transfers **Example:** ```javascript const transfers = await readOnlyAccount.getTransfers({ direction: 'incoming', limit: 5 }) console.log('Recent incoming transfers:', transfers) ``` ##### `getUnusedDepositAddresses(options?)` Returns unused single-use deposit addresses for the account. **Parameters:** - `options` (Omit\, optional): Query options **Returns:** `Promise<{depositAddresses: DepositAddressQueryResult[], offset: number}>` - The unused deposit addresses with pagination offset **Example:** ```javascript const result = await readOnlyAccount.getUnusedDepositAddresses() console.log('Unused addresses:', result.depositAddresses) ``` ##### `getStaticDepositAddresses()` Returns all existing static deposit addresses for the account. **Returns:** `Promise` - The static deposit addresses **Example:** ```javascript const addresses = await readOnlyAccount.getStaticDepositAddresses() console.log('Static deposit addresses:', addresses) ``` ##### `getUtxosForDepositAddress(options)` Returns confirmed UTXOs for a specific deposit address. **Parameters:** - `options` (GetUtxosParams): Query options **Returns:** `Promise<{utxos: {txid: string, vout: number}[], offset: number}>` - The confirmed UTXOs with pagination offset **Example:** ```javascript const result = await readOnlyAccount.getUtxosForDepositAddress({ depositAddress: 'bc1q...' }) console.log('UTXOs:', result.utxos) ``` ##### `getSparkInvoices(params)` Queries the status of Spark invoices. **Parameters:** - `params` (QuerySparkInvoicesParams): The query parameters **Returns:** `Promise<{invoiceStatuses: InvoiceResponse[], offset: number}>` - The invoice statuses with pagination offset **Example:** ```javascript const result = await readOnlyAccount.getSparkInvoices({ sparkAddress: await readOnlyAccount.getAddress() }) console.log('Invoice statuses:', result.invoiceStatuses) ``` ##### `quoteSendTransaction({to, value})` Estimates the fee for a Spark transaction (always returns 0). **Parameters:** - `to` (string): Recipient's Spark address - `value` (number): Amount in satoshis **Returns:** `Promise<{fee: bigint}>` - Fee estimate (always 0) **Example:** ```javascript const quote = await readOnlyAccount.quoteSendTransaction({ to: 'spark1...', value: 1000000 }) console.log('Estimated fee:', Number(quote.fee)) ``` ##### `quoteTransfer(options)` Quotes the costs of a transfer operation. **Parameters:** - `options` (object): Transfer options - `token` (string): Token identifier - `amount` (bigint): Amount of tokens - `recipient` (string): Recipient Spark address **Returns:** `Promise<{fee: bigint}>` - Transfer fee quote **Example:** ```javascript const quote = await readOnlyAccount.quoteTransfer({ token: 'btkn1...', amount: BigInt(1000000), recipient: 'spark1...' }) console.log('Transfer fee:', Number(quote.fee)) ``` ## Types ### SparkScanConfig ```typescript interface SparkScanConfig { baseUrl?: string // Optional SparkScan URL (default: "https://api.sparkscan.io") network?: 'MAINNET' | 'SIGNET' | 'REGTEST' // Spark network, SparkScan only accepts MAINNET and REGTEST at runtime apiKey?: string // Optional API key for SparkScan requests } ``` ### SparkWalletConfig ```typescript interface SparkWalletConfig { network?: 'MAINNET' | 'SIGNET' | 'REGTEST' // The network (default: "MAINNET") sparkscan?: SparkScanConfig // Optional SparkScan configuration for balance polling syncAndRetry?: boolean // When true, failed sends and Lightning payments sync wallet state and retry once } ``` ### SparkTransaction ```typescript interface SparkTransaction { to: string // The transaction's recipient (Spark address) value: number | bigint // The amount of bitcoins to send to the recipient (in satoshis) } ``` ### TransactionResult ```typescript interface TransactionResult { hash: string // Transaction hash/ID fee: bigint // Transaction fee in satoshis (always 0n for Spark) } ``` ### KeyPair ```typescript interface KeyPair { publicKey: Uint8Array // Public key bytes privateKey: Uint8Array | null // Private key bytes; null after dispose() is called } ``` The arrays returned by `keyPair` are bound to the wallet account. Treat them as a read-only view — do not modify their contents. `privateKey` is `null` after [`dispose()`](#dispose-1) is called. ### LightningReceiveRequest ```typescript interface LightningReceiveRequest { invoice: string // BOLT11 encoded Lightning invoice id: string // Invoice ID for tracking amountSats: number // Amount in satoshis memo?: string // Optional description } ``` ### LightningSendRequest ```typescript interface LightningSendRequest { id: string // Payment request ID invoice: string // BOLT11 encoded invoice that was paid maxFeeSats: number // Maximum fee that was allowed status: string // Payment status } ``` ### WalletLeaf ```typescript interface WalletLeaf { // Spark SDK internal structure for wallet state // Exact properties depend on Spark SDK implementation } ``` ### CoopExitRequest ```typescript interface CoopExitRequest { id: string // Withdrawal request ID onchainAddress: string // Bitcoin address for withdrawal amountSats: number // Amount in satoshis exitSpeed: string // Withdrawal speed ('FAST', 'MEDIUM', 'SLOW') - default: 'MEDIUM' status: string // Withdrawal status } ``` ### TransferOptions ```typescript interface TransferOptions { token: string // Token identifier (Bech32m, e.g. btkn1...) amount: bigint // Amount of tokens to transfer recipient: string // Recipient Spark address } ``` ### GetTransfersOptions ```typescript interface GetTransfersOptions { direction?: 'incoming' | 'outgoing' | 'all' // Filter by direction (default: 'all') limit?: number // Number of transfers to return (default: 10) skip?: number // Number of transfers to skip (default: 0) } ``` ### SparkTransfer Type alias for `Transfer` from `@buildonspark/spark-sdk/proto/spark`. Key properties include: ```typescript interface SparkTransfer { id: string // Transfer ID status: string // Transfer status totalValue: number // Total value in satoshis transferDirection: string // 'INCOMING' or 'OUTGOING' type: string // Transfer type createdTime?: Date // When the transfer was created updatedTime?: Date // Last update timestamp } ``` ### DepositAddressQueryResult From `@buildonspark/spark-sdk/proto/spark`: ```typescript interface DepositAddressQueryResult { address: string // The deposit address confirmationStatus: string // Confirmation status } ``` ### InvoiceResponse From `@buildonspark/spark-sdk/proto/spark`: ```typescript interface InvoiceResponse { invoiceId: string // The invoice identifier status: string // The invoice status } ``` ### QueryDepositAddressesParams ```typescript interface QueryDepositAddressesParams { sparkAddress: string // The Spark address to query deposit addresses for offset?: number // Pagination offset limit?: number // Maximum results to return } ``` ### GetUtxosParams ```typescript interface GetUtxosParams { depositAddress: string // The deposit address to query UTXOs for offset?: number // Pagination offset limit?: number // Maximum results to return } ``` ### QuerySparkInvoicesParams ```typescript interface QuerySparkInvoicesParams { sparkAddress: string // The Spark address to query invoices for offset?: number // Pagination offset limit?: number // Maximum results to return } ``` ### Lightning Invoice Options ```typescript // Use CreateLightningInvoiceParams from @buildonspark/spark-sdk // Basic options include: interface CreateLightningInvoiceParams { amountSats?: number // Amount in satoshis memo?: string // Optional description for the invoice // Additional options may be available } ``` ### Lightning Payment Options ```typescript // Use PayLightningInvoiceParams from @buildonspark/spark-sdk // Basic options include: interface PayLightningInvoiceParams { encodedInvoice: string // BOLT11-encoded Lightning invoice to pay maxFeeSats?: number // Maximum fee in satoshis to pay // Additional options may be available } ``` ### Lightning Fee Estimate Options ```typescript // Use LightningSendFeeEstimateInput from @buildonspark/spark-sdk/types // Basic options include: interface LightningSendFeeEstimateInput { encodedInvoice: string // BOLT11-encoded Lightning invoice to estimate fees for // Additional options may be available } ``` ### Withdrawal Options ```typescript // WithdrawOptions = Omit interface WithdrawOptions { onchainAddress: string // Bitcoin address where the funds should be sent amountSats: number // Amount in satoshis to withdraw } interface QuoteWithdrawOptions { withdrawalAddress: string // Bitcoin address where the funds should be sent amountSats: number // Amount in satoshis to withdraw } ``` ### Spark Invoice Options ```typescript interface CreateSatsInvoiceOptions { amount?: number // Amount of sats to receive (optional for open invoices) memo?: string // Optional memo/description senderSparkAddress?: string // Optional Spark address of expected sender expiryTime?: Date // Optional expiry time } interface CreateTokensInvoiceOptions { tokenIdentifier?: string // Bech32m token identifier (e.g., btkn1...) amount?: bigint // Amount of tokens to receive memo?: string // Optional memo/description senderSparkAddress?: string // Optional Spark address of expected sender expiryTime?: Date // Optional expiry time } interface SparkInvoice { invoice: string // The Spark invoice to pay amount?: bigint // Amount to pay (required for invoices without encoded amount) } ``` ### Refund Options ```typescript interface RefundStaticDepositOptions { depositTransactionId: string // Transaction ID of the original deposit outputIndex: number // Output index of the deposit destinationAddress: string // Bitcoin address to send refund to satsPerVbyteFee: number // Fee rate in sats per vbyte } ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Spark Wallet Usage Get started with WDK's Spark Wallet Configuration *** ### Need Help? *** ## Wallet Spark Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-spark # Configuration ## Wallet Configuration ```javascript const config = { network: 'MAINNET', // 'MAINNET', 'SIGNET', or 'REGTEST' sparkscan: { apiKey: 'your-api-key-here' }, syncAndRetry: true } const wallet = new WalletManagerSpark(seedPhrase, config) ``` ## Account Creation ```javascript // WalletAccountSpark is created by the WalletManagerSpark // It does not take configuration parameters directly const account = await wallet.getAccount(0) // Get account at index 0 ``` ## Configuration Options ### Network The `network` option specifies which Spark network to use. **Type:** `string` **Values:** - `"MAINNET"` - Spark mainnet (production) - `"SIGNET"` - Spark signet (testing) - `"REGTEST"` - Spark regtest (local development) - [Get test funds](https://app.lightspark.com/regtest-faucet) **Default:** `"MAINNET"` **Example:** ```javascript const config = { network: 'REGTEST' // Use REGTEST for development } ``` ### SparkScan Balance Polling The `sparkscan` option configures SparkScan-backed balance polling for [`getBalance()`](/sdk/wallet-modules/wallet-spark/api-reference). **Type:** `SparkScanConfig` (optional) **Fields:** - `baseUrl` - Optional SparkScan URL, default: `https://api.sparkscan.io` - `network` - Optional Spark network. SparkScan supports `MAINNET` and `REGTEST` - `apiKey` - Optional SparkScan API key **Example:** ```javascript const config = { network: 'MAINNET', sparkscan: { apiKey: 'your-api-key-here' } } ``` When `sparkscan` is configured, [`getBalance()`](/sdk/wallet-modules/wallet-spark/api-reference) returns SparkScan's soft balance from `btcSoftBalanceSats`. ### Automatic Retry The `syncAndRetry` option tells [`sendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference) and [`payLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference) to sync wallet state and retry once after a failure. **Type:** `boolean` (optional) **Default:** `false` **Example:** ```javascript const config = { network: 'MAINNET', syncAndRetry: true } ``` You can also call [`syncWalletBalance()`](/sdk/wallet-modules/wallet-spark/api-reference) directly when you want to reconcile wallet state before retrying an operation. ## Network Configuration The wallet can be configured for different Spark networks: ```javascript // Mainnet configuration const mainnetConfig = { network: 'MAINNET' } // Regtest configuration const regtestConfig = { network: 'REGTEST' } ``` ## BIP-44 Derivation Path Spark uses the [BIP-44](/resources/concepts#bip-44-multi-account-hierarchy) coin type 998, resulting in derivation paths like: - `m/44'/998'/0'/0/0` for MAINNET account index 0 - `m/44'/998'/0'/0/1` for MAINNET account index 1 - `m/44'/998'/2'/0/0` for SIGNET account index 0 - `m/44'/998'/3'/0/0` for REGTEST account index 0 The path follows the pattern `m/44'/998'/{networkNumber}'/0/{index}` where: - `998` is the coin type for Spark - `networkNumber` is 0 for MAINNET, 2 for SIGNET, or 3 for REGTEST - `index` is the account index This ensures compatibility with standard [BIP-44](/resources/concepts#bip-44-multi-account-hierarchy) wallets while using Spark's unique coin type identifier. ## Complete Configuration Example ```javascript import WalletManagerSpark from '@tetherto/wdk-wallet-spark' // Create wallet manager with configuration const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const wallet = new WalletManagerSpark(seedPhrase, { network: 'MAINNET', sparkscan: { apiKey: 'your-api-key-here' }, syncAndRetry: true }) // Get accounts (no additional configuration needed) const account0 = await wallet.getAccount(0) const account1 = await wallet.getAccount(1) // Clean up when done wallet.dispose() ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Spark Wallet Usage Get started with WDK's Spark Wallet API *** ### Need Help? *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/guides/check-balances Description: Query native Spark balances and read-only account balances. This guide explains how to read a [native Spark balance](#native-spark-balance), query a [token balance](#token-balance), and check [read-only account balances](#read-only-account-balances). ## Native Spark Balance You can read the account balance in satoshis using [`account.getBalance()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Native Spark Balance" const balance = await account.getBalance() console.log('Balance:', balance, 'satoshis') console.log('Balance in BTC:', Number(balance) / 100000000) ``` Balances are in satoshis (1 BTC = 100,000,000 satoshis). If you configure [`sparkscan`](/sdk/wallet-modules/wallet-spark/configuration#sparkscan-balance-polling), [`account.getBalance()`](/sdk/wallet-modules/wallet-spark/api-reference) returns SparkScan's `btcSoftBalanceSats` value instead of the Spark SDK balance: ```javascript title="SparkScan Balance Polling" const wallet = new WalletManagerSpark(seedPhrase, { network: 'MAINNET', sparkscan: { apiKey: 'your-api-key-here', }, }) const account = await wallet.getAccount(0) const balance = await account.getBalance() console.log('SparkScan balance:', balance) ``` ## Token Balance You can read a specific token balance using [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Token Balance" const tokenBalance = await account.getTokenBalance('token_address...') console.log('Token balance:', tokenBalance) ``` ## Read-Only Account Balances 1. Construct a [`WalletAccountReadOnlySpark`](/sdk/wallet-modules/wallet-spark/api-reference) instance with the Spark address and optional config. 2. Call [`readOnlyAccount.getBalance()`](/sdk/wallet-modules/wallet-spark/api-reference). You can create a read-only account from a Spark address using the [`WalletAccountReadOnlySpark`](/sdk/wallet-modules/wallet-spark/api-reference) constructor: ```javascript title="Read-Only Account" import { WalletAccountReadOnlySpark } from '@tetherto/wdk-wallet-spark' const readOnlyAccount = new WalletAccountReadOnlySpark('spark1...', { network: 'MAINNET' }) ``` You can read the native balance from that account using [`readOnlyAccount.getBalance()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Read-Only Native Balance" const balance = await readOnlyAccount.getBalance() console.log('Read-only balance:', balance, 'satoshis') ``` The same [`sparkscan`](/sdk/wallet-modules/wallet-spark/configuration#sparkscan-balance-polling) behavior applies to read-only accounts. You can read a token balance on a read-only account using [`readOnlyAccount.getTokenBalance()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Read-Only Token Balance" const tokenBalance = await readOnlyAccount.getTokenBalance('token_address...') console.log('Read-only token balance:', tokenBalance) ``` You can also obtain a read-only handle from an owned account with [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-spark/api-reference). ## Next Steps With balances verified, learn how to [send Spark and transfer tokens](/sdk/wallet-modules/wallet-spark/guides/send-and-transfer). *** ## Deposits and Withdrawals URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/guides/deposits-and-withdrawals Description: Fund Spark from Bitcoin layer 1 and withdraw back on-chain. This guide explains how to [get a single-use deposit address](#get-a-single-use-deposit-address), [claim deposits](#claim-deposits), [query static deposit addresses](#query-static-deposit-addresses), [query UTXOs for a deposit address](#query-utxos-for-a-deposit-address), and [withdraw to Bitcoin layer 1](#withdraw-to-bitcoin-layer-1). ## Get a Single-Use Deposit Address 1. Call [`account.getSingleUseDepositAddress()`](/sdk/wallet-modules/wallet-spark/api-reference). 2. Send Bitcoin to the returned on-chain address and wait for confirmation. You can generate a one-time Bitcoin deposit address using [`account.getSingleUseDepositAddress()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Single-Use Deposit Address" const depositAddress = await account.getSingleUseDepositAddress() console.log('Send Bitcoin to:', depositAddress) ``` ## Claim Deposits 1. Identify the Bitcoin transaction id that funded the deposit. 2. Call [`account.claimDeposit()`](/sdk/wallet-modules/wallet-spark/api-reference) with that id. You can credit the wallet after the deposit confirms using [`account.claimDeposit()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Claim Single-Use Deposit" const txId = 'f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16' const walletLeaves = await account.claimDeposit(txId) console.log('Deposit claimed:', walletLeaves) ``` ### 1. (optional) Use a static deposit address You can reuse one on-chain deposit address using [`account.getStaticDepositAddress()`](/sdk/wallet-modules/wallet-spark/api-reference), then credit the wallet with [`account.claimStaticDeposit()`](/sdk/wallet-modules/wallet-spark/api-reference) after the Bitcoin transaction confirms: ```javascript title="Static Deposit Flow" const staticAddress = await account.getStaticDepositAddress() console.log('Static deposit address:', staticAddress) const staticLeaves = await account.claimStaticDeposit( 'f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16' ) console.log('Static deposit claimed:', staticLeaves) ``` You can list unused single-use addresses with [`account.getUnusedDepositAddresses()`](/sdk/wallet-modules/wallet-spark/api-reference). The method returns a paginated result with `depositAddresses` and `offset` fields. ## Query Static Deposit Addresses You can list all existing static deposit addresses using [`account.getStaticDepositAddresses()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Query Static Deposit Addresses" const addresses = await account.getStaticDepositAddresses() console.log('Static deposit addresses:', addresses) ``` ## Query UTXOs for a Deposit Address You can check confirmed UTXOs for a specific deposit address using [`account.getUtxosForDepositAddress()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Query UTXOs" const result = await account.getUtxosForDepositAddress({ depositAddress: 'bc1q...' }) console.log('Confirmed UTXOs:', result.utxos) console.log('Offset:', result.offset) ``` ## Withdraw to Bitcoin Layer 1 1. Choose a Bitcoin `onchainAddress` and `amountSats`. 2. Request a cooperative exit quote with [`account.quoteWithdraw()`](/sdk/wallet-modules/wallet-spark/api-reference). 3. Call [`account.withdraw()`](/sdk/wallet-modules/wallet-spark/api-reference) with the destination and amount. You can request a withdrawal fee quote using [`account.quoteWithdraw()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Quote Withdrawal" const feeQuote = await account.quoteWithdraw({ withdrawalAddress: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', amountSats: 100000 }) console.log('Withdrawal fee quote:', feeQuote) ``` You can initiate the withdrawal using [`account.withdraw()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Withdraw to On-Chain Bitcoin" const withdrawal = await account.withdraw({ onchainAddress: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', amountSats: 100000 }) console.log('Withdrawal request:', withdrawal) ``` [`withdraw()`](/sdk/wallet-modules/wallet-spark/api-reference) accepts `onchainAddress` and `amountSats`. Run [`quoteWithdraw()`](/sdk/wallet-modules/wallet-spark/api-reference) first to understand the cooperative exit costs before initiating the withdrawal. ## Next Steps Learn how to [handle errors and follow operational best practices](/sdk/wallet-modules/wallet-spark/guides/handle-errors). *** ## Get Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/guides/get-started Description: Install the Spark wallet package and create your first account. This guide explains how to [install the package](#1-install-the-package), [create a wallet](#2-create-a-wallet), [get your first account](#3-get-your-first-account), and optionally [convert the account to read-only](#4-optional-convert-to-read-only). ## 1. Install the Package ### Prerequisites * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. Install [@tetherto/wdk-wallet-spark](https://www.npmjs.com/package/@tetherto/wdk-wallet-spark): ```bash title="Install @tetherto/wdk-wallet-spark" npm install @tetherto/wdk-wallet-spark ``` The WDK Spark wallet module uses ES Modules (`import` / `export`). Set `"type": "module"` in `package.json`, or run in an environment that supports ESM. ## 2. Create a Wallet You can create a wallet manager using the [`WalletManagerSpark`](/sdk/wallet-modules/wallet-spark/api-reference) constructor with a BIP-39 seed phrase: ```javascript title="Create Spark Wallet" import WalletManagerSpark from '@tetherto/wdk-wallet-spark' const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const wallet = new WalletManagerSpark(seedPhrase) const walletRegtest = new WalletManagerSpark(seedPhrase, { network: 'REGTEST' }) ``` **Secure the seed phrase:** Store this seed phrase securely. If it is lost, the user will permanently lose access to their funds. For development on **REGTEST**, you can request test funds from the [Lightspark Regtest Faucet](https://app.lightspark.com/regtest-faucet). The Spark wallet uses `@buildonspark/spark-sdk`. Network options are `MAINNET`, `SIGNET`, or `REGTEST`. There is no custom RPC provider option. ## 3. Get Your First Account 1. Call [`wallet.getAccount()`](/sdk/wallet-modules/wallet-spark/api-reference) with index `0`. 2. Call [`account.getAddress()`](/sdk/wallet-modules/wallet-spark/api-reference) to read the Spark address. You can retrieve the first account using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Get First Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Account address:', address) ``` ## 4. (optional) Convert to Read-Only You can create a read-only view of the account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Convert to Read-Only" const readOnlyAccount = await account.toReadOnlyAccount() ``` ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-spark/guides/manage-accounts). *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/guides/handle-errors Description: Handle Spark transaction and connection failures, plus fees and disposal. This guide explains how to [handle transaction errors](#transaction-errors), [handle connection errors](#connection-errors), and apply [best practices](#best-practices) for fees and secure cleanup. ## Transaction Errors Operations such as [`account.sendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference), [`account.transfer()`](/sdk/wallet-modules/wallet-spark/api-reference), [`account.payLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference), and [`account.withdraw()`](/sdk/wallet-modules/wallet-spark/api-reference) can throw. Wrap each call in `try/catch` and branch on `message` or error type when your runtime allows it: ```javascript title="Handle Transaction Errors" try { const result = await account.sendTransaction({ to: 'spark1...', value: 1000000 }) console.log('Transaction hash:', result.hash) } catch (error) { console.error('Send failed:', error.message) } ``` You can isolate Lightning failures by wrapping [`account.payLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Handle Lightning Errors" try { const payment = await account.payLightningInvoice({ encodedInvoice: 'lnbc500u1p...', maxFeeSats: 1000 }) console.log('Payment id:', payment.id) } catch (error) { console.error('Lightning payment failed:', error.message) } ``` ## Connection Errors Spark relies on network access through the Spark SDK. Failures may surface as timeouts, refused connections, or generic SDK errors. Handle them around any async wallet call: ```javascript title="Handle Connection Errors" try { const balance = await account.getBalance() console.log('Balance:', balance, 'satoshis') } catch (error) { if (error.message.includes('timeout') || error.message.includes('ECONNREFUSED')) { console.error('Network error: check connectivity and Spark service status') } else { console.error('Operation failed:', error.message) } } ``` ## Best Practices ### Fee management Native Spark sends and token transfers report zero fees, but withdrawals and Lightning payments can charge fees. Use [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-spark/api-reference) for wallet-level rate placeholders and [`account.quotePayLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference) for Lightning sends: ```javascript title="Inspect Spark Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal) console.log('Fast fee rate:', feeRates.fast) ``` ```javascript title="Quote Lightning Fee Before Paying" const lightningFee = await account.quotePayLightningInvoice({ encodedInvoice: 'lnbc500u1p...' }) console.log('Estimated Lightning fee:', Number(lightningFee), 'satoshis') ``` [`quoteWithdraw()`](/sdk/wallet-modules/wallet-spark/api-reference) should run before [`withdraw()`](/sdk/wallet-modules/wallet-spark/api-reference) so you understand cooperative exit costs. ### Dispose of sensitive data Clear keys from memory when a session ends. Call [`account.dispose()`](/sdk/wallet-modules/wallet-spark/api-reference) for each account and [`wallet.dispose()`](/sdk/wallet-modules/wallet-spark/api-reference) on the manager: ```javascript title="Dispose Wallet Resources" try { const result = await account.sendTransaction({ to: 'spark1...', value: 1000000 }) console.log('Transaction hash:', result.hash) } finally { account.dispose() wallet.dispose() } ``` After [`dispose()`](/sdk/wallet-modules/wallet-spark/api-reference), the account cannot sign new operations. Call disposal when the wallet UI or job is finished. ## Next Steps Return to the [Spark wallet usage overview](/sdk/wallet-modules/wallet-spark/usage) or open the [API Reference](/sdk/wallet-modules/wallet-spark/api-reference) for full method signatures. *** ## Lightning Payments URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/guides/lightning-payments Description: Create Lightning invoices, pay invoices, and inspect payment status. This guide explains how to [create a Lightning invoice](#create-a-lightning-invoice), [pay a Lightning invoice](#pay-a-lightning-invoice), [estimate Lightning fees](#estimate-lightning-fees), and [fetch a Lightning send request](#fetch-a-lightning-send-request). ## Create a Lightning Invoice 1. Choose an amount in satoshis and an optional memo. 2. Call [`account.createLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference). You can create a BOLT11 invoice using [`account.createLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Create Lightning Invoice" const invoice = await account.createLightningInvoice({ amountSats: 50000, memo: 'Payment for services' }) console.log('Lightning invoice:', invoice.invoice) ``` ## Pay a Lightning Invoice 1. Obtain a BOLT11 `encodedInvoice` string. 2. Set `maxFeeSats` to cap routing fees. 3. Call [`account.payLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference). You can pay an invoice using [`account.payLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Pay Lightning Invoice" const payment = await account.payLightningInvoice({ encodedInvoice: 'lnbc500u1p...', maxFeeSats: 1000 }) console.log('Payment result:', payment) ``` If you enable [`syncAndRetry`](/sdk/wallet-modules/wallet-spark/configuration#automatic-retry), the wallet syncs state and retries [`account.payLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference) once after a failure: ```javascript title="Retry Lightning Payment Once" const wallet = new WalletManagerSpark(seedPhrase, { network: 'MAINNET', syncAndRetry: true, }) const account = await wallet.getAccount(0) await account.payLightningInvoice({ encodedInvoice: 'lnbc500u1p...', maxFeeSats: 1000, }) ``` ## Estimate Lightning Fees You can estimate the routing fee before paying using [`account.quotePayLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Quote Lightning Payment Fee" const feeEstimate = await account.quotePayLightningInvoice({ encodedInvoice: 'lnbc500u1p...' }) console.log('Fee estimate:', Number(feeEstimate), 'satoshis') ``` Older references to `getLightningSendFeeEstimate()` map to [`quotePayLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference). ## Fetch a Lightning Send Request You can load a prior send request by id using [`account.getLightningSendRequest()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Get Lightning Send Request" const sendRequest = await account.getLightningSendRequest(payment.id) if (sendRequest) { console.log('Payment status:', sendRequest.status) } ``` Use the `id` from the object returned by [`payLightningInvoice()`](/sdk/wallet-modules/wallet-spark/api-reference). ## Next Steps Learn how to [handle Bitcoin layer 1 deposits and withdrawals](/sdk/wallet-modules/wallet-spark/guides/deposits-and-withdrawals). *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/guides/manage-accounts Description: Retrieve Spark accounts by index and iterate over them. This guide explains how to [retrieve accounts by index](#retrieve-accounts-by-index), [retrieve accounts by derivation path](#retrieve-accounts-by-derivation-path), and [iterate over accounts](#iterate-over-accounts). ## Retrieve Accounts by Index 1. Call [`wallet.getAccount()`](/sdk/wallet-modules/wallet-spark/api-reference) with the account index. 2. Call [`account.getAddress()`](/sdk/wallet-modules/wallet-spark/api-reference) for each account. You can retrieve multiple accounts using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-spark/api-reference) with different index values: ```javascript title="Retrieve Accounts by Index" const account0 = await wallet.getAccount(0) const address0 = await account0.getAddress() console.log('Account 0 address:', address0) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` Accounts use BIP-44 paths `m/44'/998'/{networkNumber}'/0/{index}` where `998` is Spark’s coin type and `networkNumber` is `0` for MAINNET, `2` for SIGNET, or `3` for REGTEST. ## Retrieve Accounts by Derivation Path You can retrieve an account at a specific BIP-44 derivation path using [`wallet.getAccountByPath()`](/sdk/wallet-modules/wallet-spark/api-reference): ```javascript title="Retrieve Account by Path" const account = await wallet.getAccountByPath("0'/0/0") const address = await account.getAddress() console.log('Account address:', address) ``` The path segment is appended to the base path `m/44'/998'/`. For example, `"0'/0/0"` resolves to `m/44'/998'/0'/0/0` on MAINNET. See [`getAccountByPath()`](/sdk/wallet-modules/wallet-spark/api-reference) in the API reference. ## Iterate Over Accounts You can walk a range of indices using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-spark/api-reference) inside a loop: ```javascript title="Iterate Over Accounts" for (let i = 0; i < 5; i++) { const account = await wallet.getAccount(i) const address = await account.getAddress() const balance = await account.getBalance() console.log(`Account ${i}: ${address} (${balance} satoshis)`) } ``` ## Next Steps With accounts set up, learn how to [check balances](/sdk/wallet-modules/wallet-spark/guides/check-balances). *** ## Send and Transfer URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/guides/send-and-transfer Description: Send native Spark, transfer tokens, and estimate fees. This guide explains how to [send native Spark](#send-spark), [transfer tokens](#transfer-tokens), and [estimate fees](#estimate-fees). ## Send Spark 1. Build the recipient Spark address and amount in satoshis. 2. Call [`account.sendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference#sendtransactionto-value). You can send native Spark (satoshis) using [`account.sendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference#sendtransactionto-value): ```javascript title="Send Native Spark" const result = await account.sendTransaction({ to: 'spark1...', value: 1000000 }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee) ``` On-chain Spark transfers report `fee` as `0`. Memos are not supported on [`sendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference#sendtransactionto-value). Use valid Spark network addresses. [`account.signTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference#signtransactiontx) is exposed for `IWalletAccount` compatibility, but it always throws on Spark. Spark transfers are collaboratively signed with Spark operators, so a standalone signed payload cannot be produced for separate broadcast. Use [`account.sendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference#sendtransactionto-value) for Spark sends. If you enable [`syncAndRetry`](/sdk/wallet-modules/wallet-spark/configuration#automatic-retry), the wallet syncs its state and retries [`account.sendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference#sendtransactionto-value) once after a failure: ```javascript title="Retry Failed Sends Once" const wallet = new WalletManagerSpark(seedPhrase, { network: 'MAINNET', syncAndRetry: true, }) const account = await wallet.getAccount(0) await account.sendTransaction({ to: 'spark1...', value: 1000000, }) ``` ## Transfer Tokens You can move tokens to another Spark address using [`account.transfer()`](/sdk/wallet-modules/wallet-spark/api-reference#transferoptions): ```javascript title="Transfer Tokens" const transferResult = await account.transfer({ token: 'btkn1...', amount: BigInt(1000000), recipient: 'spark1...' }) console.log('Transfer hash:', transferResult.hash) console.log('Transfer fee:', Number(transferResult.fee)) ``` Token identifiers use Bech32m (for example `btkn1...`). Amounts use the token’s base units. ## Estimate Fees ### Spark native and token transfer quotes You can preview the fee for a native send using [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-spark/api-reference#quotesendtransactionto-value): ```javascript title="Quote Native Send" const quote = await account.quoteSendTransaction({ to: 'spark1...', value: 1000000 }) console.log('Estimated fee:', quote.fee) ``` You can preview the fee for a token transfer using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-spark/api-reference#quotetransferoptions): ```javascript title="Quote Token Transfer" const transferQuote = await account.quoteTransfer({ token: 'btkn1...', amount: BigInt(1000000), recipient: 'spark1...' }) console.log('Estimated transfer fee:', Number(transferQuote.fee)) ``` ### Wallet-level fee rates You can read wallet-level fee rate placeholders using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-spark/api-reference#getfeerates): ```javascript title="Wallet Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal:', feeRates.normal) console.log('Fast:', feeRates.fast) ``` Spark network fees for native sends and token transfers are zero; [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-spark/api-reference#getfeerates) returns `{ normal: 0n, fast: 0n }`. Lightning flows can still incur fees; see [Lightning payments](/sdk/wallet-modules/wallet-spark/guides/lightning-payments). If you want to reconcile wallet state before retrying manually, call [`account.syncWalletBalance()`](/sdk/wallet-modules/wallet-spark/api-reference#syncwalletbalance): ```javascript title="Sync Wallet Balance" await account.syncWalletBalance() ``` ## Next Steps Learn how to [create and pay Lightning invoices](/sdk/wallet-modules/wallet-spark/guides/lightning-payments). *** ## Wallet Spark Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-spark/usage Description: Guide to using the @tetherto/wdk-wallet-spark module. # Usage The `@tetherto/wdk-wallet-spark` module provides wallet management for the Spark network. Install the package and create your first Spark wallet. Retrieve accounts by index and iterate over them. Query native Spark balances and read-only accounts. Send native Spark, transfer tokens, and estimate fees. Create and pay Lightning invoices and inspect payment status. Fund from Bitcoin layer 1 and withdraw on-chain. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Spark Wallet Configuration Get started with WDK's Spark Wallet API *** ### Need Help? *** ## Wallet TON Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton Description: Overview of the @tetherto/wdk-wallet-ton module A simple and secure package to manage BIP-44 wallets for the TON blockchain. This package provides a clean API for creating, managing, and interacting with TON wallets using BIP-39 seed phrases and TON-specific derivation paths. **Default Derivation Path Change in v1.0.0-beta.6+** The default derivation path was updated in v1.0.0-beta.6 to match ecosystem conventions: - **Previous path** (<= v1.0.0-beta.5): `m/44'/607'/0'/0/{index}` - **Current path** (v1.0.0-beta.6+): `m/44'/607'/{index}'` If you're upgrading from an earlier version, existing wallets created with the old path will generate different addresses. Make sure to migrate any existing wallets or use the old path explicitly if needed for compatibility. Use [`getAccountByPath`](/sdk/wallet-modules/wallet-ton/api-reference) to supply an explicit derivation path when importing or recreating legacy wallets. ## Features - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases - **TON Derivation Paths**: Support for BIP-44 standard derivation paths for TON (m/44'/607') - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase - **TON Address Support**: Generate and manage TON addresses using V5R1 wallet contracts - **Message Signing**: Sign and verify messages using TON cryptography - **Transaction Management**: Send transactions and get fee estimates - **Jetton Support**: Query native TON and Jetton token balances - **TypeScript Support**: Full TypeScript definitions included - **Memory Safety**: Secure private key management with automatic memory cleanup using sodium-universal - **Provider Flexibility**: Support for custom TON RPC endpoints and TON Center API ## Supported Networks This package works with the TON blockchain (The Open Network), including: - **TON Mainnet** - **TON Testnet** ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's TON Wallet configuration Get started with WDK's TON Wallet API Get started with WDK's TON Wallet usage *** ## Need Help? *** ## Wallet TON Gasless Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless Description: Overview of the @tetherto/wdk-wallet-ton-gasless module A simple and secure package to manage gasless transactions on the TON blockchain. This package provides a clean API for creating, managing, and interacting with TON wallets using BIP-39 seed phrases and TON-specific derivation paths, with support for gasless transactions through a paymaster system. ## Features - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases - **TON Derivation Paths**: Support for BIP-44 standard derivation paths for TON (m/44'/607') - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase - **TON Address Support**: Generate and manage TON addresses using V5R1 wallet contracts - **Message Signing**: Sign and verify messages using TON cryptography - **Gasless Transactions**: Execute transactions without requiring TON for gas fees - **Paymaster Integration**: Built-in support for paymaster-based fee delegation - **Jetton Support**: Query native TON and Jetton token balances - **TypeScript Support**: Full TypeScript definitions included - **Memory Safety**: Secure private key management with automatic memory cleanup using sodium-universal - **Provider Flexibility**: Support for both TON Center and TON API endpoints ## Supported Networks This package works with the TON blockchain (The Open Network), including: - **TON Mainnet** - **TON Testnet** ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's TON Gasless Wallet configuration Get started with WDK's TON Gasless Wallet API Get started with WDK's with TON Gasless Wallet usage *** ## Need Help? *** ## API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-ton-gasless ## Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerTonGasless](#walletmanagertongasless) | Main class for managing gasless TON wallets | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountTonGasless](#walletaccounttongasless) | Individual gasless TON wallet account implementation | [Constructor](#constructor-1), [Methods](#methods-1) | | [WalletAccountReadOnlyTonGasless](#walletaccountreadonlytongasless) | Read-only gasless TON wallet account | [Constructor](#constructor-2), [Methods](#methods-2) | ### WalletManagerTonGasless The main class for managing gasless TON wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. #### Constructor ```javascript new WalletManagerTonGasless(seed, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (TonGaslessWalletConfig): Configuration object - `tonClient` (object | TonClient | array): TON client configuration, instance, or an array of configurations or instances for failover - `url` (string): TON Center API URL (e.g., 'https://toncenter.com/api/v3') - `secretKey` (string, optional): API key for TON Center - `tonApiClient` (object | TonApiClient | array): TON API client configuration, instance, or an array of configurations or instances for failover - `url` (string): TON API URL (e.g., 'https://tonapi.io/v2') - `secretKey` (string, optional): API key for TON API - `paymasterToken` (object): Paymaster token configuration - `address` (string): Paymaster Jetton master contract address - `retries` (number, optional): Failover retries used when `tonClient` and `tonApiClient` are arrays (default: 3) - `transferMaxFee` (number | bigint, optional): Maximum fee for transfer operations **Example:** ```javascript const wallet = new WalletManagerTonGasless(seedPhrase, { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' }, tonApiClient: { url: 'https://tonapi.io/v2', secretKey: 'your-tonapi-key' }, paymasterToken: { address: 'EQ...' }, retries: 3, transferMaxFee: 1000000000 }) ``` #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAccount(index)` | Returns a gasless wallet account at the specified index | `Promise\` | | `getAccountByPath(path)` | Returns a gasless wallet account at the specified BIP-44 derivation path | `Promise\` | | `getFeeRates()` | Returns current fee rates for transactions | `Promise\<{normal: number, fast: number}\>` | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` | ##### `getAccount(index)` Returns a gasless wallet account at the specified index. Index `n` derives the account at `m/44'/607'/n'`. **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise\` - The wallet account **Example:** ```javascript // Derivation path m/44'/607'/0' const account = await wallet.getAccount(0) ``` ##### `getAccountByPath(path)` Returns a gasless wallet account at the specified BIP-44 derivation path, relative to `m/44'/607'`. **Parameters:** - `path` (string): The derivation path (e.g., "0'") **Returns:** `Promise\` - The wallet account **Example:** ```javascript // Derivation path m/44'/607'/1' const account = await wallet.getAccountByPath("1'") ``` ##### `getFeeRates()` Returns current fee rates for transactions based on blockchain config. **Returns:** `Promise\<{normal: number, fast: number}\>` - Object containing fee rates **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal) console.log('Fast fee rate:', feeRates.fast) ``` ##### `dispose()` Disposes all wallet accounts, clearing private keys from memory. **Example:** ```javascript wallet.dispose() ``` ### WalletAccountTonGasless Individual gasless TON wallet account implementation. Extends `WalletAccountReadOnlyTonGasless` and implements `IWalletAccount`. #### Constructor ```javascript new WalletAccountTonGasless(seed, path, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `path` (string): BIP-44 derivation path (e.g., "0'/0/0") - `config` (TonGaslessWalletConfig): Configuration object (same as WalletManagerTonGasless) #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's TON address | `Promise\` | | `sign(message)` | Signs a message using the account's private key | `Promise\` | | `signTransaction(tx)` | Not supported on gasless; always throws | `Promise\` | | `sendTransaction(tx)` | Not supported on gasless; always throws | `Promise\` | | `verify(message, signature)` | Verifies a message signature | `Promise\` | | `transfer(options, config?)` | Transfers tokens using gasless transactions | `Promise\<{hash: string, fee: number}\>` | | `quoteTransfer(options, config?)` | Estimates the fee for a token transfer | `Promise\<{fee: number}\>` | | `getBalance()` | Returns the native TON balance (in nanotons) | `Promise\` | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific token | `Promise\` | | `getPaymasterTokenBalance()` | Returns the balance of the paymaster token | `Promise\` | | `toReadOnlyAccount()` | Returns a read-only copy of the account | `Promise\` | | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` | ##### `getAddress()` Returns the account's address. **Returns:** `Promise\` - The account's TON address **Example:** ```javascript const address = await account.getAddress() console.log('Account address:', address) ``` ##### `sign(message)` Signs a message using the account's private key. **Parameters:** - `message` (string): The message to sign **Returns:** `Promise\` - The message signature **Example:** ```javascript const signature = await account.sign('Hello, World!') console.log('Signature:', signature) ``` ##### `signTransaction(tx)` Not supported on the gasless module. This method always throws. The gasless module only supports paymaster-funded Jetton transfers through [`transfer()`](#transferoptions-config). Use the standard `@tetherto/wdk-wallet-ton` module for offline transaction signing. **Parameters:** - `tx` (TonTransaction): The transaction **Returns:** `Promise\` - Never resolves; always throws **Example:** ```javascript // Throws: "Method 'signTransaction(tx)' not supported on ton gasless." await account.signTransaction({ to: 'EQ...', value: 1000000000n }) ``` ##### `sendTransaction(tx)` Not supported on the gasless module. This method always throws. To move funds, use [`transfer()`](#transferoptions-config), which relays a paymaster-funded Jetton transfer. **Parameters:** - `tx` (TonTransaction): The transaction **Returns:** `Promise\` - Never resolves; always throws **Example:** ```javascript // Throws: "Method 'sendTransaction(tx)' not supported on ton gasless." await account.sendTransaction({ to: 'EQ...', value: 1000000000n }) ``` ##### `verify(message, signature)` Verifies a message signature. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise\` - True if the signature is valid **Example:** ```javascript const isValid = await account.verify('Hello, World!', signature) console.log('Signature valid:', isValid) ``` ##### `transfer(options, config?)` Transfers a Jetton using a gasless transaction, paying the fee with the configured paymaster token. This is the only way to move funds on the gasless module: `sendTransaction()` and `signTransaction()` are not supported and throw. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): Token contract address - `recipient` (string): Recipient TON address - `amount` (number): Amount in token's base units - `config` (object, optional): Override configuration - `paymasterToken` (object, optional): Override default paymaster token - `address` (string): Paymaster token address - `transferMaxFee` (number, optional): Override maximum fee **Returns:** `Promise\<{hash: string, fee: number}\>` - Transfer result **Example:** ```javascript const result = await account.transfer({ token: 'EQ...', recipient: 'EQ...', amount: 1000000000 }, { paymasterToken: { address: 'EQ...' }, transferMaxFee: 2000000000 }) ``` ##### `quoteTransfer(options)` Estimates the fee for a Jetton (TON token) transfer. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): Token contract address - `recipient` (string): Recipient TON address - `amount` (number): Amount in token's base units - `config` (object, optional): Override configuration - `paymasterToken` (object, optional): Override default paymaster token **Returns:** `Promise\<{fee: number}\>` - Object containing fee estimate (in paymaster token base units) **Example:** ```javascript const quote = await account.quoteTransfer({ token: 'EQ...', recipient: 'EQ...', amount: 1000000000 }); console.log('Transfer fee estimate:', quote.fee, 'paymaster token units'); ``` ##### `getBalance()` Returns the native TON balance (in nanotons). **Returns:** `Promise\` - Balance in nanotons **Example:** ```javascript const balance = await account.getBalance(); console.log('Balance:', balance, 'nanotons'); ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific Jetton (TON token). **Parameters:** - `tokenAddress` (string): The token contract address **Returns:** `Promise\` - Token balance in base units **Example:** ```javascript const tokenBalance = await account.getTokenBalance('EQ...'); console.log('Token balance:', tokenBalance, 'token base units'); ``` ##### `getPaymasterTokenBalance()` Returns the balance of the paymaster Jetton (used for gasless fees). **Returns:** `Promise\` - Paymaster Jetton balance in base units **Example:** ```javascript const paymasterBalance = await account.getPaymasterTokenBalance(); console.log('Paymaster Jetton balance:', paymasterBalance); ``` ##### `toReadOnlyAccount()` Returns a read-only copy of the account. The same instance is reused on subsequent calls. **Returns:** `Promise\` - The read-only account **Example:** ```javascript const readOnlyAccount = await account.toReadOnlyAccount() ``` ##### `dispose()` Disposes the wallet account, clearing private keys from memory. **Example:** ```javascript account.dispose() ``` #### Properties | Property | Type | Description | |----------|------|-------------| | `index` | `number` | The derivation path's index of this account | | `path` | `string` | The full derivation path of this account | | `keyPair` | `{publicKey: Uint8Array, privateKey: Uint8Array \| null}` | The account's public and private key pair. `privateKey` is `null` after [`dispose()`](#dispose-1) | The key pair's `Uint8Array` values are bound to the wallet account: any external change reflects on the internal representation. Treat the key pair as a read-only view of the keys and never mutate its contents. **Example:** ```javascript const { publicKey, privateKey } = account.keyPair console.log('Public key length:', publicKey.length) console.log('Private key length:', privateKey?.length) ``` ### WalletAccountReadOnlyTonGasless Read-only gasless TON wallet account. #### Constructor ```javascript new WalletAccountReadOnlyTonGasless(publicKey, config) ``` **Parameters:** - `publicKey` (string | Uint8Array): The account's public key - `config` (TonGaslessWalletConfig): Configuration object #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's TON address | `Promise\` | | `getBalance()` | Returns the native TON balance | `Promise\` | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific token | `Promise\` | | `getPaymasterTokenBalance()` | Returns the balance of the paymaster token | `Promise\` | | `quoteTransfer(options, config?)` | Estimates the fee for a token transfer | `Promise\<{fee: number}\>` | | `verify(message, signature)` | Verifies a message signature | `Promise\` | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise\` | ##### `getAddress()` Returns the account's TON address. **Returns:** `Promise\` - The account's TON address **Example:** ```javascript const address = await readOnlyAccount.getAddress() console.log('Account address:', address) ``` ##### `getBalance()` Returns the native TON balance (in nanotons). **Returns:** `Promise\` - Balance in nanotons **Example:** ```javascript const balance = await readOnlyAccount.getBalance() console.log('TON balance:', balance, 'nanotons') ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific token. **Parameters:** - `tokenAddress` (string): The token contract address **Returns:** `Promise\` - Token balance in base units **Example:** ```javascript const tokenBalance = await readOnlyAccount.getTokenBalance('EQ...') console.log('Token balance:', tokenBalance, 'token base units') ``` ##### `getPaymasterTokenBalance()` Returns the balance of the paymaster token (used for gasless fees). **Returns:** `Promise\` - Paymaster token balance in base units **Example:** ```javascript const paymasterBalance = await readOnlyAccount.getPaymasterTokenBalance() console.log('Paymaster token balance:', paymasterBalance) ``` ##### `quoteTransfer(options, config?)` Estimates the fee for a token transfer. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): Token contract address - `recipient` (string): Recipient TON address - `amount` (number): Amount in token's base units - `config` (object, optional): Override configuration - `paymasterToken` (object, optional): Override default paymaster token **Returns:** `Promise\<{fee: number}\>` - Object containing fee estimate (in paymaster token base units) **Example:** ```javascript const quote = await readOnlyAccount.quoteTransfer({ token: 'EQ...', recipient: 'EQ...', amount: 1000000000 }) console.log('Transfer fee estimate:', quote.fee, 'paymaster token units') ``` ##### `verify(message, signature)` Verifies a message signature. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise\` - True if the signature is valid **Example:** ```javascript const isValid = await readOnlyAccount.verify('Hello, World!', signature) console.log('Signature valid:', isValid) ``` ##### `getTransactionReceipt(hash)` Returns a transaction's receipt. **Parameters:** - `hash` (string): The transaction's hash **Returns:** `Promise\` - The receipt, or null if the transaction has not been included in a block yet **Example:** ```javascript const receipt = await readOnlyAccount.getTransactionReceipt('transaction-hash') if (receipt) { console.log('Transaction receipt:', receipt) } else { console.log('Transaction not yet included in a block') } ``` ## Types ### TonGaslessWalletConfig ```typescript type TonClientConfig = { /** TON Center API URL @example 'https://toncenter.com/api/v3' */ url: string; /** Optional API key for TON Center */ secretKey?: string; }; type TonApiClientConfig = { /** TON API URL @example 'https://tonapi.io/v2' */ url: string; /** Optional API key for TON API */ secretKey?: string; }; type TonGaslessWalletConfig = { /** * TON client configuration or instance. Provide an array of configurations * or instances to enable failover across clients. */ tonClient: TonClientConfig | TonClient | Array; /** * TON API client configuration or instance. Provide an array of * configurations or instances to enable failover across API clients. */ tonApiClient: TonApiClientConfig | TonApiClient | Array; /** * Paymaster token configuration */ paymasterToken: { /** Paymaster Jetton master contract address @example 'EQ...' */ address: string; }; /** * Additional failover retry attempts after the initial call fails, used only * when tonClient and tonApiClient are arrays. Total attempts = 1 + retries. * @default 3 */ retries?: number; /** * Maximum fee for transfer operations (in paymaster Jetton base units) */ transferMaxFee?: number | bigint; }; ``` ### TransferOptions ```typescript interface TransferOptions { /** * Token contract address * @example 'EQ...' */ token: string; /** * Recipient's TON address * @example 'EQ...' */ recipient: string; /** * Amount in token's base units */ amount: number; } ``` ### TransferResult ```typescript interface TransferResult { /** * Transaction hash */ hash: string; /** * Fee paid in paymaster token units */ fee: number; } ``` ### KeyPair ```typescript type KeyPair = { /** * Public key bytes */ publicKey: Uint8Array; /** * Private key bytes (sensitive data). Set to null after dispose(). */ privateKey: Uint8Array | null; } ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's TON Gasless Wallet Usage Get started with WDK's TON Gasless Wallet Configuration *** ## Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-ton-gasless ## Wallet Configuration ```javascript import WalletManagerTonGasless from '@tetherto/wdk-wallet-ton-gasless' const config = { // Required parameters tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional }, tonApiClient: { url: 'https://tonapi.io/v2', secretKey: 'your-ton-api-key' // Optional }, paymasterToken: { address: 'EQ...' // Paymaster Jetton master contract address }, // Optional parameters retries: 3, // Failover retries when tonClient/tonApiClient are arrays transferMaxFee: 10000000 // Maximum fee in paymaster Jetton base units } const wallet = new WalletManagerTonGasless(seedPhrase, config) ``` ## Account Configuration ```javascript import { WalletAccountTonGasless } from '@tetherto/wdk-wallet-ton-gasless' const accountConfig = { // Required parameters tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional }, tonApiClient: { url: 'https://tonapi.io/v2', secretKey: 'your-ton-api-key' // Optional }, paymasterToken: { address: 'EQ...' // Paymaster Jetton master contract address }, // Optional parameters retries: 3, // Failover retries when tonClient/tonApiClient are arrays transferMaxFee: 10000000 // Maximum fee in paymaster Jetton base units } const account = new WalletAccountTonGasless(seedPhrase, "0'", accountConfig) ``` ## Configuration Options ### tonClient The `tonClient` option configures the TON Center API client for blockchain interactions. You can pass a single configuration object, a `TonClient` instance, or an array of configurations or instances to enable failover. **Type:** ```typescript type TonClientConfig = { /** * TON Center API endpoint URL * @example 'https://toncenter.com/api/v3' */ url: string; /** * Optional API key for TON Center * Required for higher rate limits */ secretKey?: string; }; // tonClient accepts a single config/instance or an array for failover type TonClient = TonClientConfig | TonClientInstance | Array; ``` **Required:** Yes When you provide an array, the wallet automatically falls back to the next client in the list if a connection error occurs. See [retries](#retries) for the retry count. ### tonApiClient The `tonApiClient` option configures the TON API client used to build and relay gasless transfers. Like `tonClient`, it accepts a single configuration object, a `TonApiClient` instance, or an array of configurations or instances for failover. **Type:** ```typescript type TonApiClientConfig = { /** * TON API endpoint URL * @example 'https://tonapi.io/v2' */ url: string; /** * Optional API key for TON API */ secretKey?: string; }; ``` **Required:** Yes ### paymasterToken The `paymasterToken` option specifies the Jetton used to pay gasless transfer fees instead of native TON. **Type:** ```typescript type PaymasterToken = { /** * Paymaster Jetton master contract address * @example 'EQ...' */ address: string; }; ``` **Required:** Yes **Example:** ```javascript const config = { paymasterToken: { address: 'EQ...' // Paymaster Jetton master contract address } } ``` ### retries The `retries` option sets the number of additional failover attempts after the initial call fails, used only when `tonClient` and `tonApiClient` are arrays of configurations or instances. Total attempts equal `1 + retries`. If `retries` exceeds the number of clients, failover loops back and retries already-failed clients in round-robin order. **Type:** `number` **Required:** No (default: `3`) **Example:** ```javascript const config = { tonClient: [ { url: 'https://toncenter.com/api/v3' }, { url: 'https://backup.toncenter.com/api/v3', secretKey: 'your-api-key' } ], tonApiClient: [ { url: 'https://tonapi.io/v2' }, { url: 'https://backup.tonapi.io/v2' } ], retries: 3 } ``` ### transferMaxFee The `transferMaxFee` option sets the maximum allowed fee in paymaster Jetton base units for transfer operations. A transfer throws if its estimated fee reaches this limit. **Type:** `number | bigint` **Required:** No **Example:** ```javascript const config = { transferMaxFee: 10000000 // Maximum fee in paymaster Jetton base units } ``` ## Complete Configuration Example Here's a complete configuration example with all options, including failover: ```javascript const config = { // TON Client (Required) - array enables failover tonClient: [ { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' }, { url: 'https://backup.toncenter.com/api/v3' } ], // TON API Client (Required) - array enables failover tonApiClient: [ { url: 'https://tonapi.io/v2', secretKey: 'your-ton-api-key' }, { url: 'https://backup.tonapi.io/v2' } ], // Paymaster Token (Required) paymasterToken: { address: 'EQ...' // Paymaster Jetton master contract address }, // Failover Retries (Optional) retries: 3, // Fee Limits (Optional) transferMaxFee: 10000000 // Maximum fee in paymaster Jetton base units } ``` The default derivation path changed in `1.0.0-beta.5`. Accounts now derive at `m/44'/607'/{index}'` to align with `@tetherto/wdk-wallet-ton`. Wallets created with `1.0.0-beta.4` or earlier derived `getAccount(index)` at `m/44'/607'/0'/0/{index}`, so the same seed produces different addresses after upgrading. Migrate existing accounts using [`getAccountByPath()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getaccountbypathpath) with the old path if you need to keep prior addresses. ## Security Considerations - Keep API keys and secrets secure and never expose them in client-side code - Use environment variables for sensitive configuration values - Always use HTTPS URLs for API endpoints - Set appropriate `transferMaxFee` limits to prevent excessive fees - Validate the paymaster token address before using it in configuration Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's TON Gasless Wallet Usage Get started with WDK's TON Gasless Wallet API *** ## Need Help? *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/guides/check-balances Description: Query native TON, Jetton, and paymaster token balances. This guide explains how to check [native TON balances](#native-ton-balance), [Jetton token balances](#jetton-token-balance), [paymaster token balances](#paymaster-token-balance), and [read-only account balances](#read-only-account-balances). ## Native TON Balance You can retrieve the native TON balance using [`account.getBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference): ```javascript title="Get Native TON Balance" const balance = await account.getBalance() console.log('Native TON balance:', balance, 'nanotons') ``` ## Jetton Token Balance You can check the balance of a specific Jetton token using [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#gettokenbalancetokenaddress): ```javascript title="Get Jetton Token Balance" const jettonAddress = 'EQ...' // Jetton contract address const jettonBalance = await account.getTokenBalance(jettonAddress) console.log('Jetton token balance:', jettonBalance) ``` ## Paymaster Token Balance You can check the balance of the configured paymaster token using [`account.getPaymasterTokenBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference): ```javascript title="Get Paymaster Token Balance" const paymasterBalance = await account.getPaymasterTokenBalance() console.log('Paymaster Jetton balance:', paymasterBalance) ``` The paymaster token balance determines how many gasless transfers you can execute. Ensure the paymaster has sufficient token balance before initiating gasless transfers. ## Read-Only Account Balances You can check balances for any public key without a seed phrase using [`WalletAccountReadOnlyTonGasless`](/sdk/wallet-modules/wallet-ton-gasless/api-reference): ```javascript title="Create Read-Only Account" import { WalletAccountReadOnlyTonGasless } from '@tetherto/wdk-wallet-ton-gasless' const readOnlyAccount = new WalletAccountReadOnlyTonGasless(publicKey, { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional }, tonApiClient: { url: 'https://tonapi.io/v2', secretKey: 'your-ton-api-key' // Optional }, paymasterToken: { address: 'EQ...' // Paymaster Jetton contract address } }) ``` You can retrieve balances from a read-only account using [`readOnlyAccount.getBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference) and [`readOnlyAccount.getPaymasterTokenBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference): ```javascript title="Read-Only Balances" const balance = await readOnlyAccount.getBalance() console.log('Native TON balance:', balance) const paymasterBalance = await readOnlyAccount.getPaymasterTokenBalance() console.log('Paymaster token balance:', paymasterBalance) ``` ## Next Steps With balance checks in place, learn how to [send TON](/sdk/wallet-modules/wallet-ton-gasless/guides/send-transactions). *** ## Get Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/guides/get-started Description: Install and create your first gasless TON wallet. This guide explains how to [install the package](#1-install-the-package), [create a gasless wallet](#2-create-a-gasless-wallet), and [get your first account](#3-get-your-first-account). ## 1. Install the Package ### Prerequisites * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ```bash title="Install @tetherto/wdk-wallet-ton-gasless" npm install @tetherto/wdk-wallet-ton-gasless ``` ## 2. Create a Gasless Wallet You can create a new gasless wallet instance using the [`WalletManagerTonGasless`](/sdk/wallet-modules/wallet-ton-gasless/api-reference) constructor with a BIP-39 seed phrase, TON client endpoints, and a paymaster token configuration: ```javascript title="Create Gasless TON Wallet" import WalletManagerTonGasless, { WalletAccountTonGasless, WalletAccountReadOnlyTonGasless } from '@tetherto/wdk-wallet-ton-gasless' const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const wallet = new WalletManagerTonGasless(seedPhrase, { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional }, tonApiClient: { url: 'https://tonapi.io/v2', secretKey: 'your-ton-api-key' // Optional }, paymasterToken: { address: 'EQ...' // Paymaster Jetton master contract address }, transferMaxFee: 10000000 // Optional: maximum fee in paymaster Jetton base units }) ``` **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. ## 3. Get Your First Account You can retrieve an account at a given index using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getaccountindex): ```javascript title="Get Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Wallet address:', address) ``` ## 4. (optional) Convert to Read-Only You can convert an owned account to a read-only account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference): ```javascript title="Convert to Read-Only" const readOnlyAccount = await account.toReadOnlyAccount() ``` ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-ton-gasless/guides/manage-accounts). *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/guides/handle-errors Description: Handle errors, manage fees, and dispose of sensitive data in gasless TON wallets. This guide covers how to [handle gasless transfer errors](#handle-gasless-transfer-errors) and [handle unsupported method errors](#handle-unsupported-method-errors), plus [best practices](#best-practices) for fee management and memory cleanup. ## Handle Gasless Transfer Errors Gasless transfers can fail for reasons specific to the paymaster model. Wrap calls to [`account.transfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#transferoptions-config) in `try/catch` blocks: ```javascript title="Gasless Transfer Error Handling" try { const result = await account.transfer({ token: 'EQ...', recipient: 'EQ...', amount: 1000000000 }) console.log('Transfer hash:', result.hash) } catch (error) { if (error.message.includes('insufficient jetton balance')) { console.error('Please add more Jetton tokens to your wallet') } else if (error.message.includes('insufficient paymaster balance')) { console.error('Please add more paymaster tokens for gas fees') } else if (error.message.includes('invalid address')) { console.error('The recipient address is invalid') } else if (error.message.includes('max fee')) { console.error('The transfer fee exceeds your configured maximum') } else { console.error('Transfer failed:', error.message) } } ``` ## Handle Unsupported Method Errors The gasless module supports only paymaster-funded Jetton transfers. Both [`account.sendTransaction()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#sendtransactiontx) and [`account.signTransaction()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#signtransactiontx) throw when called. Use [`account.transfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#transferoptions-config) instead, and guard any code path that might reach these methods: ```javascript title="Unsupported Method Handling" try { await account.sendTransaction({ to: 'EQ...', value: 1000000000 }) } catch (error) { // "Method 'sendTransaction(tx)' not supported on ton gasless." console.error('Use account.transfer() for gasless Jetton transfers:', error.message) } ``` ## Best Practices ### Manage Fee Limits Set `transferMaxFee` when creating the wallet to prevent gasless transfers from exceeding a maximum cost. You can retrieve current network rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getfeerates): ```javascript title="Fee Management" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'nanotons') console.log('Fast fee rate:', feeRates.fast, 'nanotons') ``` ### Dispose of Sensitive Data Call [`dispose()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#dispose-1) on accounts and wallet managers to clear private keys and sensitive data from memory when they are no longer needed: ```javascript title="Memory Cleanup" account.dispose() wallet.dispose() ``` Always call [`dispose()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#dispose-1) in a `finally` block or cleanup handler to ensure sensitive data is cleared even if an error occurs. *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/guides/manage-accounts Description: Work with multiple gasless TON accounts and custom derivation paths. This guide explains how to [retrieve accounts by index](#retrieve-accounts-by-index), [use custom derivation paths](#retrieve-account-by-custom-derivation-path), and [iterate over multiple accounts](#iterate-over-multiple-accounts). ## Retrieve Accounts by Index You can access accounts derived from the default BIP-44 path using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getaccountindex). Each index `n` derives the account at `m/44'/607'/n'`: ```javascript title="Get Accounts by Index" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Account 0 address:', address) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` ## Retrieve Account by Custom Derivation Path You can request an account at a specific BIP-44 derivation path using [`wallet.getAccountByPath()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getaccountbypathpath). The path is relative to `m/44'/607'`: ```javascript title="Custom Derivation Path" const customAccount = await wallet.getAccountByPath("5'") const customAddress = await customAccount.getAddress() console.log('Custom account address:', customAddress) ``` The default derivation path changed in `1.0.0-beta.5` to align with `@tetherto/wdk-wallet-ton`. `getAccount(index)` now derives `m/44'/607'/{index}'`; earlier versions used `m/44'/607'/0'/0/{index}`. To recover an address created before the upgrade, pass the old relative path, for example `getAccountByPath("0'/0/5")`. ## Iterate Over Multiple Accounts You can loop through multiple accounts using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getaccountindex) to inspect addresses and balances in bulk: ```javascript title="Multi-Account Iteration" async function listAccounts(wallet) { const accounts = [] for (let i = 0; i < 5; i++) { const account = await wallet.getAccount(i) const address = await account.getAddress() const balance = await account.getBalance() const paymasterBalance = await account.getPaymasterTokenBalance() accounts.push({ index: i, address, balance, paymasterBalance }) } return accounts } ``` ## Next Steps Now that you can access your accounts, learn how to [check balances](/sdk/wallet-modules/wallet-ton-gasless/guides/check-balances). *** ## Send Transactions URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/guides/send-transactions Description: Why native TON sends are unsupported on the gasless module and what to use instead. This guide explains why [native TON sends are not supported](#native-ton-sends-are-not-supported) on the gasless module and how to [check current fee rates](#check-current-fee-rates). ## Native TON Sends Are Not Supported The gasless module does not send native TON. Both [`account.sendTransaction()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#sendtransactiontx) and [`account.signTransaction()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#signtransactiontx) throw on this module. The module's purpose is to relay paymaster-funded Jetton transfers, so the only way to move funds is [`account.transfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#transferoptions-config). Calling `sendTransaction()` throws an error: ```javascript title="sendTransaction Throws" // Throws: "Method 'sendTransaction(tx)' not supported on ton gasless." await account.sendTransaction({ to: 'EQ...', value: 1000000000 // 1 TON in nanotons }) ``` To send native TON, use the standard `@tetherto/wdk-wallet-ton` module. For gasless Jetton transfers, see [Transfer Jetton Tokens](/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens). ## Check Current Fee Rates You can retrieve current fee rates from the wallet manager using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getfeerates): ```javascript title="Get Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'nanotons') console.log('Fast fee rate:', feeRates.fast, 'nanotons') ``` ## Next Steps To transfer Jetton tokens with gasless fees (paid in paymaster tokens), see [Transfer Jetton Tokens](/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens). *** ## Sign and Verify Messages URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/guides/sign-verify-messages Description: Sign messages and verify signatures with gasless TON accounts. This guide explains how to [sign messages](#sign-a-message) with an owned account and [verify signatures](#verify-a-signature). ## Sign a Message You can produce a cryptographic signature for any string message using [`account.sign()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#signmessage): ```javascript title="Sign a Message" const message = 'Hello, TON!' const signature = await account.sign(message) console.log('Signature:', signature) ``` ## Verify a Signature You can verify that a signature is valid using [`account.verify()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#verifymessage-signature): ```javascript title="Verify a Signature" const isValid = await account.verify(message, signature) console.log('Signature valid:', isValid) ``` You can also create a [`WalletAccountReadOnlyTonGasless`](/sdk/wallet-modules/wallet-ton-gasless/api-reference) from any public key to verify signatures without access to the private key: ```javascript title="Verify with Read-Only Account" import { WalletAccountReadOnlyTonGasless } from '@tetherto/wdk-wallet-ton-gasless' const readOnlyAccount = new WalletAccountReadOnlyTonGasless(publicKey, { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' }, tonApiClient: { url: 'https://tonapi.io/v2', secretKey: 'your-ton-api-key' }, paymasterToken: { address: 'EQ...' } }) const isValid = await readOnlyAccount.verify(message, signature) console.log('Signature valid:', isValid) ``` ## Next Steps For best practices on handling errors, managing fees, and cleaning up memory, see [Handle Errors](/sdk/wallet-modules/wallet-ton-gasless/guides/handle-errors). *** ## Transfer Jetton Tokens URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens Description: Transfer Jetton tokens gaslessly with fees paid in paymaster tokens. This guide explains how to [transfer Jetton tokens gaslessly](#transfer-tokens-gasless), [override paymaster configuration](#override-paymaster-configuration), [estimate transfer fees](#estimate-transfer-fees), and [validate inputs before executing](#transfer-with-validation). ## Transfer Tokens (Gasless) You can send Jetton tokens gaslessly using [`account.transfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#transferoptions-config). Fees are deducted from the configured paymaster token: ```javascript title="Gasless Jetton Transfer" const result = await account.transfer({ token: 'EQ...', // Jetton master contract address recipient: 'EQ...', // Recipient's TON address amount: 1000000000 // Amount in Jetton's base units }) console.log('Transfer hash:', result.hash) console.log('Transfer fee:', result.fee, 'paymaster token units') ``` ## Override Paymaster Configuration You can override the default paymaster token and maximum fee on a per-transfer basis by passing a second configuration argument to [`account.transfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#transferoptions-config): ```javascript title="Transfer with Config Override" const result = await account.transfer({ token: 'EQ...', recipient: 'EQ...', amount: 1000000000 }, { paymasterToken: { address: 'EQ...' // Override default paymaster token }, transferMaxFee: 2000000000 // Override maximum allowed fee }) console.log('Transfer hash:', result.hash) console.log('Transfer fee:', result.fee, 'paymaster token units') ``` ## Estimate Transfer Fees You can get a fee estimate before executing the transfer using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#quotetransferoptions): ```javascript title="Quote Gasless Transfer" const quote = await account.quoteTransfer({ token: 'EQ...', recipient: 'EQ...', amount: 1000000 }) console.log('Transfer fee estimate:', quote.fee, 'paymaster token units') ``` ## Transfer with Validation Validate balances before transferring: 1. Use [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#gettokenbalancetokenaddress) to check Jetton balance. 2. Use [`account.getPaymasterTokenBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getpaymastertokenbalance) to verify sufficient paymaster funds. 3. Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#quotetransferoptions) to estimate fees. 4. Execute the transfer with [`account.transfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#transferoptions-config): ```javascript title="Validated Gasless Transfer" async function transferWithValidation(account, jettonAddress, recipient, amount) { if (typeof jettonAddress !== 'string' || jettonAddress.length === 0) { throw new Error('Invalid Jetton address format') } if (typeof recipient !== 'string' || recipient.length === 0) { throw new Error('Invalid recipient address format') } const balance = await account.getTokenBalance(jettonAddress) if (balance < amount) { throw new Error('Insufficient Jetton balance') } const paymasterBalance = await account.getPaymasterTokenBalance() console.log('Paymaster token balance:', paymasterBalance) const quote = await account.quoteTransfer({ token: jettonAddress, recipient, amount }) console.log('Estimated fee (paymaster token):', quote.fee) const result = await account.transfer({ token: jettonAddress, recipient, amount }) console.log('Transfer hash:', result.hash) console.log('Actual fee (paymaster token):', result.fee) return result } ``` ## Next Steps Learn how to [sign and verify messages](/sdk/wallet-modules/wallet-ton-gasless/guides/sign-verify-messages) with your gasless TON account. *** ## Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton-gasless/usage Description: Guide to using the @tetherto/wdk-wallet-ton-gasless module. The `@tetherto/wdk-wallet-ton-gasless` module provides wallet management for the TON blockchain with gasless transaction support, where transfer fees are paid using a configured paymaster token instead of native TON. Install the package and create your first gasless wallet. Work with multiple accounts and custom derivation paths. Query native TON, Jetton, and paymaster token balances. Send native TON transactions. Transfer Jetton tokens gaslessly with paymaster fees. Sign messages and verify signatures. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's TON Gasless Wallet Configuration Get started with WDK's TON Gasless Wallet API --- ## Need Help? *** ## API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-ton ## API Reference ### Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerTon](#walletmanagerton) | Main class for managing TON wallets | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountTon](#walletaccountton) | Individual TON wallet account implementation | [Constructor](#constructor-1), [Methods](#methods-1) | | [WalletAccountReadOnlyTon](#walletaccountreadonlyton) | Read-only TON wallet account | [Constructor](#constructor-2), [Methods](#methods-2) | ### WalletManagerTon The main class for managing TON wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. #### Constructor ```javascript new WalletManagerTon(seed, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (object): Configuration object - `tonClient` (object | TonClient): TON client configuration or instance - `url` (string): TON Center API URL (e.g., 'https://toncenter.com/api/v3') - `secretKey` (string, optional): API key for TON Center - `transferMaxFee` (number, optional): Maximum fee amount for transfer operations (in nanotons) **Example:** ```javascript const wallet = new WalletManagerTon(seedPhrase, { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' }, transferMaxFee: 1000000000 // Maximum fee in nanotons (1 TON) }) ``` #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAccount(index)` | Returns a wallet account at the specified index | `Promise\` | | `getAccountByPath(path)` | Returns a wallet account at the specified BIP-44 derivation path | `Promise\` | | `getFeeRates()` | Returns current fee rates for transactions | `Promise\<{normal: number, fast: number}\>` | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` | ##### `getAccount(index)` Returns a wallet account at the specified index. **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise\` - The wallet account **Example:** ```javascript const account = await wallet.getAccount(0) ``` ##### `getAccountByPath(path)` Returns a wallet account at the specified BIP-44 derivation path. **Parameters:** - `path` (string): The derivation path (e.g., "0'/0/0") **Returns:** `Promise\` - The wallet account **Example:** ```javascript const account = await wallet.getAccountByPath("0'/0/1") ``` ##### `getFeeRates()` Returns current fee rates for normal and fast transactions. **Returns:** `Promise\` - Object containing normal and fast fee rates **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'nanotons') console.log('Fast fee rate:', feeRates.fast, 'nanotons') ``` ##### `dispose()` Disposes all wallet accounts, clearing private keys from memory. **Example:** ```javascript wallet.dispose() ``` #### Properties ##### `seed` The wallet's seed phrase. **Type:** `string | Uint8Array` **Example:** ```javascript console.log('Seed phrase:', wallet.seed) ``` ### WalletAccountTon Individual TON wallet account implementation. Extends `WalletAccountReadOnlyTon` and implements `IWalletAccount`. #### Constructor ```javascript new WalletAccountTon(seed, path, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `path` (string): BIP-44 derivation path (e.g., "0'/0/0") - `config` (object): Configuration object - `tonClient` (object | TonClient): TON client configuration or instance - `url` (string): TON Center API URL - `secretKey` (string, optional): API key for TON Center - `transferMaxFee` (number, optional): Maximum fee amount for transfer operations **Example:** ```javascript const account = new WalletAccountTon(seedPhrase, "0'/0/0", { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' }, transferMaxFee: 10000000 // Maximum fee in nanotons (e.g., 0.01 TON) }) ``` #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's TON address | `Promise\` | | `sign(message)` | Signs a message using the account's private key | `Promise\` | | `verify(message, signature)` | Verifies a message signature | `Promise\` | | `signTransaction(tx)` | Signs a transaction offline without broadcasting it | `Promise\` | | `sendTransaction(tx)` | Sends a TON transaction | `Promise\<{hash: string, fee: number}\>` | | `quoteSendTransaction(tx)` | Estimates the fee for a TON transaction | `Promise\<{fee: number}\>` | | `transfer(options)` | Transfers Jetton tokens to another address | `Promise\<{hash: string, fee: number}\>` | | `quoteTransfer(options)` | Estimates the fee for a Jetton transfer | `Promise\<{fee: number}\>` | | `getBalance()` | Returns the native TON balance (in nanotons) | `Promise\` | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific Jetton token | `Promise\` | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise\` | | `toReadOnlyAccount()` | Returns a read-only copy of the account | `Promise\` | | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` | ##### `verify(message, signature)` Verifies a message signature. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise\` - True if the signature is valid **Example:** ```javascript const readOnlyAccount = new WalletAccountReadOnlyTon(publicKey, { tonClient: { url: '...' } }) const isValid = await readOnlyAccount.verify('Hello, World!', signature) console.log('Signature valid:', isValid) ``` ##### `getAddress()` Returns the account's address. **Returns:** `Promise\` - The account's TON address **Example:** ```javascript const address = await account.getAddress() console.log('Account address:', address) ``` ##### `sign(message)` Signs a message using the account's private key. **Parameters:** - `message` (string): The message to sign **Returns:** `Promise\` - The message signature **Example:** ```javascript const signature = await account.sign('Hello, World!') console.log('Signature:', signature) ``` ##### `signTransaction(tx)` Signs a transaction offline and returns the signed external-message body without broadcasting it. Use this to build a transaction on a signing device and broadcast it elsewhere. Added in v1.0.0-beta.8. **Parameters:** - `tx` (object): The transaction object (same shape as `sendTransaction`) - `to` (string): Recipient TON address (e.g., 'EQ...') - `value` (number | bigint): Amount in nanotons (1 TON = 1,000,000,000 nanotons) - `bounceable` (boolean, optional): Whether the destination address is bounceable - `body` (string | Cell, optional): Optional message body **Returns:** `Promise\` - The signed external-message body as a TON `Cell`. Call `cell.toBoc().toString('base64')` to obtain a wire-format payload ready for broadcast. **Example:** ```javascript const cell = await account.signTransaction({ to: 'EQ...', // TON address value: 1000000000 // 1 TON in nanotons }); const boc = cell.toBoc().toString('base64'); console.log('Signed payload:', boc); ``` ##### `sendTransaction(tx)` Sends a TON transaction and returns the result with hash and fee. **Parameters:** - `tx` (object): The transaction object - `to` (string): Recipient TON address (e.g., 'EQ...') - `value` (number): Amount in nanotons (1 TON = 1,000,000,000 nanotons) - `bounceable` (boolean, optional): Whether the address is bounceable (TON-specific, optional) **Returns:** `Promise\<{hash: string, fee: number}\>` - Object containing hash and fee (in nanotons) **Example:** ```javascript const result = await account.sendTransaction({ to: 'EQ...', // TON address value: 1000000000 // 1 TON in nanotons }); console.log('Transaction hash:', result.hash); console.log('Transaction fee:', result.fee, 'nanotons'); ``` ##### `quoteSendTransaction(tx)` Estimates the fee for a transaction. **Parameters:** - `tx` (object): The transaction object (same as sendTransaction) - `to` (string): Recipient TON address (e.g., 'EQ...') - `value` (number): Amount in nanotons (1 TON = 1,000,000,000 nanotons) - `bounceable` (boolean, optional): Whether the address is bounceable (TON-specific, optional) **Returns:** `Promise\<{fee: number}\>` - Object containing fee estimate (in nanotons) **Example:** ```javascript const quote = await account.quoteSendTransaction({ to: 'EQ...', // TON address value: 1000000000 // 1 TON in nanotons }); console.log('Estimated fee:', quote.fee, 'nanotons'); ``` ##### `transfer(options)` Transfers Jettons (TON tokens) to another address. **Parameters:** - `options` (object): Transfer options - `token` (string): Jetton master contract address (TON format, e.g., 'EQ...') - `recipient` (string): Recipient TON address (e.g., 'EQ...') - `amount` (number): Amount in Jetton's base units **Returns:** `Promise\<{hash: string, fee: number}\>` - Object containing hash and fee (in nanotons) **Example:** ```javascript const result = await account.transfer({ token: 'EQ...', // Jetton master contract address recipient: 'EQ...', // Recipient's TON address amount: 1000000000 // Amount in Jetton's base units }); console.log('Transfer hash:', result.hash); console.log('Transfer fee:', result.fee, 'nanotons'); ``` ##### `quoteTransfer(options)` Estimates the fee for a Jetton (TON token) transfer. **Parameters:** - `options` (object): Transfer options (same as transfer) - `token` (string): Jetton master contract address (TON format, e.g., 'EQ...') - `recipient` (string): Recipient TON address (e.g., 'EQ...') - `amount` (number): Amount in Jetton's base units **Returns:** `Promise\<{fee: number}\>` - Object containing fee estimate (in nanotons) **Example:** ```javascript const quote = await account.quoteTransfer({ token: 'EQ...', // Jetton master contract address recipient: 'EQ...', // Recipient's TON address amount: 1000000000 // Amount in Jetton's base units }); console.log('Transfer fee estimate:', quote.fee, 'nanotons'); ``` ##### `getBalance()` Returns the native TON balance (in nanotons). **Returns:** `Promise\` - Balance in nanotons **Example:** ```javascript const balance = await account.getBalance(); console.log('Balance:', balance, 'nanotons'); ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific Jetton (TON token). **Parameters:** - `tokenAddress` (string): The Jetton master contract address (TON format, e.g., 'EQ...') **Returns:** `Promise\` - Token balance in base units **Example:** ```javascript const tokenBalance = await account.getTokenBalance('EQ...'); console.log('Token balance:', tokenBalance, 'nanotons'); ``` ##### `getTransactionReceipt(hash)` Returns a transaction's receipt if it has been mined. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise\` - Transaction receipt or null if not yet mined **Example:** ```javascript const receipt = await account.getTransactionReceipt('EQ...') console.log('Transaction confirmed:', receipt.success) ``` ##### `toReadOnlyAccount()` Returns a read-only copy of the account. The read-only account exposes balance and verification methods without holding the private key. The instance is cached, so repeated calls return the same read-only account. **Returns:** `Promise\` - The read-only account **Example:** ```javascript const readOnlyAccount = await account.toReadOnlyAccount() const address = await readOnlyAccount.getAddress() ``` ##### `dispose()` Disposes the wallet account, clearing private keys from memory. **Example:** ```javascript account.dispose() ``` #### Properties | Property | Type | Description | |----------|------|-------------| | `index` | `number` | The derivation path's index of this account | | `path` | `string` | The full derivation path of this account | | `keyPair` | `{publicKey: Uint8Array, privateKey: Uint8Array \| null}` | The account's public and private key pair. `privateKey` is `null` after the account is disposed. | The key pair arrays are bound to the wallet account: any external change to them is reflected in the account's internal state. Treat the key pair as a read-only view and never mutate its contents. **Example:** ```javascript const { publicKey, privateKey } = account.keyPair console.log('Public key length:', publicKey.length) console.log('Private key length:', privateKey.length) ``` ### WalletAccountReadOnlyTon Read-only TON wallet account. #### Constructor ```javascript new WalletAccountReadOnlyTon(publicKey, config) ``` **Parameters:** - `publicKey` (string): The account's public key (hex or base64) - `config` (object): Configuration object (same as WalletManagerTon) #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's TON address | `Promise\` | | `getBalance()` | Returns the native TON balance | `Promise\` | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific Jetton | `Promise\` | | `verify(message, signature)` | Verifies a message signature | `Promise\` | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise\` | ##### `getAddress()` Returns the account's address. **Returns:** `Promise\` - The account's TON address ##### `getBalance()` Returns the native TON balance. **Returns:** `Promise\` - Balance in nanotons ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific Jetton. **Parameters:** - `tokenAddress` (string): The Jetton master contract address **Returns:** `Promise\` - Token balance ##### `verify(message, signature)` Verifies a message signature. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise\` - True if the signature is valid **Example:** ```javascript const isValid = await readOnlyAccount.verify('Hello, World!', signature) console.log('Signature valid:', isValid) ``` ##### `getTransactionReceipt(hash)` Returns a transaction's receipt if it has been mined. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise\` - Transaction receipt or null if not yet mined **Example:** ```javascript const receipt = await readOnlyAccount.getTransactionReceipt('EQ...') console.log('Transaction confirmed:', receipt.success) ``` ## Types ### TonTransaction ```typescript interface TonTransaction { /** * Recipient's TON address in base64 format * @example 'EQD4FPq...' */ to: string; /** * Amount to send in nanotons (1 TON = 1,000,000,000 nanotons) * @example 1000000000 // 1 TON */ value: number | bigint; /** * If set, overrides the bounceability of the transaction */ bounceable?: boolean; /** * Optional message body */ body?: string | Cell; } ``` ### TransferOptions ```typescript interface TransferOptions { /** * Jetton master contract address * @example 'EQD4FPq...' */ token: string; /** * Recipient's TON address * @example 'EQD4FPq...' */ recipient: string; /** * Amount in Jetton's base units * @example 1000000000 // Amount depends on token decimals */ amount: number; } ``` ### TransactionResult ```typescript interface TransactionResult { /** * Transaction hash in base64 format * @example 'EQD4FPq...' */ hash: string; /** * Transaction fee in nanotons * @example 100000 // 0.0001 TON */ fee: number; } ``` ### FeeRates ```typescript interface FeeRates { /** * Fee rate for normal priority transactions (in nanotons) * @example 100000000 // 0.1 TON */ normal: number; /** * Fee rate for high priority transactions (in nanotons) * @example 200000000 // 0.2 TON */ fast: number; } ``` ### KeyPair ```typescript interface KeyPair { /** * Ed25519 public key */ publicKey: Uint8Array; /** * Ed25519 private key (sensitive data; null after the account is disposed) * @security Never expose or log this value */ privateKey: Uint8Array | null; } ``` ### TonWalletConfig ```typescript interface TonWalletConfig { /** * TON Center client configuration, a TonClient instance, or an array of * either. When an array is provided, connection errors cause the wallet to * fail over to the next client in the list. */ tonClient?: TonClientConfig | TonClient | Array; /** * Number of additional retry attempts after the initial call fails, used * only when tonClient is an array. Total attempts = 1 + retries. * @default 3 */ retries?: number; /** * Maximum allowed fee for transfers (in nanotons) * @example 1000000000 // 1 TON */ transferMaxFee?: number | bigint; } interface TonClientConfig { /** * TON Center API endpoint * @example 'https://toncenter.com/api/v3' */ url: string; /** * Optional API key for higher rate limits */ secretKey?: string; } ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's TON Wallet Usage Get started with WDK's TON Wallet Configuration *** ## Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-ton ## Wallet Configuration The `WalletManagerTon` accepts a configuration object that defines how the wallet interacts with the TON blockchain: ```javascript import WalletManagerTon from '@tetherto/wdk-wallet-ton' const config = { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional }, transferMaxFee: 1000000000 // Optional: Maximum fee in nanotons (1 TON) } const wallet = new WalletManagerTon(seedPhrase, config) ``` ## Account Configuration ```javascript import { WalletAccountTon } from '@tetherto/wdk-wallet-ton' const accountConfig = { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional }, transferMaxFee: 1000000000 // Optional: Maximum fee in nanotons } const account = new WalletAccountTon(seedPhrase, "0'/0/0", accountConfig) ``` ## Configuration Options ### tonClient The `tonClient` option configures the TON Center API client for blockchain interactions. It accepts a single client configuration, a `TonClient` instance, or an array of either for endpoint failover. **Type:** ```typescript interface TonClientConfig { /** * TON Center API endpoint URL */ url: string; /** * Optional API key for TON Center * Required for higher rate limits */ secretKey?: string; } // tonClient accepts one config/instance or an array of them type TonClientOption = | TonClientConfig | TonClient | Array; ``` **Examples:** ```javascript // Basic configuration const config = { tonClient: { url: 'https://toncenter.com/api/v3' } } // With API key for higher rate limits const config = { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' } } ``` ### tonClient (array) and retries Since v1.0.0-beta.8, `tonClient` also accepts an array of configurations or instances. When a connection error occurs, the wallet automatically fails over to the next client in the list. The `retries` option sets the number of additional retry attempts after the initial call fails. **Type:** `retries` is `number` (optional) **Default:** `3` The total number of attempts is `1 + retries`. For example, `retries: 3` with four clients tries each client once before throwing. If `retries` exceeds the number of clients, the failover loops back and retries already-failed clients in round-robin order. ```javascript const config = { tonClient: [ { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' }, { url: 'https://toncenter.com/api/v3' } ], retries: 3 // Optional: additional retry attempts after the first failure } ``` ### transferMaxFee The `transferMaxFee` option sets the maximum allowed fee (in nanotons) for transfer operations. This helps prevent unexpectedly high transaction fees. **Type:** `number` (nanotons) **Default:** No maximum (undefined) **Examples:** ```javascript const config = { transferMaxFee: 1000000000 // 1 TON in nanotons } // Example with both options const config = { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' }, transferMaxFee: 1000000000 } ``` ## Read-Only Account Configuration For read-only accounts, you only need the TON client configuration: ```javascript import { WalletAccountReadOnlyTon } from '@tetherto/wdk-wallet-ton' const readOnlyConfig = { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional } } const readOnlyAccount = new WalletAccountReadOnlyTon(publicKey, readOnlyConfig) ``` ## Network Selection The TON network (mainnet or testnet) is determined by the TON Center API endpoint URL: - Mainnet: `https://toncenter.com/api/v3` - Testnet: `https://testnet.toncenter.com/api/v3` ## Derivation Paths TON wallets use BIP-44 standard derivation paths. The default derivation path follows ecosystem conventions: - Default path: `m/44'/607'/{index}'` (where `{index}` is the account index) **Default Derivation Path Change in v1.0.0-beta.6+** The default derivation path was updated in v1.0.0-beta.6 to match ecosystem conventions: - **Previous path** (<= v1.0.0-beta.5): `m/44'/607'/0'/0/{index}` - **Current path** (v1.0.0-beta.6+): `m/44'/607'/{index}'` If you're upgrading from an earlier version, existing wallets created with the old path will generate different addresses. Make sure to migrate any existing wallets or use the old path explicitly if needed for compatibility. Use [`getAccountByPath`](/sdk/wallet-modules/wallet-ton/api-reference) to supply an explicit derivation path when importing or recreating legacy wallets. ## Security Considerations - Always use HTTPS URLs for TON Center API endpoints - Keep API keys secure and never expose them in client-side code - Consider using environment variables for API keys - Set appropriate `transferMaxFee` limits for your use case Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's TON Wallet Usage Get started with WDK's TON Wallet API *** ## Need Help? *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/guides/check-balances Description: Query native TON and Jetton token balances. This guide explains how to check [native TON balances](#native-ton-balance), [Jetton token balances](#jetton-token-balance), and [read-only account balances](#read-only-account-balances). ## Native TON Balance You can retrieve the native TON balance using [`account.getBalance()`](/sdk/wallet-modules/wallet-ton/api-reference): ```javascript title="Get Native TON Balance" const balance = await account.getBalance() console.log('Native TON balance:', balance, 'nanotons') ``` On TON, values are expressed in nanotons (1 TON = 10^9 nanotons). ## Jetton Token Balance You can check the balance of a specific Jetton token using [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-ton/api-reference#gettokenbalancetokenaddress): ```javascript title="Get Jetton Token Balance" const jettonAddress = 'EQ...' // Jetton contract address const jettonBalance = await account.getTokenBalance(jettonAddress) console.log('Jetton token balance:', jettonBalance) ``` ## Read-Only Account Balances You can check balances for any public key without a seed phrase using [`WalletAccountReadOnlyTon`](/sdk/wallet-modules/wallet-ton/api-reference): ```javascript title="Create Read-Only Account" import { WalletAccountReadOnlyTon } from '@tetherto/wdk-wallet-ton' const readOnlyAccount = new WalletAccountReadOnlyTon(publicKey, { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional } }) ``` You can retrieve the native balance from a read-only account using [`readOnlyAccount.getBalance()`](/sdk/wallet-modules/wallet-ton/api-reference): ```javascript title="Read-Only Native Balance" const balance = await readOnlyAccount.getBalance() console.log('Read-only account balance:', balance) ``` You can also create a read-only account from an existing owned account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-ton/api-reference#toreadonlyaccount). ## Next Steps With balance checks in place, learn how to [send TON](/sdk/wallet-modules/wallet-ton/guides/send-transactions). *** ## Get Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/guides/get-started Description: Install and create your first TON wallet. This guide explains how to [install the package](#1-install-the-package), [create a wallet](#2-create-a-wallet), and [get your first account](#3-get-your-first-account). ## 1. Install the Package ### Prerequisites * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ```bash title="Install @tetherto/wdk-wallet-ton" npm install @tetherto/wdk-wallet-ton ``` ## 2. Create a Wallet You can create a new wallet instance using the [`WalletManagerTon`](/sdk/wallet-modules/wallet-ton/api-reference) constructor with a BIP-39 seed phrase and a TON Center client configuration: ```javascript title="Create TON Wallet" import WalletManagerTon, { WalletAccountTon, WalletAccountReadOnlyTon } from '@tetherto/wdk-wallet-ton' const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const wallet = new WalletManagerTon(seedPhrase, { tonClient: { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' // Optional } }) ``` **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. ## 3. Get Your First Account You can retrieve an account at a given index using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-ton/api-reference#getaccountindex): ```javascript title="Get Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Wallet address:', address) ``` ## 4. (optional) Convert to Read-Only You can convert an owned account to a read-only account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-ton/api-reference#toreadonlyaccount): ```javascript title="Convert to Read-Only" const readOnlyAccount = await account.toReadOnlyAccount() ``` ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-ton/guides/manage-accounts). *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/guides/handle-errors Description: Handle errors, manage fees, and dispose of sensitive data in TON wallets. This guide covers how to [handle transaction errors](#handle-transaction-errors) and [handle token transfer errors](#handle-token-transfer-errors), plus [best practices](#best-practices) for fee management and memory cleanup. ## Handle Transaction Errors Wrap transactions in `try/catch` blocks to handle common failure scenarios. Use [`account.sendTransaction()`](/sdk/wallet-modules/wallet-ton/api-reference#sendtransactiontx) with proper error handling: ```javascript title="Transaction Error Handling" try { const result = await account.sendTransaction({ to: 'EQ...', value: 1000000000, bounceable: true }) console.log('Transaction hash:', result.hash) console.log('Fee paid:', result.fee, 'nanotons') } catch (error) { if (error.message.includes('insufficient balance')) { console.error('Not enough TON to complete transaction') } else if (error.message.includes('invalid address')) { console.error('Invalid recipient address') } else if (error.message.includes('timeout')) { console.error('Network timeout, please try again') } else { console.error('Transaction failed:', error.message) } } ``` ## Handle Token Transfer Errors Jetton transfers can fail for multiple reasons, such as insufficient token balances. Use [`account.transfer()`](/sdk/wallet-modules/wallet-ton/api-reference#transferoptions) with error handling: ```javascript title="Token Transfer Error Handling" try { const result = await account.transfer({ token: 'EQ...', recipient: 'EQ...', amount: 1000000 }) console.log('Transfer submitted:', result.hash) } catch (error) { console.error('Transfer failed:', error.message) if (error.message.toLowerCase().includes('insufficient')) { console.log('Please add more tokens to your wallet') } else if (error.message.toLowerCase().includes('fee')) { console.log('The transfer fee exceeds your configured maximum') } } ``` ## Best Practices ### Manage Fee Limits Set `transferMaxFee` when creating the wallet to prevent transactions from exceeding a maximum cost. You can retrieve current network rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton/api-reference): ```javascript title="Fee Management" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'nanotons') console.log('Fast fee rate:', feeRates.fast, 'nanotons') ``` ### Dispose of Sensitive Data Call [`dispose()`](/sdk/wallet-modules/wallet-ton/api-reference) on accounts and wallet managers to clear private keys and sensitive data from memory when they are no longer needed: ```javascript title="Memory Cleanup" account.dispose() wallet.dispose() ``` Always call [`dispose()`](/sdk/wallet-modules/wallet-ton/api-reference) in a `finally` block or cleanup handler to ensure sensitive data is cleared even if an error occurs. *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/guides/manage-accounts Description: Work with multiple TON accounts and custom derivation paths. This guide explains how to [retrieve accounts by index](#retrieve-accounts-by-index), [use custom derivation paths](#retrieve-account-by-custom-derivation-path), and [iterate over multiple accounts](#iterate-over-multiple-accounts). ## Retrieve Accounts by Index You can access accounts derived from the default BIP-44 path using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-ton/api-reference#getaccountindex): ```javascript title="Get Accounts by Index" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Account 0 address:', address) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` ## Retrieve Account by Custom Derivation Path You can request an account at a specific BIP-44 derivation path using [`wallet.getAccountByPath()`](/sdk/wallet-modules/wallet-ton/api-reference#getaccountbypathpath): ```javascript title="Custom Derivation Path" const customAccount = await wallet.getAccountByPath("0'/0/5") const customAddress = await customAccount.getAddress() console.log('Custom account address:', customAddress) ``` ## Iterate Over Multiple Accounts You can loop through multiple accounts using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-ton/api-reference#getaccountindex) to inspect addresses and balances in bulk: ```javascript title="Multi-Account Iteration" async function listAccounts(wallet) { const accounts = [] for (let i = 0; i < 5; i++) { const account = await wallet.getAccount(i) const address = await account.getAddress() const balance = await account.getBalance() accounts.push({ index: i, address, balance }) } return accounts } ``` ## Next Steps Now that you can access your accounts, learn how to [check balances](/sdk/wallet-modules/wallet-ton/guides/check-balances). *** ## Send TON URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/guides/send-transactions Description: Send native TON and estimate transaction fees. This guide explains how to [send native TON](#send-native-ton), [estimate transaction fees](#estimate-transaction-fees), [use dynamic fee rates](#use-dynamic-fee-rates), and [sign a transaction offline](#sign-a-transaction-offline). On TON, values are expressed in nanotons (1 TON = 10^9 nanotons). Transactions support an optional `bounceable` parameter specific to the TON network. ## Send Native TON You can transfer TON to a recipient address using [`account.sendTransaction()`](/sdk/wallet-modules/wallet-ton/api-reference#sendtransactiontx): ```javascript title="Send TON" const result = await account.sendTransaction({ to: 'EQ...', // TON address value: 1000000000, // 1 TON in nanotons bounceable: true // Optional: specify if the address is bounceable }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee, 'nanotons') ``` ## Estimate Transaction Fees You can get a fee estimate before sending using [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-ton/api-reference#quotesendtransactiontx): ```javascript title="Quote Transaction Fee" const quote = await account.quoteSendTransaction({ to: 'EQ...', value: 1000000000, bounceable: true }) console.log('Estimated fee:', quote.fee, 'nanotons') ``` ## Use Dynamic Fee Rates You can retrieve current fee rates from the wallet manager using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton/api-reference): ```javascript title="Get Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'nanotons') console.log('Fast fee rate:', feeRates.fast, 'nanotons') ``` ## Sign a Transaction Offline You can sign a transaction without broadcasting it using [`account.signTransaction()`](/sdk/wallet-modules/wallet-ton/api-reference#signtransactiontx). This returns the signed external-message body as a TON `Cell`, which you can serialize and broadcast from another environment. ```javascript title="Sign Transaction Offline" const cell = await account.signTransaction({ to: 'EQ...', // TON address value: 1000000000 // 1 TON in nanotons }) // Serialize to a wire-format payload for broadcast elsewhere const boc = cell.toBoc().toString('base64') console.log('Signed payload:', boc) ``` ## Next Steps To transfer Jetton tokens instead of native TON, see [Transfer Jetton Tokens](/sdk/wallet-modules/wallet-ton/guides/transfer-tokens). *** ## Sign and Verify Messages URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/guides/sign-verify-messages Description: Sign messages and verify signatures with TON accounts. This guide explains how to [sign messages](#sign-a-message) with an owned account and [verify signatures](#verify-a-signature) using a read-only account. ## Sign a Message You can produce a cryptographic signature for any string message using [`account.sign()`](/sdk/wallet-modules/wallet-ton/api-reference#signmessage): ```javascript title="Sign a Message" const message = 'Hello, TON!' const signature = await account.sign(message) console.log('Signature:', signature) ``` ## Verify a Signature You can verify that a signature was produced by the corresponding private key using [`readOnlyAccount.verify()`](/sdk/wallet-modules/wallet-ton/api-reference#verifymessage-signature): ```javascript title="Verify a Signature" const readOnlyAccount = await account.toReadOnlyAccount() const isValid = await readOnlyAccount.verify(message, signature) console.log('Signature valid:', isValid) ``` You can also create a [`WalletAccountReadOnlyTon`](/sdk/wallet-modules/wallet-ton/api-reference) from any public key to verify signatures without access to the private key. ## Next Steps For best practices on handling errors, managing fees, and cleaning up memory, see [Handle Errors](/sdk/wallet-modules/wallet-ton/guides/handle-errors). *** ## Transfer Jetton Tokens URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/guides/transfer-tokens Description: Transfer Jetton tokens and estimate transfer fees on TON. This guide explains how to [transfer Jetton tokens](#transfer-tokens), [estimate transfer fees](#estimate-transfer-fees), and [validate inputs before executing](#transfer-with-validation). ## Transfer Tokens You can send Jetton tokens to a recipient address using [`account.transfer()`](/sdk/wallet-modules/wallet-ton/api-reference#transferoptions): ```javascript title="Transfer Jetton Tokens" const transferResult = await account.transfer({ token: 'EQ...', // Jetton contract address recipient: 'EQ...', // Recipient's TON address amount: 1000000 // Amount in Jetton's base units }) console.log('Transfer hash:', transferResult.hash) console.log('Transfer fee:', transferResult.fee, 'nanotons') ``` ## Estimate Transfer Fees You can get a fee estimate before executing the transfer using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-ton/api-reference#quotetransferoptions): ```javascript title="Quote Token Transfer" const transferQuote = await account.quoteTransfer({ token: 'EQ...', // Jetton contract address recipient: 'EQ...', // Recipient's TON address amount: 1000000 // Amount in Jetton's base units }) console.log('Transfer fee estimate:', transferQuote.fee, 'nanotons') ``` ## Transfer with Validation Validate addresses and check balances before transferring to catch errors early: 1. Use [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-ton/api-reference#gettokenbalancetokenaddress) to verify sufficient funds. 2. Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-ton/api-reference#quotetransferoptions) to confirm fees. 3. Execute the transfer with [`account.transfer()`](/sdk/wallet-modules/wallet-ton/api-reference#transferoptions): ```javascript title="Validated Jetton Transfer" async function transferJettonWithValidation(account, jettonAddress, recipient, amount) { if (typeof jettonAddress !== 'string' || jettonAddress.length === 0) { throw new Error('Invalid Jetton address format') } if (typeof recipient !== 'string' || recipient.length === 0) { throw new Error('Invalid recipient address format') } const balance = await account.getTokenBalance(jettonAddress) if (balance < amount) { throw new Error('Insufficient Jetton balance') } const quote = await account.quoteTransfer({ token: jettonAddress, recipient, amount }) console.log('Estimated fee:', quote.fee, 'nanotons') const result = await account.transfer({ token: jettonAddress, recipient, amount }) console.log('Transfer hash:', result.hash) console.log('Actual fee:', result.fee, 'nanotons') return result } ``` ## Next Steps Learn how to [sign and verify messages](/sdk/wallet-modules/wallet-ton/guides/sign-verify-messages) with your TON account. *** ## Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-ton/usage Description: Guide to using the @tetherto/wdk-wallet-ton module. The `@tetherto/wdk-wallet-ton` module provides wallet management for the TON blockchain. Install the package and create your first wallet. Work with multiple accounts and custom derivation paths. Query native TON and Jetton token balances. Send native TON and estimate transaction fees. Transfer Jetton tokens and estimate fees. Sign messages and verify signatures. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's TON Wallet Configuration Get started with WDK's TON Wallet API --- ### Need Help? *** ## Wallet Tron Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron Description: Overview of the @tetherto/wdk-wallet-tron module A simple and secure package to manage BIP-44 wallets for the Tron blockchain. This package provides a clean API for creating, managing, and interacting with Tron wallets using BIP-39 seed phrases and Tron-specific derivation paths. ## Features - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases - **Tron Derivation Paths**: Support for BIP-44 standard derivation paths for Tron - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase - **Tron Address Support:** Generate and manage Tron addresses - **Message Signing:** Sign and verify messages using Tron cryptography - **Transaction Management**: Send transactions and get fee estimates - **TRC20 Support:** Query native TRX and TRC20 token balances. - **TypeScript Support**: Full TypeScript definitions included - **Memory Safety**: Secure private key management with automatic memory cleanup - **Provider Flexibility:** Support for custom Tron RPC endpoints ## Supported Networks This package works with the Tron blockchain, including: - **Tron Mainnet** - **Tron Shasta Testnet** ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's Tron Wallet configuration Get started with WDK's Tron Wallet API Get started with WDK's with Tron Wallet usage *** ## Need Help? *** ## Wallet Tron Gas-Free Overview URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree Description: Overview of the @tetherto/wdk-wallet-tron-gasfree module A simple and secure package to manage BIP-44 wallets for the Tron blockchain with **gas-free TRC20 token transfers**. This package provides a clean API for creating, managing, and interacting with Tron wallets using BIP-39 seed phrases and Tron-specific derivation paths, with support for gas-free operations via a service provider. ## Features - **Gas-Free Transactions**: Support for gas-free transactions using TRC20 tokens. - **BIP-39 Seed Phrase Support**: Generate and validate BIP-39 mnemonic seed phrases. - **Tron Derivation Paths**: Support for BIP-44 standard derivation paths for Tron. - **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase. - **Tron Address Support**: Generate and manage Tron addresses. - **Message Signing**: Sign and verify messages using Tron cryptography. - **Transaction Management**: Send transactions and get fee estimates. - **TRC20 Support**: Query native TRX and TRC20 token balances. - **TypeScript Support**: Full TypeScript definitions included. - **Memory Safety**: Secure private key management with automatic memory cleanup. - **Provider Flexibility**: Support for custom Tron RPC endpoints. ## Supported Networks This package works with the Tron blockchain, including: - **Tron Mainnet** - **Tron Nile Testnet** ## Next Steps Get started with WDK in a Node.js environment Get started with WDK's Tron Gasfree Wallet configuration Get started with WDK's Tron Gasfree Wallet API Get started with WDK's with Tron Gasfree Wallet usage *** ## Need Help? *** ## API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-tron-gasfree ## Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerTronGasfree](#walletmanagertrongasfree) | Main class for managing gas-free Tron wallets. Extends `WalletManagerTron`. | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountTronGasfree](#walletaccounttrongasfree) | Individual gas-free Tron wallet account implementation. Extends `WalletAccountReadOnlyTronGasfree`. | [Constructor](#constructor-1), [Methods](#methods-1) | | [WalletAccountReadOnlyTronGasfree](#walletaccountreadonlytrongasfree) | Read-only gas-free Tron wallet account. | [Constructor](#constructor-2), [Methods](#methods-2) | ### WalletManagerTronGasfree The main class for managing gas-free Tron wallets. #### Constructor ```javascript new WalletManagerTronGasfree(seed, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (object): Configuration object - `chainId` (number): The blockchain's id - `provider` (string | TronWeb): Tron RPC endpoint URL or TronWeb instance - `gasFreeProvider` (string): Gas-free service endpoint - `serviceProvider` (string): Service provider Tron address - `verifyingContract` (string): Gas-free verifying contract address - `gasFreeApiKey` (string, optional): API key for signed GasFree provider requests - `gasFreeApiSecret` (string, optional): API secret for signed GasFree provider requests. Provide together with `gasFreeApiKey`. - `transferMaxFee` (number, optional): Maximum fee for transfer operations **Example:** ```javascript const wallet = new WalletManagerTronGasfree(seedPhrase, { chainId: 728126428, provider: 'https://api.trongrid.io', gasFreeProvider: 'https://gasfree.provider.url', serviceProvider: 'T...', verifyingContract: 'T...', gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret gasFreeApiSecret: 'your-api-secret', transferMaxFee: 10000000 // Optional }) ``` #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAccount(index)` | Returns a wallet account at the specified index | `Promise\` | | `getAccountByPath(path)` | Returns a wallet account at the specified BIP-44 derivation path | `Promise\` | | `getFeeRates()` | Returns current fee rates for normal and fast transactions | `Promise\<{normal: number, fast: number}\>` | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` | ##### `getAccount(index)` Returns a gas-free wallet account at the specified index. **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise\` - The wallet account **Example:** ```javascript const account = await wallet.getAccount(0) ``` ##### `getAccountByPath(path)` Returns a gas-free wallet account at the specified BIP-44 derivation path. **Parameters:** - `path` (string): The derivation path (e.g., "0'/0/0") **Returns:** `Promise\` - The wallet account **Example:** ```javascript const account = await wallet.getAccountByPath("0'/0/1") ``` ##### `getFeeRates()` Returns current fee rates for normal and fast transactions. **Returns:** `Promise\<{normal: number, fast: number}\>` - Object containing fee rates in sun - `normal`: Fee rate for normal priority transactions - `fast`: Fee rate for high priority transactions **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'sun') console.log('Fast fee rate:', feeRates.fast, 'sun') ``` ##### `dispose()` Disposes all wallet accounts, clearing private keys from memory. This method should be called when you're done using the wallet to ensure sensitive data is removed. **Example:** ```javascript wallet.dispose() ``` ### WalletAccountTronGasfree Individual gas-free Tron wallet account implementation. #### Constructor ```javascript new WalletAccountTronGasfree(seed, path, config) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `path` (string): BIP-44 derivation path (e.g., "0'/0/0") - `config` (object): Same configuration object as WalletManagerTronGasfree #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's address | `Promise\` | | `getBalance()` | Returns the native TRX balance (in sun) | `Promise\` | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific TRC20 token | `Promise\` | | `transfer(options)` | Transfers TRC20 tokens to another address | `Promise\<{hash: string, fee: bigint}\>` | | `quoteTransfer(options)` | Estimates the fee for a TRC20 transfer | `Promise\<{fee: bigint}\>` | | `sign(message)` | Signs a message using the account's private key | `Promise\` | | `signTransaction(tx)` | Unsupported on Tron GasFree; always throws | `Promise\` | | `sendTransaction(tx)` | Unsupported on Tron GasFree; always throws | `Promise\` | | `quoteSendTransaction(tx)` | Unsupported on Tron GasFree; always throws | `Promise\\>` | | `verify(message, signature)` | Verifies a message signature | `Promise\` | | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` | ##### `getAddress()` Returns the account's gas-free Tron address. **Returns:** `Promise\` - The account's Tron address **Example:** ```javascript const address = await account.getAddress() console.log('Account address:', address) ``` ##### `getBalance()` Returns the native TRX balance in sun units. **Returns:** `Promise\` - Balance in sun **Example:** ```javascript const balance = await account.getBalance() console.log('TRX Balance:', balance, 'sun') ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific TRC20 token. **Parameters:** - `tokenAddress` (string): The TRC20 contract address (e.g., 'T...') **Returns:** `Promise\` - Token balance in base units **Example:** ```javascript const tokenBalance = await account.getTokenBalance('T...') console.log('Token balance:', tokenBalance) ``` ##### `transfer(options)` Transfers TRC20 tokens to another address using the gas-free service. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): TRC20 contract address - `recipient` (string): Recipient's Tron address - `amount` (number): Amount in token base units **Returns:** `Promise\<{hash: string, fee: bigint}\>` - Object containing transaction hash and fee paid in token base units. The returned fee includes the estimated transfer fee plus any activation fee returned by the GasFree service. **Example:** ```javascript const result = await account.transfer({ token: 'T...', // TRC20 contract address recipient: 'T...', // Recipient's address amount: 1000000 // Amount in token base units }) console.log('Transaction hash:', result.hash) console.log('Fee paid:', result.fee, 'token base units') ``` ##### `quoteTransfer(options)` Estimates the fee for a TRC20 token transfer. **Parameters:** - `options` (TransferOptions): Transfer options (same as transfer method) **Returns:** `Promise\<{fee: bigint}\>` - Estimated fee in token base units. The estimate includes the token's transfer fee and, when the GasFree account is inactive, the token activation fee. **Example:** ```javascript const quote = await account.quoteTransfer({ token: 'T...', recipient: 'T...', amount: 1000000 }) console.log('Estimated fee:', quote.fee, 'token base units') ``` ##### `sign(message)` Signs a message using the account's private key. **Parameters:** - `message` (string): The message to sign **Returns:** `Promise\` - The message signature **Example:** ```javascript const signature = await account.sign('Hello, World!') console.log('Signature:', signature) ``` ##### `signTransaction(tx)` Transaction signing is not supported by the Tron GasFree module. This method is present for `IWalletAccount` compatibility and always throws `"Method 'signTransaction(tx)' not supported on tron gasfree."`. **Parameters:** - `tx` (TronTransaction): The transaction object **Returns:** `Promise\` - Never resolves successfully. ##### `sendTransaction(tx)` Native Tron transaction sending is not supported by the Tron GasFree module. Use `transfer(options)` for gas-free TRC20 transfers, or use the base Tron wallet module when you need native TRX transactions. **Parameters:** - `tx` (TronTransaction): The transaction object **Returns:** `Promise\` - The method always throws `"Method 'sendTransaction(tx)' not supported on tron gasfree."`. ##### `quoteSendTransaction(tx)` Native Tron transaction fee quotes are not supported by the Tron GasFree module. **Parameters:** - `tx` (TronTransaction): The transaction object **Returns:** `Promise\\>` - The method always throws `"Method 'quoteSendTransaction(tx)' not supported on tron gasfree."`. ##### `verify(message, signature)` Verifies a message signature. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise\` - True if the signature is valid **Example:** ```javascript const isValid = await account.verify('Hello, World!', signature) console.log('Signature valid:', isValid) ``` ##### `dispose()` Disposes the wallet account, clearing private keys from memory. **Example:** ```javascript account.dispose() ``` ### WalletAccountReadOnlyTronGasfree Read-only gas-free Tron wallet account. #### Constructor ```javascript new WalletAccountReadOnlyTronGasfree(address, config) ``` **Parameters:** - `address` (string): The account's Tron address - `config` (object): Configuration object - `chainId` (number): The blockchain's id - `provider` (string | TronWeb): Tron RPC endpoint URL or TronWeb instance - `gasFreeProvider` (string): Gas-free service endpoint - `serviceProvider` (string): Service provider Tron address - `verifyingContract` (string): Gas-free verifying contract address - `gasFreeApiKey` (string, optional): API key for signed GasFree provider requests - `gasFreeApiSecret` (string, optional): API secret for signed GasFree provider requests. Provide together with `gasFreeApiKey`. #### Methods | Method | Description | Returns | |--------|-------------|---------| | `getAddress()` | Returns the account's address | `Promise\` | | `getBalance()` | Returns the native TRX balance (in sun) | `Promise\` | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific TRC20 token | `Promise\` | | `quoteTransfer(options)` | Estimates the fee for a TRC20 transfer | `Promise\<{fee: bigint}\>` | | `quoteSendTransaction(tx)` | Unsupported on Tron GasFree; always throws | `Promise\\>` | | `verify(message, signature)` | Verifies a message signature | `Promise\` | ##### `getAddress()` Returns the account's gas-free Tron address. **Returns:** `Promise\` - The account's Tron address **Example:** ```javascript const address = await readOnlyAccount.getAddress() console.log('Account address:', address) ``` ##### `getBalance()` Returns the native TRX balance in sun units. **Returns:** `Promise\` - Balance in sun **Example:** ```javascript const balance = await readOnlyAccount.getBalance() console.log('TRX Balance:', balance, 'sun') ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific TRC20 token. **Parameters:** - `tokenAddress` (string): The TRC20 contract address (e.g., 'T...') **Returns:** `Promise\` - Token balance in base units **Example:** ```javascript const tokenBalance = await readOnlyAccount.getTokenBalance('T...') console.log('Token balance:', tokenBalance) ``` ##### `quoteTransfer(options)` Estimates the fee for a TRC20 token transfer without requiring private keys. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): TRC20 contract address - `recipient` (string): Recipient's Tron address - `amount` (number): Amount in token base units **Returns:** `Promise\<{fee: bigint}\>` - Estimated fee in token base units. The estimate includes the token's transfer fee and, when the GasFree account is inactive, the token activation fee. **Example:** ```javascript const quote = await readOnlyAccount.quoteTransfer({ token: 'T...', recipient: 'T...', amount: 1000000 }) console.log('Estimated fee:', quote.fee, 'token base units') ``` ##### `quoteSendTransaction(tx)` Native Tron transaction fee quotes are not supported by the Tron GasFree module. **Parameters:** - `tx` (TronTransaction): The transaction object **Returns:** `Promise\\>` - The method always throws `"Method 'quoteSendTransaction(tx)' not supported on tron gasfree."`. ##### `verify(message, signature)` Verifies a message signature. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify **Returns:** `Promise\` - True if the signature is valid **Example:** ```javascript const isValid = await readOnlyAccount.verify('Hello, World!', signature) console.log('Signature valid:', isValid) ``` ## Types ### TransferOptions Configuration options for token transfers. ```typescript interface TransferOptions { /** * The TRC20 token contract address * @example 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t' // USDT contract */ token: string; /** * The recipient's Tron address * @example 'TJYeasTPa6gpEEfQa7s9CqqqgvYh6JtpAR' */ recipient: string; /** * Amount to transfer in token base units * @example 1000000 // 1 USDT (6 decimals) */ amount: number; } ``` ### TransferResult Result object returned by transfer operations. ```typescript interface TransferResult { /** * The transaction hash * @example '0x123...' */ hash: string; /** * Fee paid in token base units * @example 1000 // Fee in token base units */ fee: bigint; } ``` ### FeeRates Fee rate information for transactions. ```typescript interface FeeRates { /** * Fee rate for normal priority transactions (in sun) * @example 1000 */ normal: number; /** * Fee rate for high priority transactions (in sun) * @example 2000 */ fast: number; } ``` ### TronGasfreeAssetInfo Asset metadata returned by the GasFree account service. ```typescript interface TronGasfreeAssetInfo { /** * Token smart contract address * @example 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t' */ tokenAddress: string; /** * Token symbol * @example 'USDT' */ tokenSymbol: string; /** * Fee to activate the GasFree account for this token */ activateFee: number; /** * Fee for transferring this token */ transferFee: number; /** * Token decimals */ decimal: number; /** * Whether the token is frozen by the GasFree service */ frozen: number; } ``` ### TronGasfreeAccountInfo Account metadata returned by the GasFree account service. ```typescript interface TronGasfreeAccountInfo { /** * Owner account address */ accountAddress: string; /** * GasFree contract address for the account */ gasFreeAddress: string; /** * Whether the GasFree account is active */ active: boolean; /** * Account nonce used by GasFree transfers */ nonce: number; /** * Whether the account can submit GasFree transactions */ allowSubmit: boolean; /** * Supported assets and their fee metadata */ assets: TronGasfreeAssetInfo[]; } ``` ### TronGasfreeWalletConfig Configuration options for wallet initialization. ```typescript interface TronGasfreeWalletConfig { /** * The blockchain's ID * @example 728126428 // Tron Mainnet */ chainId: number; /** * Tron RPC endpoint URL or TronWeb instance * @example 'https://api.trongrid.io' */ provider: string | TronWeb; /** * Gas-free service endpoint * @example 'https://gasfree.trongrid.io' */ gasFreeProvider: string; /** * API key for signed GasFree provider requests * @optional */ gasFreeApiKey?: string; /** * API secret for signed GasFree provider requests. * Provide together with gasFreeApiKey. * @optional */ gasFreeApiSecret?: string; /** * Service provider Tron address * @example 'T...' */ serviceProvider: string; /** * Gas-free verifying contract address * @example 'T...' */ verifyingContract: string; /** * Maximum fee for transfer operations (in token base units) * @optional * @example 10000000 */ transferMaxFee?: number | bigint; } ``` ### KeyPair Account key pair information. ```typescript interface KeyPair { /** * Public key as buffer */ publicKey: Buffer; /** * Private key as buffer (sensitive data) */ privateKey: Buffer; } ``` The returned byte arrays are a read-only view of the wallet account's internal key material. Do not mutate `publicKey` or `privateKey`; external changes can alter the internal representation. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Tron Gasfree Wallet Usage Get started with WDK's Tron Gasfree Wallet Configuration *** ## Need Help? *** ## Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-tron-gasfree ## Network & Service Providers | Service | Provider | URL (Mainnet) | URL (Testnet) | | :--- | :--- | :--- | :--- | | **RPC Provider** | TronGrid | `https://api.trongrid.io` | `https://nile.trongrid.io` | | **Gas-Free Service** | GasFree.io | `https://open.gasfree.io/tron/` | `https://open-test.gasfree.io/nile/` | > **Note:** For the latest connection parameters and contract addresses, please refer to the official [GasFree Specification](https://gasfree.io/specification?lang=en-US). ## Wallet Configuration ```javascript import WalletManagerTronGasfree from '@tetherto/wdk-wallet-tron-gasfree' import TronWeb from 'tronweb' // Option 1: Using RPC URL const config = { // Required parameters chainId: 728126428, // Blockchain ID provider: 'https://api.trongrid.io', // Tron RPC endpoint gasFreeProvider: 'https://open.gasfree.io/tron/', // Gas-free service URL serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', // Service provider address verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', // Verifying contract address // Optional parameters gasFreeApiKey: 'your-api-key', // Required only for signed GasFree API requests gasFreeApiSecret: 'your-api-secret', // Provide together with gasFreeApiKey transferMaxFee: 10000000 // Maximum fee in token base units } const wallet = new WalletManagerTronGasfree(seedPhrase, config) // Option 2: Using TronWeb instance const tronWeb = new TronWeb({ fullHost: 'https://api.trongrid.io' }) const config2 = { chainId: 728126428, provider: tronWeb, gasFreeProvider: 'https://open.gasfree.io/tron/', serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', gasFreeApiKey: 'your-api-key', gasFreeApiSecret: 'your-api-secret' } ``` `gasFreeApiKey` and `gasFreeApiSecret` are optional. Omit both when your GasFree provider accepts unsigned requests. If you configure signed GasFree API requests, provide both values together; the constructor rejects partial credentials. ## Account Configuration Both `WalletAccountTronGasfree` and `WalletAccountReadOnlyTronGasfree` share similar configuration requirements: ```javascript import { WalletAccountTronGasfree, WalletAccountReadOnlyTronGasfree } from '@tetherto/wdk-wallet-tron-gasfree' // Full access account const account = new WalletAccountTronGasfree( seedPhrase, "0'/0/0", // BIP-44 derivation path { chainId: 728126428, provider: 'https://api.trongrid.io', gasFreeProvider: 'https://open.gasfree.io/tron/', serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret gasFreeApiSecret: 'your-api-secret', transferMaxFee: 10000000 // Optional } ) // Read-only account (transferMaxFee not needed) const readOnlyAccount = new WalletAccountReadOnlyTronGasfree( 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', // Tron address { chainId: 728126428, provider: 'https://api.trongrid.io', gasFreeProvider: 'https://open.gasfree.io/tron/', serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret gasFreeApiSecret: 'your-api-secret' } ) ``` ## Configuration Options ### Provider The `provider` option specifies how to connect to the Tron network. **Type:** `string | TronWeb` **Required:** Yes **Examples:** ```javascript // Option 1: Using RPC URL const config = { provider: 'https://api.trongrid.io' } // Option 2: Using TronWeb instance const tronWeb = new TronWeb({ fullHost: 'https://api.trongrid.io' }) const config = { provider: tronWeb } ``` ### Chain ID The `chainId` option specifies the blockchain's ID. **Type:** `number` **Required:** Yes **Example:** ```javascript const config = { chainId: 728126428 // Tron Mainnet } ``` ### Gas-Free Provider The `gasFreeProvider` option specifies the URL of the gas-free service. **Type:** `string` **Required:** Yes **Example:** ```javascript const config = { gasFreeProvider: 'https://open.gasfree.io/tron/' } ``` ### Gas-Free API Key The `gasFreeApiKey` option is your API key for signed requests to the gas-free service. **Type:** `string` **Required:** No, unless your GasFree provider requires signed API requests. **Example:** ```javascript const config = { gasFreeApiKey: 'your-api-key', gasFreeApiSecret: 'your-api-secret' } ``` Provide `gasFreeApiKey` and `gasFreeApiSecret` together. Passing only one of them throws during account construction. ### Gas-Free API Secret The `gasFreeApiSecret` option is your API secret for signed requests to the gas-free service. **Type:** `string` **Required:** No, unless your GasFree provider requires signed API requests. **Example:** ```javascript const config = { gasFreeApiKey: 'your-api-key', gasFreeApiSecret: 'your-api-secret' } ``` Provide `gasFreeApiSecret` and `gasFreeApiKey` together. Passing only one of them throws during account construction. ### Service Provider The `serviceProvider` option is the Tron address of the gas-free service provider. **Type:** `string` **Required:** Yes **Example:** ```javascript const config = { serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH' } ``` ### Verifying Contract The `verifyingContract` option is the Tron address of the contract that verifies gas-free transactions. **Type:** `string` **Required:** Yes **Example:** ```javascript const config = { verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH' } ``` ### Transfer Max Fee The `transferMaxFee` option sets a maximum limit for transaction fees to prevent unexpectedly high costs. **Type:** `number | bigint` **Required:** No (optional) **Unit:** Token base units **Example:** ```javascript const config = { transferMaxFee: 10000000 // Maximum fee in token base units } // Usage with error handling try { const result = await account.transfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 }, { transferMaxFee: 5000 // Override default max fee }) } catch (error) { if (error.message.includes('exceeds the transfer max fee')) { console.error('Transfer cancelled: Fee too high') } } ``` ## Network-Specific Configurations ### Tron Mainnet ```javascript const mainnetConfig = { chainId: 728126428, provider: 'https://api.trongrid.io', gasFreeProvider: 'https://open.gasfree.io/tron/', serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', verifyingContract: 'TFFAMLQZybALab4uxHA9RBE7pxhUAjfF3U', // Official Mainnet Contract gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret gasFreeApiSecret: 'your-api-secret' } ``` ### Tron Nile Testnet ```javascript const nileConfig = { chainId: 3448148188, // Nile Testnet (Specific ID required for GasFree) provider: 'https://nile.trongrid.io', gasFreeProvider: 'https://open-test.gasfree.io/nile/', serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', verifyingContract: 'THQGuFzL87ZqhxkgqYEryRAd7gqFqL5rdc', // Official Testnet Contract gasFreeApiKey: 'your-testnet-api-key', // Optional: provide with gasFreeApiSecret gasFreeApiSecret: 'your-testnet-api-secret' } ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Tron Gasfree Wallet Usage Get started with WDK's Tron Gasfree Wallet API *** ## Need Help? *** ## Need Help? *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/guides/check-balances Description: Query native TRX and TRC20 token balances on gas-free Tron wallets. This guide explains how to check [native TRX balances](#native-trx-balance), [TRC20 token balances](#trc20-token-balance), and [read-only account balances](#read-only-account-balances). ## Native TRX Balance You can retrieve the native TRX balance using [`account.getBalance()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference): ```javascript title="Get Native TRX Balance" const balance = await account.getBalance() console.log('Native TRX balance:', balance, 'sun') ``` On Tron, values are expressed in sun (1 TRX = 1,000,000 sun). ## TRC20 Token Balance You can check the balance of a specific TRC20 token using [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#gettokenbalancetokenaddress): ```javascript title="Get TRC20 Token Balance" const trc20Address = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t' // USDT const trc20Balance = await account.getTokenBalance(trc20Address) console.log('TRC20 token balance:', trc20Balance) ``` ## Read-Only Account Balances You can check balances for any Tron address without a seed phrase using [`WalletAccountReadOnlyTronGasfree`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference): ```javascript title="Create Read-Only Account" import { WalletAccountReadOnlyTronGasfree } from '@tetherto/wdk-wallet-tron-gasfree' const readOnlyAccount = new WalletAccountReadOnlyTronGasfree('TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', { chainId: 728126428, provider: 'https://api.trongrid.io', gasFreeProvider: 'https://gasfree.provider.url', serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret gasFreeApiSecret: 'your-api-secret' }) ``` You can retrieve the native balance from a read-only account using [`readOnlyAccount.getBalance()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference): ```javascript title="Read-Only Native Balance" const balance = await readOnlyAccount.getBalance() console.log('Read-only account balance:', balance) ``` You can also create a read-only account from an existing owned account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference). ## Next Steps With balance checks in place, learn how to [send TRX](/sdk/wallet-modules/wallet-tron-gasfree/guides/send-transactions). *** ## Get Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/guides/get-started Description: Install and create your first gas-free Tron wallet. This guide explains how to [install the package](#1-install-the-package), [create a gas-free wallet](#2-create-a-gas-free-wallet), and [get your first account](#3-get-your-first-account). ## 1. Install the Package ### Prerequisites * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ```bash title="Install @tetherto/wdk-wallet-tron-gasfree" npm install @tetherto/wdk-wallet-tron-gasfree ``` ## 2. Create a Gas-Free Wallet You can create a new gas-free wallet instance using the [`WalletManagerTronGasfree`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference) constructor with a BIP-39 seed phrase, Tron RPC provider, and gas-free service configuration: ```javascript title="Create Gas-Free Tron Wallet" import WalletManagerTronGasfree, { WalletAccountTronGasfree, WalletAccountReadOnlyTronGasfree } from '@tetherto/wdk-wallet-tron-gasfree' const seedPhrase = 'your twelve word seed phrase here' const wallet = new WalletManagerTronGasfree(seedPhrase, { chainId: 728126428, provider: 'https://api.trongrid.io', gasFreeProvider: 'https://gasfree.provider.url', serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', gasFreeApiKey: 'your-gasfree-api-key', // Optional: provide with gasFreeApiSecret gasFreeApiSecret: 'your-gasfree-api-secret', transferMaxFee: 10000000 // Optional: maximum fee in token base units }) ``` If your GasFree provider does not require signed API requests, omit both `gasFreeApiKey` and `gasFreeApiSecret`. If you provide one, provide both. **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. ## 3. Get Your First Account You can retrieve an account at a given index using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#getaccountindex): ```javascript title="Get Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Gas-free account address:', address) ``` ## 4. (optional) Convert to Read-Only You can convert an owned account to a read-only account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference): ```javascript title="Convert to Read-Only" const readOnlyAccount = await account.toReadOnlyAccount() ``` All Tron addresses start with `T` and are 34 characters long. ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-tron-gasfree/guides/manage-accounts). *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/guides/handle-errors Description: Handle errors, manage fees, and dispose of sensitive data in gas-free Tron wallets. This guide covers how to [handle gas-free transfer errors](#handle-gas-free-transfer-errors) and [unsupported native transaction methods](#handle-unsupported-native-transaction-methods), plus [best practices](#best-practices) for fee management and memory cleanup. ## Handle Gas-Free Transfer Errors Gas-free transfers can fail for reasons including exceeded fee limits or insufficient token balances. Wrap calls to [`account.transfer()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference) in `try/catch` blocks: ```javascript title="Gas-Free Transfer Error Handling" try { const result = await account.transfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 }) console.log('Transfer successful:', result.hash) console.log('Fee paid:', result.fee, 'token units') } catch (error) { console.error('Transfer failed:', error.message) if (error.message.includes('exceeds the transfer max fee')) { console.log('Transfer cancelled: fee too high') } else if (error.message.toLowerCase().includes('insufficient')) { console.log('Please add more TRC20 tokens to your wallet') } } ``` ## Handle Unsupported Native Transaction Methods The Tron GasFree module does not support native TRX transaction execution, native fee quotes, or offline transaction signing. The related methods are present for wallet-interface compatibility and throw module-specific errors: ```javascript title="Unsupported Native Transaction Methods" try { await account.sendTransaction({ to: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', value: 1000000 }) } catch (error) { if (error.message.includes("Method 'sendTransaction(tx)' not supported")) { console.error('Use the base Tron wallet module for native TRX transactions.') } } ``` ## Best Practices ### Manage Fee Limits Set `transferMaxFee` when creating the wallet or per-transfer to prevent gas-free transfers from exceeding a maximum cost. You can retrieve current network rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference): ```javascript title="Fee Management" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'sun') console.log('Fast fee rate:', feeRates.fast, 'sun') ``` ### Dispose of Sensitive Data Call [`dispose()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference) on accounts and wallet managers to clear private keys and sensitive data from memory when they are no longer needed: ```javascript title="Memory Cleanup" account.dispose() wallet.dispose() ``` Always call [`dispose()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference) in a `finally` block or cleanup handler to ensure sensitive data is cleared even if an error occurs. *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/guides/manage-accounts Description: Work with multiple gas-free Tron accounts and custom derivation paths. This guide explains how to [retrieve accounts by index](#retrieve-accounts-by-index), [use custom derivation paths](#retrieve-account-by-custom-derivation-path), and [iterate over multiple accounts](#iterate-over-multiple-accounts). ## Retrieve Accounts by Index You can access accounts derived from the default BIP-44 path (`m/44'/195'`) using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#getaccountindex): ```javascript title="Get Accounts by Index" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Account 0 address:', address) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` ## Retrieve Account by Custom Derivation Path You can request an account at a specific BIP-44 derivation path using [`wallet.getAccountByPath()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#getaccountbypathpath): ```javascript title="Custom Derivation Path" const customAccount = await wallet.getAccountByPath("0'/0/5") const customAddress = await customAccount.getAddress() console.log('Custom account address:', customAddress) ``` ## Iterate Over Multiple Accounts You can iterate through multiple accounts using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#getaccountindex) to inspect addresses and balances in bulk: ```javascript title="Multi-Account Iteration" async function listAccounts(wallet) { const accounts = [] for (let i = 0; i < 5; i++) { const account = await wallet.getAccount(i) const address = await account.getAddress() const balance = await account.getBalance() accounts.push({ index: i, address, balance }) console.log(`Account ${i}:`, address) } return accounts } ``` ## Next Steps Now that you can access your accounts, learn how to [check balances](/sdk/wallet-modules/wallet-tron-gasfree/guides/check-balances). *** ## Native TRX Transactions URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/guides/send-transactions Description: Understand native TRX transaction limitations in the gas-free Tron wallet module. The Tron GasFree wallet module is for gas-free TRC20 token transfers. It does not support native TRX transaction execution or native transaction fee quotes. For gas-free TRC20 transfers, use [`transfer(options)`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#transferoptions) and [`quoteTransfer(options)`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#quotetransferoptions). If you need native TRX transactions, use the base `@tetherto/wdk-wallet-tron` module instead. ## Unsupported Methods The following native transaction methods exist on the public surface for wallet-interface compatibility, but they throw on Tron GasFree accounts: - [`account.sendTransaction(tx)`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#sendtransactiontx) throws `"Method 'sendTransaction(tx)' not supported on tron gasfree."` - [`account.quoteSendTransaction(tx)`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#quotesendtransactiontx) throws `"Method 'quoteSendTransaction(tx)' not supported on tron gasfree."` - [`account.signTransaction(tx)`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#signtransactiontx) throws `"Method 'signTransaction(tx)' not supported on tron gasfree."` ## Use Gas-Free Token Transfers Instead Use `quoteTransfer()` before executing a gas-free TRC20 transfer: ```javascript title="Quote Gas-Free TRC20 Transfer" const quote = await account.quoteTransfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 }) console.log('Gas-free transfer fee estimate:', quote.fee, 'token units') ``` The fee estimate includes the token transfer fee and, when the GasFree account is inactive, the token activation fee returned by the provider. ## Next Steps To transfer TRC20 tokens with gas-free fees, see [Transfer TRC20 Tokens](/sdk/wallet-modules/wallet-tron-gasfree/guides/transfer-tokens). *** ## Sign and Verify Messages URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/guides/sign-verify-messages Description: Sign messages and verify signatures with gas-free Tron accounts. This guide explains how to [sign messages](#sign-a-message) with an owned account and [verify signatures](#verify-a-signature). ## Sign a Message You can produce a cryptographic signature for any string message using [`account.sign()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#signmessage): ```javascript title="Sign a Message" const message = 'Hello, Tron!' const signature = await account.sign(message) console.log('Signature:', signature) ``` ## Verify a Signature You can verify that a signature is valid using [`account.verify()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#verifymessage-signature): ```javascript title="Verify a Signature" const isValid = await account.verify(message, signature) console.log('Signature valid:', isValid) ``` You can also create a [`WalletAccountReadOnlyTronGasfree`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference) from any Tron address to verify signatures without access to the private key. ## Transaction Signing `signTransaction(tx)` exists for wallet-interface compatibility, but the Tron GasFree module does not support offline transaction signing. Calling it throws `"Method 'signTransaction(tx)' not supported on tron gasfree."`. ## Next Steps For best practices on handling errors, managing fees, and cleaning up memory, see [Handle Errors](/sdk/wallet-modules/wallet-tron-gasfree/guides/handle-errors). *** ## Transfer TRC20 Tokens URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/guides/transfer-tokens Description: Transfer TRC20 tokens gas-free and estimate transfer fees. This guide explains how to [transfer TRC20 tokens gas-free](#transfer-tokens-gas-free), [override the fee limit](#override-fee-limit), [estimate transfer fees](#estimate-transfer-fees), and [validate inputs before executing](#transfer-with-validation). ## Transfer Tokens (Gas-Free) You can send TRC20 tokens gas-free using [`account.transfer()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference). The gas-free service handles the fee payment: ```javascript title="Gas-Free TRC20 Transfer" const transferResult = await account.transfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 // Amount in TRC20's base units }) console.log('Transfer hash:', transferResult.hash) console.log('Transfer fee:', transferResult.fee, 'token units') ``` The returned `fee` includes the GasFree transfer fee and, when the GasFree account is not active yet, the token activation fee returned by the provider. ## Override Fee Limit You can set a maximum fee for a specific transfer by passing a second configuration object specifying a `transferMaxFee` to [`account.transfer()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference): ```javascript title="Transfer with Fee Limit" const result = await account.transfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 }, { transferMaxFee: 1000 // Maximum fee allowed in token units }) console.log('Transfer hash:', result.hash) console.log('Transfer fee:', result.fee, 'token units') ``` ## Estimate Transfer Fees You can get a fee estimate before executing the transfer using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#quotetransferoptions): ```javascript title="Quote Gas-Free Transfer" const transferQuote = await account.quoteTransfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 }) console.log('Transfer fee estimate:', transferQuote.fee, 'token units') ``` If the GasFree account is inactive, the quote includes the token's activation fee in addition to the transfer fee. ## Transfer with Validation Validate addresses and check balances before transferring: 1. Use [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#gettokenbalancetokenaddress) to verify sufficient funds. 2. Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#quotetransferoptions) to confirm fees. 3. Execute the transfer with [`account.transfer()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference): ```javascript title="Validated Gas-Free Transfer" async function transferWithValidation(account, tokenAddress, recipient, amount) { if (!tokenAddress.startsWith('T') || tokenAddress.length !== 34) { throw new Error('Invalid TRC20 contract address') } if (!recipient.startsWith('T') || recipient.length !== 34) { throw new Error('Invalid recipient address') } const balance = await account.getTokenBalance(tokenAddress) if (balance < amount) { throw new Error('Insufficient TRC20 token balance') } const quote = await account.quoteTransfer({ token: tokenAddress, recipient, amount }) console.log('Transfer fee estimate:', quote.fee, 'token units') const result = await account.transfer({ token: tokenAddress, recipient, amount }) console.log('Transfer completed:', result.hash) console.log('Fee paid:', result.fee, 'token units') return result } ``` ## Next Steps Learn how to [sign and verify messages](/sdk/wallet-modules/wallet-tron-gasfree/guides/sign-verify-messages) with your gas-free Tron account. *** ## Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron-gasfree/usage Description: Guide to using the @tetherto/wdk-wallet-tron-gasfree module. The `@tetherto/wdk-wallet-tron-gasfree` module provides wallet management for the Tron blockchain with gas-free transaction support, where TRC20 transfer fees are handled by the gas-free service. Install the package and create your first gas-free wallet. Work with multiple accounts and custom derivation paths. Query native TRX and TRC20 token balances. Understand unsupported native TRX transaction methods. Transfer TRC20 tokens gas-free. Sign messages and verify signatures. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Tron Gasfree Wallet Configuration Get started with WDK's Tron Gasfree Wallet API --- ## Need Help? *** ## API Reference URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/api-reference Description: Complete API documentation for @tetherto/wdk-wallet-tron ## Table of Contents | Class | Description | Methods | |-------|-------------|---------| | [WalletManagerTron](#walletmanagertron) | Main class for managing Tron wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. | [Constructor](#constructor), [Methods](#methods) | | [WalletAccountTron](#walletaccounttron) | Individual Tron wallet account implementation. Extends `WalletAccountReadOnlyTron` and implements `IWalletAccount`. | [Constructor](#constructor-1), [Methods](#methods-1), [Properties](#properties) | | [WalletAccountReadOnlyTron](#walletaccountreadonlytron) | Read-only Tron wallet account. Extends `WalletAccountReadOnly` from `@tetherto/wdk-wallet`. | [Constructor](#constructor-2), [Methods](#methods-2) | ## WalletManagerTron The main class for managing Tron wallets. Extends `WalletManager` from `@tetherto/wdk-wallet`. ### Fee Rate Constants ```javascript const FEE_RATE_NORMAL_MULTIPLIER = 1.1 const FEE_RATE_FAST_MULTIPLIER = 2.0 ``` ### Constructor ```javascript new WalletManagerTron(seed, config?) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `config` (TronWalletConfig, optional): Configuration object - `provider` (string | TronWeb, optional): Tron RPC endpoint URL or TronWeb instance - `transferMaxFee` (number, optional): Maximum fee amount for transfer operations (in sun) **Example:** ```javascript const wallet = new WalletManagerTron(seedPhrase, { provider: 'https://api.trongrid.io', // Tron RPC endpoint transferMaxFee: 10000000 // 10 TRX in sun }) // Or with TronWeb instance const tronWeb = new TronWeb({ fullHost: 'https://api.trongrid.io' }) const wallet2 = new WalletManagerTron(seedPhrase, { provider: tronWeb, transferMaxFee: 10000000 }) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getAccount(index?)` | Returns a wallet account at the specified index | `Promise\` | - | | `getAccountByPath(path)` | Returns a wallet account at the specified BIP-44 derivation path | `Promise\` | - | | `getFeeRates()` | Returns current fee rates from Tron network | `Promise\<{normal: number, fast: number}\>` | If no provider | | `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` | - | ##### `getAccount(index?)` Returns a wallet account at the specified index using Tron's BIP-44 derivation (m/44'/195'). **Parameters:** - `index` (number, optional): The index of the account to get (default: 0) **Returns:** `Promise\` - The wallet account **Example:** ```javascript // Get first account (m/44'/195'/0'/0/0) const account = await wallet.getAccount(0) // Get second account (m/44'/195'/0'/0/1) const account1 = await wallet.getAccount(1) ``` ##### `getAccountByPath(path)` Returns a wallet account at the specified BIP-44 derivation path. **Parameters:** - `path` (string): The derivation path (e.g., "0'/0/0") **Returns:** `Promise\` - The wallet account **Example:** ```javascript // Full path: m/44'/195'/0'/0/1 const account = await wallet.getAccountByPath("0'/0/1") ``` ##### `getFeeRates()` Returns current fee rates from Tron network chain parameters. **Returns:** `Promise\<{normal: number, fast: number}\>` - Fee rates in sun - `normal`: Base fee × 1.1 - `fast`: Base fee × 2.0 **Throws:** Error if no TronWeb provider is configured **Example:** ```javascript const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'sun') console.log('Fast fee rate:', feeRates.fast, 'sun') ``` ##### `dispose()` Disposes all wallet accounts, clearing private keys from memory. **Example:** ```javascript wallet.dispose() ``` ## WalletAccountTron Represents an individual Tron wallet account. Extends `WalletAccountReadOnlyTron` and implements `IWalletAccount`. ### Constants ```javascript const BIP_44_TRON_DERIVATION_PATH_PREFIX = "m/44'/195'" const BANDWIDTH_PRICE = 1_000 ``` ### Constructor ```javascript new WalletAccountTron(seed, path, config?) ``` **Parameters:** - `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes - `path` (string): BIP-44 derivation path (e.g., "0'/0/0") - `config` (TronWalletConfig, optional): Configuration object **Throws:** Error if seed phrase is invalid (BIP-39 validation fails) **Example:** ```javascript const account = new WalletAccountTron(seedPhrase, "0'/0/0", { provider: 'https://api.trongrid.io', transferMaxFee: 10000000 // 10 TRX in sun }) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getAddress()` | Returns the account's Tron address | `Promise\` | - | | `sign(message)` | Signs a message using the account's private key | `Promise\` | - | | `verify(message, signature)` | Verifies a message signature | `Promise\` | - | | `sendTransaction(tx)` | Sends a Tron transaction | `Promise\<{hash: string, fee: number}\>` | If no provider or fee exceeds max | | `quoteSendTransaction(tx)` | Estimates the fee for a Tron transaction | `Promise\<{fee: number}\>` | If no provider | | `transfer(options)` | Transfers TRC20 tokens to another address | `Promise\<{hash: string, fee: number}\>` | If no provider or fee exceeds max | | `quoteTransfer(options)` | Estimates the fee for a TRC20 transfer | `Promise\<{fee: number}\>` | If no provider | | `getBalance()` | Returns the native TRX balance (in sun) | `Promise\` | If no provider | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific TRC20 token | `Promise\` | If no provider | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise\` | If no provider | | `toReadOnlyAccount()` | Returns a read-only copy of the account | `Promise\` | - | | `dispose()` | Disposes the wallet account, clearing private keys from memory | `void` | - | ##### `getAddress()` Returns the account's Tron address (starts with 'T'). **Returns:** `Promise\` - The account's Tron address **Example:** ```javascript const address = await account.getAddress() console.log('Account address:', address) // T... ``` ##### `sign(message)` Signs a message using Keccak-256 hash and secp256k1 signature. **Parameters:** - `message` (string): The message to sign (UTF-8 encoded) **Returns:** `Promise\` - The message signature (hex string) **Example:** ```javascript const message = 'Hello, Tron!' const signature = await account.sign(message) console.log('Signature:', signature) ``` ##### `verify(message, signature)` Verifies a message signature using secp256k1. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify (hex string) **Returns:** `Promise\` - True if signature is valid **Example:** ```javascript const isValid = await account.verify('Hello, Tron!', signature) console.log('Signature valid:', isValid) ``` ##### `sendTransaction(tx)` Sends a TRX transaction and returns the result with hash and fee. **Parameters:** - `tx` (TronTransaction): The transaction object - `to` (string): Recipient Tron address (e.g., 'T...') - `value` (number): Amount in sun (1 TRX = 1,000,000 sun) **Returns:** `Promise\<{hash: string, fee: number}\>` - Transaction hash and fee in sun **Throws:** - Error if no TronWeb provider is configured - Error if fee exceeds `transferMaxFee` **Example:** ```javascript const result = await account.sendTransaction({ to: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', // Tron address value: 1000000 // 1 TRX in sun }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee, 'sun') ``` ##### `quoteSendTransaction(tx)` Estimates the bandwidth cost for a TRX transaction. **Parameters:** - `tx` (TronTransaction): The transaction object (same as sendTransaction) **Returns:** `Promise\<{fee: number}\>` - Fee estimate in sun **Throws:** Error if no TronWeb provider is configured **Example:** ```javascript const quote = await account.quoteSendTransaction({ to: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', value: 1000000 }) console.log('Estimated fee:', quote.fee, 'sun') ``` ##### `transfer(options)` Transfers TRC20 tokens using smart contract call. **Parameters:** - `options` (TransferOptions): Transfer options - `token` (string): TRC20 contract address (e.g., 'T...') - `recipient` (string): Recipient Tron address (e.g., 'T...') - `amount` (number | bigint): Amount in token's base units **Returns:** `Promise\<{hash: string, fee: number}\>` - Transaction hash and fee in sun **Throws:** - Error if no TronWeb provider is configured - Error if fee exceeds `transferMaxFee` **Example:** ```javascript const result = await account.transfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT TRC20 recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 // 1 USDT (6 decimals) }) console.log('Transfer hash:', result.hash) console.log('Transfer fee:', result.fee, 'sun') ``` ##### `quoteTransfer(options)` Estimates the energy and bandwidth cost for a TRC20 token transfer. **Parameters:** - `options` (TransferOptions): Transfer options (same as transfer) **Returns:** `Promise\<{fee: number}\>` - Fee estimate in sun (energy + bandwidth costs) **Throws:** Error if no TronWeb provider is configured **Example:** ```javascript const quote = await account.quoteTransfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 }) console.log('Transfer fee estimate:', quote.fee, 'sun') ``` ##### `getBalance()` Returns the native TRX balance. **Returns:** `Promise\` - Balance in sun **Throws:** Error if no TronWeb provider is configured **Example:** ```javascript const balance = await account.getBalance() console.log('TRX balance:', balance, 'sun') ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific TRC20 token. **Parameters:** - `tokenAddress` (string): The TRC20 contract address (e.g., 'T...') **Returns:** `Promise\` - Token balance in base units **Throws:** Error if no TronWeb provider is configured **Example:** ```javascript const tokenBalance = await account.getTokenBalance('TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t') console.log('USDT balance:', tokenBalance) // In 6 decimal units ``` ##### `getTransactionReceipt(hash)` Returns a transaction's receipt if it has been processed. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise\` - Transaction receipt or null **Throws:** Error if no TronWeb provider is configured **Example:** ```javascript const receipt = await account.getTransactionReceipt('0x...') console.log('Transaction confirmed:', receipt.success) ``` ##### `toReadOnlyAccount()` Creates a read-only copy of the account. **Returns:** `Promise\` - Read-only account instance **Example:** ```javascript const readOnlyAccount = await account.toReadOnlyAccount() // Can check balances but cannot send transactions const balance = await readOnlyAccount.getBalance() ``` ##### `dispose()` Disposes the wallet account, clearing private keys from memory using sodium_memzero. **Example:** ```javascript account.dispose() ``` ### Properties | Property | Type | Description | |----------|------|-------------| | `index` | `number` | The derivation path's index of this account | | `path` | `string` | The full BIP-44 derivation path of this account | | `keyPair` | `{privateKey: Buffer, publicKey: Buffer}` | The account's key pair (⚠️ Contains sensitive data) | **Example:** ```javascript console.log('Account index:', account.index) // 0, 1, 2, etc. console.log('Account path:', account.path) // m/44'/195'/0'/0/0 // ⚠️ SENSITIVE: Handle with care const { privateKey, publicKey } = account.keyPair console.log('Public key length:', publicKey.length) // 33 bytes (compressed) console.log('Private key length:', privateKey.length) // 32 bytes ``` ⚠️ **Security Note**: The `keyPair` property contains sensitive cryptographic material. Never log, display, or expose the private key. ## WalletAccountReadOnlyTron Represents a read-only Tron wallet account that can query balances and estimate fees but cannot send transactions. ### Constructor ```javascript new WalletAccountReadOnlyTron(address, config?) ``` **Parameters:** - `address` (string): The account's Tron address - `config` (`Omit`, optional): Configuration object without transferMaxFee **Example:** ```javascript const readOnlyAccount = new WalletAccountReadOnlyTron('TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', { provider: 'https://api.trongrid.io' }) ``` ### Methods | Method | Description | Returns | Throws | |--------|-------------|---------|--------| | `getBalance()` | Returns the native TRX balance (in sun) | `Promise\` | If no provider | | `getTokenBalance(tokenAddress)` | Returns the balance of a specific TRC20 token | `Promise\` | If no provider | | `quoteSendTransaction(tx)` | Estimates the fee for a TRX transaction | `Promise\<{fee: number}\>` | If no provider | | `quoteTransfer(options)` | Estimates the fee for a TRC20 transfer | `Promise\<{fee: number}\>` | If no provider | | `verify(message, signature)` | Verifies a message signature | `Promise\` | - | | `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise\` | If no provider | ##### `getBalance()` Returns the native TRX balance. **Returns:** `Promise\` - Balance in sun **Throws:** Error if no TronWeb provider is configured **Example:** ```javascript const balance = await readOnlyAccount.getBalance() console.log('TRX balance:', balance, 'sun') ``` ##### `getTokenBalance(tokenAddress)` Returns the balance of a specific TRC20 token. **Parameters:** - `tokenAddress` (string): The TRC20 contract address **Returns:** `Promise\` - Token balance in base units **Throws:** Error if no TronWeb provider is configured **Example:** ```javascript const tokenBalance = await readOnlyAccount.getTokenBalance('TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t') console.log('USDT balance:', tokenBalance) ``` ##### `quoteSendTransaction(tx)` Estimates the bandwidth cost for a TRX transaction. **Parameters:** - `tx` (TronTransaction): The transaction object **Returns:** `Promise\<{fee: number}\>` - Fee estimate in sun **Throws:** Error if no TronWeb provider is configured ##### `quoteTransfer(options)` Estimates the energy and bandwidth cost for a TRC20 transfer. **Parameters:** - `options` (TransferOptions): Transfer options **Returns:** `Promise\<{fee: number}\>` - Fee estimate in sun **Throws:** Error if no TronWeb provider is configured ##### `verify(message, signature)` Verifies a message signature using secp256k1. **Parameters:** - `message` (string): The original message - `signature` (string): The signature to verify (hex string) **Returns:** `Promise\` - True if signature is valid **Example:** ```javascript const readOnlyAccount = new WalletAccountReadOnlyTron('T...', { provider: '...' }) const isValid = await readOnlyAccount.verify(message, signature) console.log('Signature valid:', isValid) ``` ##### `getTransactionReceipt(hash)` Returns a transaction's receipt if it has been processed. **Parameters:** - `hash` (string): The transaction hash **Returns:** `Promise\` - Transaction receipt or null **Throws:** Error if no TronWeb provider is configured ## Types ### TronWalletConfig ```typescript interface TronWalletConfig { provider?: string | TronWeb; // Tron RPC URL or TronWeb instance transferMaxFee?: number; // Maximum fee in sun } ``` ### TronTransaction ```typescript interface TronTransaction { to: string; // Recipient Tron address value: number; // Amount in sun (1 TRX = 1,000,000 sun) } ``` ### TransferOptions ```typescript interface TransferOptions { token: string; // TRC20 contract address recipient: string; // Recipient Tron address amount: number | bigint; // Amount in token base units } ``` ### TransactionResult ```typescript interface TransactionResult { hash: string; // Transaction hash fee: number; // Fee paid in sun } ``` ### TransferResult ```typescript interface TransferResult { hash: string; // Transaction hash fee: number; // Fee paid in sun } ``` ### Constants ```typescript // Tron-specific constants const BIP_44_TRON_DERIVATION_PATH_PREFIX: string = "m/44'/195'"; const BANDWIDTH_PRICE: number = 1_000; // Fee rate multipliers const FEE_RATE_NORMAL_MULTIPLIER: number = 1.1; const FEE_RATE_FAST_MULTIPLIER: number = 2.0; ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Tron Wallet Usage Get started with WDK's Tron Wallet Configuration *** ## Need Help? *** ## Configuration URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/configuration Description: Configuration options and settings for @tetherto/wdk-wallet-tron ## Wallet Configuration ```javascript import WalletManagerTron from '@tetherto/wdk-wallet-tron' const config = { provider: 'https://api.trongrid.io', // Tron RPC endpoint transferMaxFee: 10000000 // Maximum fee in sun (optional) } const wallet = new WalletManagerTron(seedPhrase, config) ``` ## Account Configuration ```javascript import { WalletAccountTron } from '@tetherto/wdk-wallet-tron' const accountConfig = { provider: 'https://api.trongrid.io', transferMaxFee: 10000000 // Maximum fee in sun (optional) } const account = new WalletAccountTron(seedPhrase, "0'/0/0", accountConfig) ``` ## Configuration Options ### Provider The `provider` option specifies the Tron RPC endpoint or TronWeb instance for blockchain interactions. **Type:** `string | TronWeb` **Example:** ```javascript const config = { provider: 'https://api.trongrid.io' } ``` ### Transfer Max Fee The `transferMaxFee` option sets the maximum fee amount (in sun) for transfer operations. This helps prevent transactions from being sent with unexpectedly high fees. **Type:** `number` (optional) **Unit:** Sun (1 TRX = 1,000,000 Sun) **Example:** ```javascript const config = { transferMaxFee: 10000000 // 10 TRX in sun } ``` Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Tron Wallet Usage Get started with WDK's Tron Wallet API *** ## Need Help? *** ## Check Balances URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/guides/check-balances Description: Query native TRX and TRC20 token balances on Tron. This guide explains how to check [native TRX balances](#native-trx-balance), [TRC20 token balances](#trc20-token-balance), and [read-only account balances](#read-only-account-balances). ## Native TRX Balance You can retrieve the native TRX balance using [`account.getBalance()`](/sdk/wallet-modules/wallet-tron/api-reference): ```javascript title="Get Native TRX Balance" const balance = await account.getBalance() console.log('Native TRX balance:', balance, 'sun') ``` On Tron, values are expressed in sun (1 TRX = 1,000,000 sun). ## TRC20 Token Balance You can check the balance of a specific TRC20 token using [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-tron/api-reference#gettokenbalancetokenaddress): ```javascript title="Get TRC20 Token Balance" const trc20Address = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t' // USDT const trc20Balance = await account.getTokenBalance(trc20Address) console.log('TRC20 token balance:', trc20Balance) ``` ## Read-Only Account Balances You can check balances for any Tron address without a seed phrase using [`WalletAccountReadOnlyTron`](/sdk/wallet-modules/wallet-tron/api-reference): ```javascript title="Create Read-Only Account" import { WalletAccountReadOnlyTron } from '@tetherto/wdk-wallet-tron' const readOnlyAccount = new WalletAccountReadOnlyTron('TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', { provider: 'https://api.trongrid.io' }) ``` You can retrieve the native balance from a read-only account using [`readOnlyAccount.getBalance()`](/sdk/wallet-modules/wallet-tron/api-reference): ```javascript title="Read-Only Native Balance" const balance = await readOnlyAccount.getBalance() console.log('Read-only account balance:', balance) ``` You can also create a read-only account from an existing owned account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-tron/api-reference). ## Next Steps With balance checks in place, learn how to [send TRX](/sdk/wallet-modules/wallet-tron/guides/send-transactions). *** ## Get Started URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/guides/get-started Description: Install and create your first Tron wallet. This guide explains how to [install the package](#1-install-the-package), [create a wallet](#2-create-a-wallet), and [get your first account](#3-get-your-first-account). ## 1. Install the Package ### Prerequisites * **[Node.js](https://nodejs.org/)**: version 18 or higher. * **[npm](https://www.npmjs.com/)**: usually comes with Node.js. ```bash title="Install @tetherto/wdk-wallet-tron" npm install @tetherto/wdk-wallet-tron ``` ## 2. Create a Wallet You can create a new wallet instance using the [`WalletManagerTron`](/sdk/wallet-modules/wallet-tron/api-reference) constructor with a BIP-39 seed phrase and a Tron RPC provider: ```javascript title="Create Tron Wallet" import WalletManagerTron, { WalletAccountTron, WalletAccountReadOnlyTron } from '@tetherto/wdk-wallet-tron' const seedPhrase = 'your twelve word seed phrase here' const wallet = new WalletManagerTron(seedPhrase, { provider: 'https://api.trongrid.io' }) ``` **Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds. ## 3. Get Your First Account You can retrieve an account at a given index using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-tron/api-reference#getaccountindex): ```javascript title="Get Account" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Wallet address:', address) ``` ## 4. (optional) Convert to Read-Only You can convert an owned account to a read-only account using [`account.toReadOnlyAccount()`](/sdk/wallet-modules/wallet-tron/api-reference): ```javascript title="Convert to Read-Only" const readOnlyAccount = await account.toReadOnlyAccount() ``` All Tron addresses start with `T` and are 34 characters long. ## Next Steps With your wallet ready, learn how to [manage multiple accounts](/sdk/wallet-modules/wallet-tron/guides/manage-accounts). *** ## Handle Errors URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/guides/handle-errors Description: Handle errors, manage fees, and dispose of sensitive data in Tron wallets. This guide covers how to [handle transaction errors](#handle-transaction-errors) and [handle token transfer errors](#handle-token-transfer-errors), plus [best practices](#best-practices) for fee management and memory cleanup. ## Handle Transaction Errors Wrap transactions in `try/catch` blocks to handle common failure scenarios. Use [`account.sendTransaction()`](/sdk/wallet-modules/wallet-tron/api-reference#sendtransactiontx) with proper error handling: ```javascript title="Transaction Error Handling" try { const result = await account.sendTransaction({ to: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', value: 1000000 // 1 TRX in sun }) console.log('Transaction hash:', result.hash) console.log('Fee paid:', result.fee, 'sun') } catch (error) { if (error.message.toLowerCase().includes('insufficient')) { console.error('Not enough TRX to complete transaction') } else if (error.message.toLowerCase().includes('max fee')) { console.error('The transfer fee exceeds your configured maximum') } else { console.error('Transaction failed:', error.message) } } ``` ## Handle Token Transfer Errors TRC20 transfers can fail for additional reasons such as insufficient token balances. Use [`account.transfer()`](/sdk/wallet-modules/wallet-tron/api-reference#transferoptions) with error handling: ```javascript title="Token Transfer Error Handling" try { const result = await account.transfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 }) console.log('Transfer successful:', result.hash) console.log('Fee paid (sun):', result.fee) } catch (error) { console.error('Transfer failed:', error.message) if (error.message.toLowerCase().includes('insufficient')) { console.log('Please add more TRC20 tokens to your wallet') } else if (error.message.toLowerCase().includes('max fee')) { console.log('The transfer fee exceeds your configured maximum') } } ``` ## Best Practices ### Manage Fee Limits Set `transferMaxFee` when creating the wallet to prevent transactions from exceeding a maximum cost. You can retrieve current network rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-tron/api-reference): ```javascript title="Fee Management" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'sun') console.log('Fast fee rate:', feeRates.fast, 'sun') ``` ### Dispose of Sensitive Data Call [`dispose()`](/sdk/wallet-modules/wallet-tron/api-reference) on accounts and wallet managers to clear private keys and sensitive data from memory when they are no longer needed: ```javascript title="Memory Cleanup" account.dispose() wallet.dispose() ``` Always call [`dispose()`](/sdk/wallet-modules/wallet-tron/api-reference) in a `finally` block or cleanup handler to ensure sensitive data is cleared even if an error occurs. *** ## Manage Accounts URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/guides/manage-accounts Description: Work with multiple Tron accounts and custom derivation paths. This guide explains how to [retrieve accounts by index](#retrieve-accounts-by-index), [use custom derivation paths](#retrieve-account-by-custom-derivation-path), and [iterate over multiple accounts](#iterate-over-multiple-accounts). ## Retrieve Accounts by Index You can access accounts derived from the default BIP-44 path (`m/44'/195'`) using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-tron/api-reference#getaccountindex): ```javascript title="Get Accounts by Index" const account = await wallet.getAccount(0) const address = await account.getAddress() console.log('Account 0 address:', address) const account1 = await wallet.getAccount(1) const address1 = await account1.getAddress() console.log('Account 1 address:', address1) ``` ## Retrieve Account by Custom Derivation Path You can request an account at a specific BIP-44 derivation path using [`wallet.getAccountByPath()`](/sdk/wallet-modules/wallet-tron/api-reference#getaccountbypathpath): ```javascript title="Custom Derivation Path" const customAccount = await wallet.getAccountByPath("0'/0/5") const customAddress = await customAccount.getAddress() console.log('Custom account address:', customAddress) ``` ## Iterate Over Multiple Accounts You can loop through multiple accounts using [`wallet.getAccount()`](/sdk/wallet-modules/wallet-tron/api-reference#getaccountindex) to inspect addresses and balances in bulk: ```javascript title="Multi-Account Iteration" async function listAccounts(wallet) { const accounts = [] for (let i = 0; i < 5; i++) { const account = await wallet.getAccount(i) const address = await account.getAddress() const balance = await account.getBalance() accounts.push({ index: i, address, balance }) } return accounts } ``` ## Next Steps Now that you can access your accounts, learn how to [check balances](/sdk/wallet-modules/wallet-tron/guides/check-balances). *** ## Send TRX URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/guides/send-transactions Description: Send native TRX and estimate transaction fees on Tron. This guide explains how to [send native TRX](#send-native-trx), [estimate transaction fees](#estimate-transaction-fees), and [use dynamic fee rates](#use-dynamic-fee-rates). On Tron, values are expressed in sun (1 TRX = 1,000,000 sun). ## Send Native TRX You can transfer TRX to a recipient address using [`account.sendTransaction()`](/sdk/wallet-modules/wallet-tron/api-reference#sendtransactiontx): ```javascript title="Send TRX" const result = await account.sendTransaction({ to: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', value: 1000000 // 1 TRX in sun }) console.log('Transaction hash:', result.hash) console.log('Transaction fee:', result.fee, 'sun') ``` ## Estimate Transaction Fees You can get a fee estimate before sending using [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-tron/api-reference#quotesendtransactiontx): ```javascript title="Quote Transaction Fee" const quote = await account.quoteSendTransaction({ to: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', value: 1000000 }) console.log('Estimated fee:', quote.fee, 'sun') ``` ## Use Dynamic Fee Rates You can retrieve current fee rates from the wallet manager using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-tron/api-reference): ```javascript title="Get Fee Rates" const feeRates = await wallet.getFeeRates() console.log('Normal fee rate:', feeRates.normal, 'sun') console.log('Fast fee rate:', feeRates.fast, 'sun') ``` ## Next Steps To transfer TRC20 tokens instead of native TRX, see [Transfer TRC20 Tokens](/sdk/wallet-modules/wallet-tron/guides/transfer-tokens). *** ## Sign and Verify Messages URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/guides/sign-verify-messages Description: Sign messages and verify signatures with Tron accounts using secp256k1. This guide explains how to [sign messages](#sign-a-message) with an owned account and [verify signatures](#verify-a-signature) using a read-only account. Tron uses secp256k1 cryptography for signing. ## Sign a Message You can produce a cryptographic signature for any string message using [`account.sign()`](/sdk/wallet-modules/wallet-tron/api-reference#signmessage): ```javascript title="Sign a Message" const message = 'Hello, Tron!' const signature = await account.sign(message) console.log('Signature:', signature) ``` ## Verify a Signature You can verify that a signature was produced by the corresponding private key using [`readOnlyAccount.verify()`](/sdk/wallet-modules/wallet-tron/api-reference#verifymessage-signature): ```javascript title="Verify a Signature" const readOnlyAccount = await account.toReadOnlyAccount() const isValid = await readOnlyAccount.verify(message, signature) console.log('Signature valid:', isValid) ``` You can also create a [`WalletAccountReadOnlyTron`](/sdk/wallet-modules/wallet-tron/api-reference) from any Tron address to verify signatures without access to the private key. ## Next Steps For best practices on handling errors, managing fees, and cleaning up memory, see [Handle Errors](/sdk/wallet-modules/wallet-tron/guides/handle-errors). *** ## Transfer TRC20 Tokens URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/guides/transfer-tokens Description: Transfer TRC20 tokens and estimate transfer fees on Tron. This guide explains how to [transfer TRC20 tokens](#transfer-tokens), [estimate transfer fees](#estimate-transfer-fees), and [validate inputs before executing](#transfer-with-validation). ## Transfer Tokens You can send TRC20 tokens to a recipient address using [`account.transfer()`](/sdk/wallet-modules/wallet-tron/api-reference#transferoptions): ```javascript title="Transfer TRC20 Tokens" const transferResult = await account.transfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 // Amount in TRC20's base units }) console.log('Transfer hash:', transferResult.hash) console.log('Transfer fee:', transferResult.fee, 'sun') ``` ## Estimate Transfer Fees You can get a fee estimate before executing the transfer using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-tron/api-reference#quotetransferoptions): ```javascript title="Quote Token Transfer" const transferQuote = await account.quoteTransfer({ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', amount: 1000000 }) console.log('Transfer fee estimate:', transferQuote.fee, 'sun') ``` ## Transfer with Validation Validate addresses and check balances before transferring to catch errors early: 1. Use [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-tron/api-reference#gettokenbalancetokenaddress) to verify sufficient funds. 2. Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-tron/api-reference#quotetransferoptions) to confirm fees. 3. Execute the transfer with [`account.transfer()`](/sdk/wallet-modules/wallet-tron/api-reference#transferoptions): ```javascript title="Validated TRC20 Transfer" async function transferTRC20WithValidation(account, trc20Address, recipient, amount) { if (!trc20Address.startsWith('T') || trc20Address.length !== 34) { throw new Error('Invalid TRC20 contract address') } if (!recipient.startsWith('T') || recipient.length !== 34) { throw new Error('Invalid recipient address') } const balance = await account.getTokenBalance(trc20Address) if (balance < amount) { throw new Error('Insufficient TRC20 token balance') } const quote = await account.quoteTransfer({ token: trc20Address, recipient, amount }) console.log('Transfer fee estimate (sun):', quote.fee) const result = await account.transfer({ token: trc20Address, recipient, amount }) console.log('Transfer completed:', result.hash) console.log('Fee paid (sun):', result.fee) return result } ``` ## Next Steps Learn how to [sign and verify messages](/sdk/wallet-modules/wallet-tron/guides/sign-verify-messages) with your Tron account. *** ## Usage URL: https://docs.wdk.tether.io/sdk/wallet-modules/wallet-tron/usage Description: Guide to using the @tetherto/wdk-wallet-tron module. The `@tetherto/wdk-wallet-tron` module provides wallet management for the Tron blockchain. Install the package and create your first wallet. Work with multiple accounts and custom derivation paths. Query native TRX and TRC20 token balances. Send native TRX and estimate transaction fees. Transfer TRC20 tokens and estimate fees. Sign messages and verify secp256k1 signatures. Handle errors, manage fees, and dispose of sensitive data. Get started with WDK in a Node.js environment Build mobile wallets with React Native Expo Get started with WDK's Tron Wallet Configuration Get started with WDK's Tron Wallet API --- ## Need Help? *** ## Node.js & Bare Runtime Quickstart URL: https://docs.wdk.tether.io/start-building/nodejs-bare-quickstart Description: Get started with WDK in Node.js or Bare runtime environments in 3 minutes ## What You'll Build In this quickstart, you'll create a simple application that: * [ ] Sets up WDK with multiple blockchain wallets (EVM, Bitcoin, TRON) * [ ] Generates a new secret phrase (seed phrase) * [ ] Resolves addresses across different chains * [ ] Checks balances and estimates transaction costs * [ ] Sends transactions on multiple blockchains *** ## Prerequisites Before we start, make sure you have: | Tool | Version | Why You Need It | | --------------- | ------- | ---------------------- | | **Node.js** | 20+ | To run JavaScript code | | **npm** | Latest | To install packages | | **Code Editor** | Any | To write code | | Tool | Version | Why You Need It | | ---------------- | ------- | ------------------- | | **Bare Runtime** | >= 1.23.5 | To run JavaScript | | **npm** | Latest | To install packages | | **Code Editor** | Any | To write code | To install Bare runtime first include to the `package.json`: ``` "type": "module" ``` and run the command `npm i -g bare` You can try all features without real funds required. You can use the Pimlico or Candide faucets to get some Sepolia USD₮. [Get mock/test USD₮ on Pimlico](https://dashboard.pimlico.io/test-erc20-faucet) [Get mock/test USD₮ on Candide](https://dashboard.candide.dev/faucet) See the [configuration](/sdk/wallet-modules/wallet-evm-erc-4337/configuration) for quick setup and Sepolia testnet configuration. *** ## Step 1: Set Up Your Project First, we need to create a folder and initialize the project ```bash mkdir wdk-quickstart && cd wdk-quickstart && npm init -y && npm pkg set type=module ``` Then install necessary WDK modules ```bash npm install @tetherto/wdk @tetherto/wdk-wallet-evm @tetherto/wdk-wallet-tron @tetherto/wdk-wallet-btc ``` Learn more about WDK modules: * [**@tetherto/wdk**](/sdk/core-module/) - The main SDK module * [**@tetherto/wdk-wallet-evm**](/sdk/wallet-modules/wallet-evm/) - Ethereum and EVM-compatible chains support * [**@tetherto/wdk-wallet-tron**](/sdk/wallet-modules/wallet-tron/) - TRON blockchain support * [**@tetherto/wdk-wallet-btc**](/sdk/wallet-modules/wallet-btc/) - Bitcoin blockchain support *** ## Step 2: Create Your First Wallet Create a file called `app.js`: ```javascript title="app.js" import WDK from '@tetherto/wdk' import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerTron from '@tetherto/wdk-wallet-tron' import WalletManagerBtc from '@tetherto/wdk-wallet-btc' console.log('Starting WDK App...') try { // Your code will go here } catch (error) { console.error('Application error:', error.message) process.exit(1) } ``` Now, add the following code to generate a seed phrase: ```typescript title="app.js" try { const seedPhrase = WDK.getRandomSeedPhrase() console.log('Generated seed phrase:', seedPhrase) } catch (error) { console.error('Application error:', error.message) process.exit(1) } ``` Now, let's register wallets for different blockchains: ```typescript title="app.js" // Add this code after the seed phrase generation console.log('Registering wallets...') const wdkWithWallets = new WDK(seedPhrase) .registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth.drpc.org' }) .registerWallet('tron', WalletManagerTron, { provider: 'https://api.trongrid.io' }) .registerWallet('bitcoin', WalletManagerBtc, { network: 'mainnet', host: 'electrum.blockstream.info', port: 50001 }) console.log('Wallets registered for Ethereum, TRON, and Bitcoin') ``` To learn more about configuring the wallet modules: * [Configuring @tetherto/wdk-wallet-evm](/sdk/wallet-modules/wallet-evm/configuration) * [Configuring @tetherto/wdk-wallet-tron](/sdk/wallet-modules/wallet-tron/configuration) * [Configuring @tetherto/wdk-wallet-btc](/sdk/wallet-modules/wallet-btc/configuration) *** ## Step 3: Check Balances To check balances, we first need to get accounts and addresses. Let's get accounts and addresses for all blockchains: ```typescript title="app.js" // Add this code after the wallet registration console.log('Retrieving accounts...') const accounts = { ethereum: await wdkWithWallets.getAccount('ethereum', 0), tron: await wdkWithWallets.getAccount('tron', 0), bitcoin: await wdkWithWallets.getAccount('bitcoin', 0) } console.log('Resolving addresses:') for (const [chain, account] of Object.entries(accounts)) { const address = await account.getAddress() console.log(` ${chain.toUpperCase()}: ${address}`) } ``` Now, let's check balances across all chains: ```typescript // Add this code after the address resolution console.log('Checking balances...') for (const [chain, account] of Object.entries(accounts)) { const balance = await account.getBalance() console.log(` ${chain.toUpperCase()}: ${balance.toString()} units`) } ``` Here is the complete `app.js` file: ```javascript title="app.js" import WDK from '@tetherto/wdk' import WalletManagerEvm from '@tetherto/wdk-wallet-evm' import WalletManagerTron from '@tetherto/wdk-wallet-tron' import WalletManagerBtc from '@tetherto/wdk-wallet-btc' console.log('Starting WDK App...') try { const seedPhrase = WDK.getRandomSeedPhrase() console.log('Generated seed phrase:', seedPhrase) console.log('Registering wallets...') const wdkWithWallets = new WDK(seedPhrase) .registerWallet('ethereum', WalletManagerEvm, { provider: 'https://eth.drpc.org' }) .registerWallet('tron', WalletManagerTron, { provider: 'https://api.trongrid.io' }) .registerWallet('bitcoin', WalletManagerBtc, { network: 'mainnet', host: 'electrum.blockstream.info', port: 50001 }) console.log('Wallets registered for Ethereum, TRON, and Bitcoin') const accounts = { ethereum: await wdkWithWallets.getAccount('ethereum', 0), tron: await wdkWithWallets.getAccount('tron', 0), bitcoin: await wdkWithWallets.getAccount('bitcoin', 0) } console.log('Resolving addresses:') for (const [chain, account] of Object.entries(accounts)) { const address = await account.getAddress() console.log(` ${chain.toUpperCase()}: ${address}`) } console.log('Checking balances...') for (const [chain, account] of Object.entries(accounts)) { const balance = await account.getBalance() console.log(` ${chain.toUpperCase()}: ${balance.toString()} units`) } console.log('Application completed successfully!') process.exit(0) } catch (error) { console.error('Application error:', error.message) process.exit(1) } ``` *** ## Step 4: Run Your App Execute your app: ```bash node app.js ``` ```bash bare app.js ``` You should see an output similar to this: ``` Starting WDK App... Generated seed phrase: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about Registering wallets... Wallets registered for Ethereum, TRON, and Bitcoin Resolving addresses: ETHEREUM: 0x742d35Cc6634C0532925a3b8D9C5c8b7b6e5f6e5 TRON: TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH BITCOIN: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa Checking balances... ETHEREUM: 0 units TRON: 0 units BITCOIN: 0 units Application completed successfully! ``` *** ## What Just Happened? **Congratulations!** You've successfully created your first multi-chain WDK application that works in both Node.js and Bare runtime environments. Here's what happened: * [x] You generated a single seed phrase that works across all blockchains * [x] You registered wallets for Ethereum, TRON, and Bitcoin * [x] You created accounts derived from the same seed phrase using BIP-44 * [x] You used the same API to interact with different blockchains * [x] You checked balances across multiple chains with consistent methods *** ## Next Steps Now that you have a basic multi-chain wallet running, here's what you can explore: ### Add More Blockchains For example, to add Solana support: ```bash npm install @tetherto/wdk-wallet-solana ``` ```typescript import WalletManagerSolana from '@tetherto/wdk-wallet-solana' // New or existing WDK instance const wdk = new WDK(seedPhrase) wdk.registerWallet('solana', WalletManagerSolana, { rpcUrl: 'https://api.mainnet-beta.solana.com', wsUrl: 'wss://api.mainnet-beta.solana.com' }) ``` ### Estimate Transaction Costs ```typescript for (const [chain, account] of Object.entries(accounts)) { try { const quote = await account.quoteSendTransaction({ to: await account.getAddress(), value: chain === 'bitcoin' ? 100000000n : chain === 'tron' ? 1000000n : 1000000000000000000n }) console.log(` ${chain.toUpperCase()}: ${quote.fee.toString()} units`) } catch (error) { console.log(` ${chain.toUpperCase()}: Unable to estimate`) } } ``` ### **Send Transactions** ```typescript const result = await ethAccount.sendTransaction({ to: '0x742d35Cc6634C05...a3b8D9C5c8b7b6e5f6e5', value: 1000000000000000000n // 1 ETH }) console.log('Transaction hash:', result.hash) ``` ### **Use DeFi Protocols** ```bash npm install @tetherto/wdk-protocol-swap-velora-evm ``` ```typescript import VeloraProtocolEvm from '@tetherto/wdk-protocol-swap-velora-evm' wdk.registerProtocol('ethereum', 'swap-velora-evm', VeloraProtocolEvm, { provider: 'https://eth.drpc.org' }) ``` *** ## Troubleshooting ### **Common Issues** **"Provider not connected"** * Check your API keys and network connections * Ensure you're using the correct provider URLs **"Insufficient balance"** * This is normal for new addresses * Use testnet faucets to get test tokens **"Module not found"** * Make sure you've installed all required packages * Check your import statements ## Need Help? *** ## React Native Quickstart URL: https://docs.wdk.tether.io/start-building/react-native-quickstart Description: Get started with WDK in React Native in under 3 minutes ## What You'll Build In this quickstart, you'll integrate WDK into a React Native app to create a multi-chain wallet that: - [ ] Supports multiple blockchains (Bitcoin, Ethereum, Polygon, Arbitrum, TON, Tron) - [ ] Manages multiple tokens (BTC, USD₮, XAU₮, and more) - [ ] Provides secure seed generation and encrypted storage - [ ] Shows real-time balances and transaction history - [ ] Includes wallet creation, import, and unlock flows You can try all features without real funds required. You can use the Pimlico or Candide faucets to get some Sepolia USD₮. - [Get mock/test USD₮ on Pimlico](https://dashboard.pimlico.io/test-erc20-faucet) - [Get mock/test USD₮ on Candide](https://dashboard.candide.dev/faucet) See the [ERC-4337 configuration](/sdk/wallet-modules/wallet-evm-erc-4337/configuration) for quick setup and Sepolia testnet configuration. ## Prerequisites Before we start, make sure you have: | Tool | Version | Why You Need It | | ---------------- | ------- | --------------------------- | | **Node.js** | 22+ | To run JavaScript code | | **npm** | Latest | To install packages | | **React Native** | 0.81.0+ | Framework version | | **Android SDK** | API 29+ | Android minimum SDK version | | **iOS** | 15.1+ | iOS deployment target | --- ## Quickstart Paths You have 2 options for using WDK in a React Native. Choose your preferred starting point: Get up and running in 3 minutes with our pre-configured starter template. [**→ Jump to Starter Template Setup**](#option-1-starter-template) Integrate WDK into your existing React Native or Expo app. [**→ Jump to Library Integration**](#option-2-add-to-existing-app) --- ## Option 1: Starter Template The fastest way to get started is with our starter template. Note: this is still in alpha, and may be subject to breaking changes. ### Step 1: Clone the Starter ```bash git clone https://github.com/tetherto/wdk-starter-react-native.git cd wdk-starter-react-native ``` ### Step 2: Install Dependencies ```bash npm install ``` ### Step 3: Configure Environment Create an environment file for the WDK Indexer API: ```bash cp .env.example .env ``` Edit `.env` and add your WDK Indexer API key: ```bash EXPO_PUBLIC_WDK_INDEXER_BASE_URL=https://wdk-api.tether.io EXPO_PUBLIC_WDK_INDEXER_API_KEY=your_actual_api_key_here # Optional: For Tron network support EXPO_PUBLIC_TRON_API_KEY=your_tron_api_key EXPO_PUBLIC_TRON_API_SECRET=your_tron_api_secret ``` **Where do I get an Indexer API key?** The WDK Indexer is required for transaction history and balance indexing. Get your free API key from the [Indexer API setup guide](/tools/indexer-api/get-started). ### Step 4: Run Your App ```bash npm run ios ``` ```bash npm run android ``` **Congratulations!** You now have a multi-chain wallet running. [**→ Skip to What's Next**](#whats-next) --- ## Option 2: Add to Existing App Integrate WDK into your existing React Native or Expo project using `@tetherto/wdk-react-native-core`. ### Step 1: Install ```bash npm install @tetherto/wdk-react-native-core ``` ### Step 2: Configure Android minSdkVersion The library requires **Android API 29** or higher to support `react-native-bare-kit`. Add to your `app.json` or `app.config.js`: ```json { "expo": { "plugins": [ [ "expo-build-properties", { "android": { "minSdkVersion": 29 } } ] ] } } ``` If you haven't installed `expo-build-properties`: ```bash npx expo install expo-build-properties ``` Update `android/build.gradle`: ```groovy buildscript { ext { minSdkVersion = 29 // ... other config } } ``` ### Step 3: Configure the Bundle The WDK engine runs inside a Bare worklet. You need to provide a bundle - choose one of two approaches: Use the `@tetherto/wdk-worklet-bundler` CLI to generate a bundle with only the modules you need: ```bash # 1. Install the bundler CLI npm install -g @tetherto/wdk-worklet-bundler # 2. Initialize configuration in your React Native project wdk-worklet-bundler init # 3. Edit wdk.config.js to configure your networks (see example below) # 4. Install required WDK modules (pick the ones you need) npm install @tetherto/wdk @tetherto/wdk-wallet-evm-erc-4337 # 5. Generate the bundle wdk-worklet-bundler generate ``` This generates a `.wdk/` directory in your project. Import it: ```typescript import { bundle } from './.wdk' ``` **Which WDK modules do I need?** Each blockchain requires its own wallet module (e.g., `wdk-wallet-evm-erc-4337` for Ethereum/Polygon, `wdk-wallet-btc` for Bitcoin). See the full list of available modules in the [wdk-worklet-bundler documentation](https://github.com/tetherto/wdk-worklet-bundler). For quick prototyping, install and import the ready-made bundle from `@tetherto/pear-wrk-wdk`: ```bash npm install @tetherto/pear-wrk-wdk ``` ```typescript import { bundle } from '@tetherto/pear-wrk-wdk' ``` The pre-built bundle includes all blockchain modules, resulting in a larger bundle size. For production apps, generate a custom bundle with only the modules you need. ### Step 4: Configure WDK Settings Create a configuration file for your WDK setup (e.g., `src/config/wdk.ts`): ```typescript // src/config/wdk.ts import type { WdkConfigs } from '@tetherto/wdk-react-native-core' export const wdkConfigs: WdkConfigs = { networks: { ethereum: { blockchain: 'ethereum', config: { chainId: 11155111, // Sepolia testnet provider: 'https://rpc.sepolia.org', bundlerUrl: 'https://api.candide.dev/public/v3/sepolia', paymasterUrl: 'https://api.candide.dev/public/v3/sepolia', paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba', transferMaxFee: 5000000, paymasterToken: { address: '0xaA8E23Fb1079EA71e0a56F48a2aA51851D8433D0', // USDT on Sepolia }, }, }, // Add more networks as needed }, } ``` This example uses **Sepolia testnet** with a free public RPC so you can start immediately without API keys. For production or mainnet configuration, see the [Chain Configuration Guide](/sdk/core-module/configuration). ### Step 5: Add WdkAppProvider Wrap your app with `WdkAppProvider` to enable wallet functionality throughout your app. Add to your `app/_layout.tsx`: ```tsx // app/_layout.tsx import { WdkAppProvider } from '@tetherto/wdk-react-native-core' import { bundle } from './.wdk' import { Stack } from 'expo-router' import { wdkConfigs } from '../config/wdk' export default function RootLayout() { return ( ) } ``` Update your `App.tsx`: ```tsx // App.tsx import React from 'react' import { WdkAppProvider } from '@tetherto/wdk-react-native-core' import { bundle } from './.wdk' import { NavigationContainer } from '@react-navigation/native' import { MainNavigator } from './src/navigation' import { wdkConfigs } from './src/config/wdk' export default function App() { return ( ) } ``` ### Step 6: Use Hooks Now you can use the WDK hooks in any component inside `WdkAppProvider`: ```tsx import { useWdkApp, useWalletManager, useAccount } from '@tetherto/wdk-react-native-core' function WalletScreen() { const { state } = useWdkApp() const { createWallet, unlock } = useWalletManager() const { address } = useAccount({ network: 'ethereum', accountIndex: 0 }) switch (state.status) { case 'INITIALIZING': return Loading... case 'NO_WALLET': return ) } ``` *** ## Custom Themes ### Brand Themes Apply your brand colors and fonts using `createThemeFromBrand`: ```tsx title="Brand Theme Creation" import { ThemeProvider, createThemeFromBrand } from '@tetherto/wdk-uikit-react-native' const brandTheme = createThemeFromBrand({ primaryColor: '#007AFF', secondaryColor: '#FF3B30', fontFamily: { regular: 'Inter-Regular', bold: 'Inter-Bold', }, }, 'light') {/* Your branded app */} ``` **BrandConfig Interface** ```typescript title="BrandConfig Type" type BrandConfig = { primaryColor: string secondaryColor?: string fontFamily?: { regular?: string medium?: string semiBold?: string bold?: string } } ``` ### Custom Theme Create a completely custom theme with full control over all design tokens: ```tsx title="Custom Theme" import { ThemeProvider } from '@tetherto/wdk-uikit-react-native' const myLightTheme = { mode: 'light' as const, colors: { primary: '#007AFF', primaryLight: '#4DA6FF', primaryDark: '#0056CC', onPrimary: '#FFFFFF', secondary: '#FF3B30', secondaryLight: '#FF6B60', secondaryDark: '#CC2F26', background: '#FFFFFF', surface: '#F9FAFB', surfaceVariant: '#F3F4F6', surfaceElevated: '#E5E7EB', text: '#111827', textSecondary: '#6B7280', textDisabled: '#9CA3AF', border: '#E5E7EB', borderLight: '#F3F4F6', error: '#EF4444', warning: '#F59E0B', success: '#10B981', info: '#3B82F6', }, typography: { fontFamily: { regular: 'System', medium: 'System', semiBold: 'System', bold: 'System' }, fontSize: { xs: 10, sm: 12, base: 14, md: 16, lg: 18, xl: 20, xxl: 24, xxxl: 30 }, fontWeight: { regular: '400', medium: '500', semiBold: '600', bold: '700' }, }, spacing: { xs: 4, sm: 8, base: 12, md: 16, lg: 24, xl: 32, xxl: 48, xxxl: 64 }, borderRadius: { none: 0, sm: 4, md: 8, lg: 16, xl: 24, xxl: 32, full: 9999 }, } {/* Your app */} ``` **Theme Interface** ```typescript title="Theme Type" type Theme = { mode: 'light' | 'dark' | 'auto' colors: ColorPalette typography: Typography spacing: Spacing borderRadius: BorderRadius componentVariants?: ComponentVariant componentOverrides?: ComponentOverrides } ``` *** ## Component Customization You can customize the components with fine-grained control. Fine-grained style overrides for specific component parts: ```tsx title="Component Overrides" {/* Your app */} ``` Set default visual variants per component: ```tsx title="Component Variants" const customTheme = { ...lightTheme, componentVariants: { 'AmountInput.default': { /* variant styles */ }, 'TransactionItem.compact': { /* variant styles */ } } } ``` *** ## Using Theme Anywhere Access theme values anywhere in your components: **useTheme Hook** ```tsx title="useTheme Hook" import { useTheme } from '@tetherto/wdk-uikit-react-native' function MyComponent() { const { theme } = useTheme() return ( Hello World ) } ``` *** ## Theme Structure ### Color Palette The theming system uses semantic naming for colors: | Token | Purpose | | ------------------------ | -------------------------------- | | `colors.primary` | Primary brand color | | `colors.primaryLight` | Light variant of primary | | `colors.primaryDark` | Dark variant of primary | | `colors.onPrimary` | Text color on primary background | | `colors.secondary` | Secondary brand color | | `colors.secondaryLight` | Light variant of secondary | | `colors.secondaryDark` | Dark variant of secondary | | `colors.background` | Main background color | | `colors.surface` | Card/container background | | `colors.surfaceVariant` | Alternative surface color | | `colors.surfaceElevated` | Elevated surface color | | `colors.text` | Primary text color | | `colors.textSecondary` | Secondary text color | | `colors.textDisabled` | Disabled text color | | `colors.border` | Border color | | `colors.borderLight` | Light border color | | `colors.error` | Error state color | | `colors.warning` | Warning state color | | `colors.success` | Success state color | | `colors.info` | Info state color | ### Typography | Token | Purpose | | -------------------------------- | ----------------------------- | | `typography.fontFamily.regular` | Regular font family | | `typography.fontFamily.medium` | Medium font family | | `typography.fontFamily.semiBold` | Semi-bold font family | | `typography.fontFamily.bold` | Bold font family | | `typography.fontSize.xs` | Extra small font size (10px) | | `typography.fontSize.sm` | Small font size (12px) | | `typography.fontSize.base` | Base font size (14px) | | `typography.fontSize.md` | Medium font size (16px) | | `typography.fontSize.lg` | Large font size (18px) | | `typography.fontSize.xl` | Extra large font size (20px) | | `typography.fontSize.xxl` | 2X large font size (24px) | | `typography.fontSize.xxxl` | 3X large font size (30px) | | `typography.fontWeight.regular` | Regular font weight ('400') | | `typography.fontWeight.medium` | Medium font weight ('500') | | `typography.fontWeight.semiBold` | Semi-bold font weight ('600') | | `typography.fontWeight.bold` | Bold font weight ('700') | ### Spacing | Token | Purpose | | -------------- | -------------------------- | | `spacing.xs` | Extra small spacing (4px) | | `spacing.sm` | Small spacing (8px) | | `spacing.base` | Base spacing (12px) | | `spacing.md` | Medium spacing (16px) | | `spacing.lg` | Large spacing (24px) | | `spacing.xl` | Extra large spacing (32px) | | `spacing.xxl` | 2X large spacing (48px) | | `spacing.xxxl` | 3X large spacing (64px) | ### Border Radius | Token | Purpose | | ------------------- | -------------------------------- | | `borderRadius.none` | No border radius (0px) | | `borderRadius.sm` | Small border radius (4px) | | `borderRadius.md` | Medium border radius (8px) | | `borderRadius.lg` | Large border radius (16px) | | `borderRadius.xl` | Extra large border radius (24px) | | `borderRadius.xxl` | 2X large border radius (32px) | | `borderRadius.full` | Full border radius (9999px) | *** ## Advanced Usage **Dynamic Theme Updates** Update themes dynamically at runtime: ```tsx title="Dynamic Theme Updates" function Settings() { const { setBrandConfig, setComponentOverrides } = useTheme() const updateBrand = () => { setBrandConfig({ primaryColor: '#FF6501', }) } const customizeTransactions = () => { setComponentOverrides({ TransactionItem: { container: { backgroundColor: 'rgba(255, 101, 1, 0.1)', }, }, }) } return ( <> ) } ``` ## Next Steps * [Get Started](get-started) - Quick start guide for the UI Kit * [Components List](api-reference) - Complete API Reference for all components * [React Native Quickstart](/start-building/react-native-quickstart) - See theming in action *** ## Need Help?