# 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
Connect AI assistants to WDK docs and tools
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 vs WDK CLI**: Use an **agent skill** when your agent platform works with file-based instructions (e.g., OpenClaw, Cursor). Use the [MCP Toolkit](/ai/mcp-toolkit/) when you are building a custom MCP server in code. Use [WDK CLI](/cli/) when you want a ready-made local wallet CLI, daemon, and MCP server.
## 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 and routes** | DEX swaps via Velora and Swidge routes through providers such as Orchestra |
| **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](/ai/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](/ai/openclaw/) or clone to workspace. See [OpenClaw Integration](/ai/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 [WDK CLI](/cli/guides/use-mcp-server/) for a ready-made local daemon, or the [MCP Toolkit](/ai/mcp-toolkit/) for a custom MCP server |
| **Any local CLI-capable agent** | Use [WDK CLI](/cli/) with `--json` output |
| **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 CLI](/cli/) - Local CLI, daemon, and MCP server for AI agents
- [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
Use a ready-made local wallet CLI, daemon, and MCP server when you do not need to build a custom MCP server
***
## 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
[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
})
```
***
## 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://ts.sdk.modelcontextprotocol.io/v2/servers/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 |
***
## 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](/ai/mcp-toolkit/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, {
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/) |
***
## Next Steps
* [**Configuration**](/ai/mcp-toolkit/configuration/) - Wallets, tokens, protocols, custom tools, and security
* [**API Reference**](/ai/mcp-toolkit/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](/ai/mcp-toolkit/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_API_KEY` plus a non-sensitive `MOONPAY_SECRET_KEY=unused` sentinel in the released CLI; URLs remain unsigned |
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 | -- | With the sentinel below, passes the released `serve` CLI's MoonPay registration gate |
| `MOONPAY_SECRET_KEY` | No | -- | Set to a non-sensitive sentinel such as `unused` only to satisfy the released CLI gate; never provide the real MoonPay signing secret |
The released `serve` CLI checks for both MoonPay variables but passes a configuration field that MoonPay beta.3 does not use. Its generated widget URLs therefore remain unsigned. Never give `serve` the real signing secret. For signed MoonPay flows, use programmatic registration with the [`signUrl` configuration](/sdk/fiat-modules/fiat-moonpay/configuration#basic-configuration) and keep the signing secret in an authenticated backend.
### 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.
If you want OpenClaw to call a ready-made local wallet daemon through MCP instead of loading a file-based skill, see [WDK CLI MCP setup](/cli/guides/use-mcp-server/).
## 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.

*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 and route tokens** via Velora and Swidge providers such as Orchestra
- **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](/ai/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](/ai/agent-skills/) - Full capabilities, how skills work, and a comparison with other agentic wallet solutions
- [WDK CLI MCP](/cli/guides/use-mcp-server/) - Configure OpenClaw with the bundled WDK CLI MCP server
- [MCP Toolkit](/ai/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
***
## WDK CLI
URL: https://docs.wdk.tether.io/cli
Description: Install and use the local WDK wallet CLI, daemon, and MCP server
WDK CLI provides a local command-line wallet built with WDK. Use it to manage named wallets, derive addresses, read balances and history, send registered assets, and connect an MCP-compatible AI client to the same local wallet daemon.
**Beta** - This documentation describes `@tetherto/wdk-cli@1.0.0-beta.1`. Test integrations with a dedicated wallet and limited funds before relying on them.
## Install
WDK CLI requires Node.js 22.18.0 or later.
```bash title="Terminal"
npm install -g --allow-scripts=@tetherto/wdk-cli @tetherto/wdk-cli@1.0.0-beta.1
```
The allowlist permits the CLI package's postinstall script, which installs the version-pinned wallet modules in its bundled network configuration.
Verify the installation:
```bash title="Terminal"
wdk --version
```
Install the scoped `@tetherto/wdk-cli` package. The unscoped `wdk-cli` package is not this CLI.
## Installed Binaries
| Binary | Purpose |
| --- | --- |
| `wdk` | Runs wallet, read, send, configuration, network, token, fiat-ramp, and MCP setup commands |
| `wdk-daemon` | Holds unlocked wallet instances and serves local wallet requests; `wdk` starts it when needed |
| `wdk-mcp` | Exposes WDK CLI wallet operations to MCP-compatible clients |
Most users invoke `wdk`. The CLI and MCP integrations manage the other two binaries.
## Command Map
| Command | What it does |
| --- | --- |
| `wdk wallet` | Create, import, export, list, rename, delete, unlock, lock, and select wallets |
| `wdk get` | Derive addresses and read balances or transfer history |
| `wdk send` | Preview or broadcast native and registered-token transfers |
| `wdk buy`, `wdk sell` | Create MoonPay on-ramp or off-ramp URLs |
| `wdk config` | Read, update, reset, or locate local configuration |
| `wdk network` | Inspect built-in networks and manage custom networks |
| `wdk token` | Inspect built-in tokens and manage custom token entries |
| `wdk mcp` | Configure the bundled MCP server for Claude Desktop, Claude Code, or OpenClaw |
Run `wdk --help` for the command map or append `--help` to a command:
```bash title="Terminal"
wdk wallet create --help
wdk get balance --help
```
## Choose an Interface
| Interface | Use it when |
| --- | --- |
| WDK CLI | A person, shell script, or local agent can run `wdk` commands |
| Bundled MCP server | An MCP-compatible client should call the CLI's structured wallet tools; automated setup supports Claude Desktop, Claude Code, and OpenClaw |
| [MCP Toolkit](/ai/mcp-toolkit/) | You are building and customizing an MCP server in code |
The CLI and bundled MCP server use the same local wallet store and daemon. Review the [security model](/cli/reference/security-model) before unlocking a funded wallet or giving another local process access to the MCP server.
## Next Steps
Create an Ethereum mainnet wallet, fund it, and send ETH
Configure paths, defaults, indexer access, wallet modules, and MoonPay
Review all commands, required parameters, options, and defaults
Create, import, unlock, lock, rename, export, and delete wallets
Connect the bundled MCP server to an MCP-compatible client
Use exit statuses and understand the current `--json` contract
***
## Need Help?
***
## API Reference
URL: https://docs.wdk.tether.io/cli/api-reference
Description: Complete WDK CLI beta.1 command and option reference
This page documents the 31 leaf commands in `@tetherto/wdk-cli@1.0.0-beta.1`. Run `wdk COMMAND --help` to inspect the installed command surface.
## Root Options
| Option | Behavior |
| --- | --- |
| `--json` | Requests machine-readable output from the selected command; see [JSON and exit behavior](#json-and-exit-behavior) for exceptions |
| `--verbose` | Adds a stack trace to handled errors; it does not enable general debug logging |
| `-V`, `--version` | Prints the CLI version followed by the installed WDK dependency versions |
| `-h`, `--help` | Prints help for the selected command |
The WDK-specific global flags are `--json` and `--verbose`; version and help are also root options. Options such as `--wallet` and `--index` belong to individual commands.
## Shared Wallet Selection
Wallet-dependent read, send, buy, and sell commands use:
| Option | Behavior |
| --- | --- |
| `--wallet ` | Uses the named wallet; otherwise uses `defaultWallet` |
| `--index ` | Uses a non-negative account index; otherwise uses `defaultIndex`, initially `0` |
The selected wallet must be unlocked before daemon-backed operations. See [Manage Wallets](/cli/guides/manage-wallets).
## Wallet Commands
### `wdk wallet create`
Creates a named wallet from a newly generated BIP-39 seed phrase.
| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `--name ` | Yes | — | Wallet name |
| `--words ` | No | `12` | Seed length; accepts `12` or `24` |
The first created wallet becomes the default. The command prompts for a passphrase and prints the seed phrase. With `--json`, the success object also contains `seedPhrase`.
### `wdk wallet import`
Imports an existing 12-word or 24-word BIP-39 seed phrase.
| Option | Required | Description |
| --- | --- | --- |
| `--name ` | Yes | Wallet name |
The command prompts for the seed phrase and a new storage passphrase. `WDK_PASSPHRASE` supplies only the passphrase; it does not supply the seed phrase.
### `wdk wallet export`
Decrypts and prints a wallet's seed phrase.
| Option | Required | Description |
| --- | --- | --- |
| `--name ` | Yes | Wallet name |
With `--json`, the success object contains `seedPhrase`.
The output from `wallet create` and `wallet export` is secret material in both text and JSON modes. Do not log it, paste it into an agent transcript, or store it in CI output.
### `wdk wallet list`
Lists local wallets with their default, lock, and TTL state. This command has no command-specific options.
### `wdk wallet delete`
Deletes a named wallet after verifying its passphrase.
| Option | Required | Description |
| --- | --- | --- |
| `--name ` | Yes | Wallet name |
If the deleted wallet was the default, the CLI selects the first remaining wallet as the new default. See [Manage Wallets](/cli/guides/manage-wallets) for the deletion and backup implications.
### `wdk wallet unlock`
Unlocks a wallet and starts the daemon when needed.
| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `--name ` | Yes | — | Wallet name |
| `--ttl ` | No | `5` | Non-negative session duration in minutes; `0` disables automatic expiry |
Unlocking an already unlocked wallet resets that wallet's timer. The timer is absolute from unlock or reset; wallet activity does not extend it.
### `wdk wallet lock`
Locks one wallet or every wallet.
| Option | Required | Description |
| --- | --- | --- |
| `--name ` | One selector required | Wallet to lock |
| `--all` | One selector required | Lock every wallet |
If both selectors are present, beta.1 applies `--all`.
### `wdk wallet default`
Sets the default wallet after passphrase confirmation.
| Option | Required | Description |
| --- | --- | --- |
| `--name ` | Yes | Existing wallet name |
### `wdk wallet rename`
Renames a wallet after verifying its passphrase. An unlocked source wallet is locked first.
| Option | Required | Description |
| --- | --- | --- |
| `--name ` | Yes | Current wallet name |
| `--new-name ` | Yes | New wallet name |
## Read Commands
### `wdk get address`
Derives an address for one network or for a network group.
| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `--network ` | One selector required | — | Derive one network address |
| `--all` | One selector required | — | Derive addresses for all mainnets by default |
| `--wallet ` | No | Default wallet | Wallet selection |
| `--index ` | No | Configured index, initially `0` | Non-negative account index |
| `--testnet` | No | Off | With `--all`, select testnets instead of mainnets |
When both `--network` and `--all` are supplied, beta.1 runs the single-network path. In aggregate mode, networks that fail address derivation are omitted from the result.
### `wdk get balance`
Reads one registered asset balance or native balances across a network group.
| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `--network ` | One selector required | — | Query one network |
| `--all` | One selector required | — | Query native balances on all mainnets by default |
| `--token ` | No | Native asset | Registered ticker for a single-network query |
| `--wallet ` | No | Default wallet | Wallet selection |
| `--index ` | No | Configured index, initially `0` | Non-negative account index |
| `--testnet` | No | Off | With `--all`, select testnets instead of mainnets |
`--token` is ignored by the aggregate path, which queries native assets. Networks that fail in aggregate mode are omitted. A missing price produces a USD value of `0` rather than failing the balance lookup.
### `wdk get history`
Reads token-transfer history through the configured indexer.
| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `--network ` | Yes | — | Network to query |
| `--token ` | No | All indexer-supported tokens | Exact `metadata.indexerSlug` code; the installed registry yields `btc`, `usdt`, and `xaut`, while custom entries can add other codes |
| `--limit ` | No | `30` | Positive maximum number of transfers |
| `--from-date ` | No | — | ISO 8601 start date |
| `--to-date ` | No | — | ISO 8601 end date |
| `--wallet ` | No | Default wallet | Wallet selection |
| `--index ` | No | Configured index, initially `0` | Non-negative account index |
When `--token` is omitted, beta.1 batches the network's supported token requests, ignores failed batch items, merges successful transfers by timestamp, and then applies `--limit`.
## Send Command
### `wdk send`
Previews or broadcasts a native or registered-token transfer.
| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `--network ` | Yes | — | Network to send on |
| `--to ` | Yes | — | Recipient address |
| `--amount ` | Yes | — | Positive decimal amount, or an integer when `--base-units` is set |
| `--token ` | No | Native asset | Registered token ticker |
| `--wallet ` | No | Default wallet | Wallet selection |
| `--index ` | No | Configured index, initially `0` | Non-negative account index |
| `--base-units` | No | Off | Treat `--amount` as raw base units |
| `--dry-run` | No | Off | Estimate fees and return a preview without broadcasting |
Use `--dry-run` before broadcasting:
```bash title="Terminal"
wdk send \
--network ethereum \
--to 0x000000000000000000000000000000000000dEaD \
--amount 0.001 \
--dry-run
```
Without `--dry-run`, the command broadcasts immediately. There is no additional interactive confirmation.
## Fiat Ramp Commands
`wdk buy` and `wdk sell` derive the selected wallet address and print a signed provider URL to open in a browser. Both require an unlocked wallet and valid [MoonPay configuration](/cli/configuration#moonpay).
### `wdk buy`
| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `--network ` | Yes | — | Network to receive the asset on |
| `--token ` | Yes | — | Registered asset code |
| `--fiat-amount ` | One amount required | — | Fiat amount to spend |
| `--crypto-amount ` | One amount required | — | Crypto amount to buy |
| `--fiat-currency ` | No | `usd` | Fiat currency code |
| `--module ` | No | `moonpay` | Fiat provider module |
| `--wallet ` | No | Default wallet | Wallet selection |
| `--index ` | No | Configured index, initially `0` | Non-negative account index |
### `wdk sell`
| Option | Required | Default | Description |
| --- | --- | --- | --- |
| `--network ` | Yes | — | Network holding the asset |
| `--token ` | Yes | — | Registered asset code |
| `--fiat-amount ` | One amount required | — | Target fiat amount |
| `--crypto-amount ` | One amount required | — | Crypto amount to sell |
| `--fiat-currency ` | No | `usd` | Fiat currency code |
| `--module ` | No | `moonpay` | Fiat provider module |
| `--wallet ` | No | Default wallet | Wallet selection |
| `--index ` | No | Configured index, initially `0` | Non-negative account index |
For each command, provide exactly one of `--fiat-amount` and `--crypto-amount`. Beta.1 supports only the `moonpay` module.
## Configuration Commands
See [Configuration](/cli/configuration) for keys, types, precedence, and storage considerations.
### `wdk config get`
| Option | Required | Description |
| --- | --- | --- |
| `--key ` | One selector required | Read one dot-separated key |
| `--network ` | One selector required | Read a network object, or scope `--key` to a network |
| `--all` | One selector required | Read the configuration view |
`--all` cannot be combined with `--key` or `--network`. `--network` and `--key` can be combined.
### `wdk config set`
| Option | Required | Description |
| --- | --- | --- |
| `--value ` | Yes | JSON value when parseable; otherwise a string |
| `--key ` | Without `--network` | Dot-separated key |
| `--network ` | No | Scope `--key`, or replace the network's entire configuration object |
### `wdk config reset`
| Option | Required | Description |
| --- | --- | --- |
| `--key ` | One selector required | Reset or remove one key |
| `--network ` | No | Scope `--key` to a network |
| `--all` | One selector required | Reset configuration while preserving the default wallet and custom network/token records |
`--key` and `--all` are mutually exclusive. `--network` can be combined only with `--key`.
### `wdk config path`
Prints the resolved `config.json` path. This command has no command-specific options.
## Network Commands
### `wdk network list`
| Option | Default | Description |
| --- | --- | --- |
| `--testnet` | Off | Show only testnets |
| `--mainnet` | Off | Show only mainnets |
With neither option, the command shows every registered network. If both are provided, beta.1 applies `--testnet`.
### `wdk network create `
Creates a custom network from an inline JSON object or a JSON file path. The `` positional argument is required.
See [Custom Networks](/cli/guides/custom-networks) for the network schema and validation rules.
### `wdk network delete`
| Option | Required | Description |
| --- | --- | --- |
| `--name ` | Yes | Custom network to delete |
Built-in networks cannot be deleted. Deleting a custom network also removes its network configuration and custom token entries.
### `wdk network info`
| Option | Required | Description |
| --- | --- | --- |
| `--network ` | Yes | Registered network to inspect |
## Token Commands
### `wdk token list`
| Option | Default | Description |
| --- | --- | --- |
| `--network ` | All networks | Filter to one registered network |
### `wdk token info`
| Option | Required | Description |
| --- | --- | --- |
| `--network ` | Yes | Registered network |
| `--token ` | Yes | Registered token ticker |
### `wdk token add `
Adds or overrides a token from an inline JSON object or a JSON file path. The `` positional argument is required.
See [Manage Tokens](/cli/guides/manage-tokens) for the token schema and built-in override behavior.
### `wdk token delete`
| Option | Required | Description |
| --- | --- | --- |
| `--network ` | Yes | Registered network |
| `--token ` | Yes | Custom token ticker to delete |
The command removes only a custom entry. If that entry overrides a built-in token, the built-in entry becomes effective again.
## MCP Setup Commands
The accepted `--ai-tool` values are `claude-desktop`, `claude-code`, and `openclaw`.
### `wdk mcp setup`
Adds the bundled MCP server to the selected client. `--ai-tool ` is required.
### `wdk mcp remove`
Removes the bundled MCP server from the selected client. `--ai-tool ` is required.
### `wdk mcp verify-setup`
Checks the selected client's configuration and tests the MCP server. `--ai-tool ` is required.
### `wdk mcp list`
Shows setup status for all supported clients. This command has no command-specific options.
See [Use the MCP Server](/cli/guides/use-mcp-server) for client-specific setup and the exposed tool surface.
## JSON and Exit Behavior
Most command handlers print one JSON value to stdout when `--json` is set. The current contract has exceptions:
- `wdk mcp setup`, `remove`, `verify-setup`, and `list` print human-readable success output even with `--json`.
- Help, version, unknown-command, unknown-option, and missing-required-option output remains text.
- Interactive wallet prompts render on stdout. If a wallet command opens a prompt, prompt text and terminal-control bytes can precede any JSON result; `wallet import` always prompts for the seed phrase.
- `wdk send` can write spinner or completion text to stderr while emitting JSON on stdout.
- A non-empty `WDK_PASSPHRASE` produces a notice on stderr.
Parse stdout separately from stderr and always check the exit status. See [Handle Errors](/cli/guides/handle-errors) for the error envelope and exit-status contract.
***
## Need Help?
***
## Configuration
URL: https://docs.wdk.tether.io/cli/configuration
Description: Configure WDK CLI paths, defaults, indexer access, wallet modules, and fiat ramps
WDK CLI stores its configuration and wallet data under one local configuration directory. Use `wdk config` for supported changes instead of editing `config.json` directly.
## Local Paths
The default configuration directory is `~/.config/wdk-cli`. If `XDG_CONFIG_HOME` is a non-empty environment variable, WDK CLI uses `$XDG_CONFIG_HOME/wdk-cli` instead.
| Data | Path |
| --- | --- |
| User configuration | `CONFIG_DIR/config.json` |
| Wallet seed | `CONFIG_DIR/wallets/NAME/seed.enc` |
| Daemon PID | `CONFIG_DIR/daemon.pid` |
| Daemon socket on Unix-like systems | `CONFIG_DIR/daemon.sock` |
| Daemon endpoint on Windows | `\\.\pipe\wdk-cli-daemon` |
Print the resolved `config.json` path:
```bash title="Terminal"
wdk config path
```
With JSON output:
```bash title="Terminal"
wdk config path --json
```
```json
{"path":"/home/user/.config/wdk-cli/config.json"}
```
See [Storage Format](/cli/reference/storage-format) for seed-file and daemon-file permissions.
## Configuration Precedence
WDK CLI resolves runtime values in this order:
| Value | Highest to lowest precedence |
| --- | --- |
| Configuration directory | Non-empty `XDG_CONFIG_HOME`, then `~/.config` |
| Wallet | Command `--wallet`, then `defaultWallet` |
| Account index | Command `--index`, then `defaultIndex`, then `0` |
| Indexer API key | Non-empty `WDK_INDEXER_API_KEY`, then stored `indexer.apiKey`, then an empty value |
| Wallet passphrase | Non-empty `WDK_PASSPHRASE`, then a hidden interactive prompt |
`--wallet` and `--index` are options on wallet-dependent commands; they are not root flags.
An empty `WDK_PASSPHRASE` value does not override the prompt. To use an empty passphrase, enter it interactively. See [Manage Wallets](/cli/guides/manage-wallets) before choosing an empty passphrase.
## Environment Variables
| Variable | Effect |
| --- | --- |
| `XDG_CONFIG_HOME` | Changes the parent directory used for WDK CLI data |
| `WDK_INDEXER_API_KEY` | Overrides `indexer.apiKey` for the current process |
| `WDK_PASSPHRASE` | Supplies a non-empty passphrase instead of opening a prompt |
There are no beta.1 environment-variable mappings for `indexer.baseUrl`, wallet defaults, account defaults, network configuration, or MoonPay configuration.
Environment variables can be inherited by child processes and may be visible to other processes running as the same OS user. Limit their lifetime and do not print them in shell history, CI logs, or agent transcripts.
## Supported Keys
| Key | Expected value | Default or behavior |
| --- | --- | --- |
| `defaultWallet` | Wallet name | The first created or imported wallet becomes the default; use `wdk wallet default` to change it |
| `defaultIndex` | Non-negative integer | `0` |
| `indexer.baseUrl` | Indexer base URL | `https://wdk-api.tether.io` |
| `indexer.apiKey` | Indexer API key | Empty; `WDK_INDEXER_API_KEY` overrides it |
| `ramp.moonpay.apiKey` | MoonPay publishable key (`pk_test_...` or `pk_live_...`) | Empty; do not use a MoonPay secret key |
| `ramp.moonpay.signUrl` | URL of an HTTP service that signs MoonPay widget URLs | Empty; the service must return a `signedUrl` |
| `ramp.moonpay.environment` | `sandbox` or `production` | Empty; required by `wdk buy` and `wdk sell` |
| `networks.NETWORK` | Wallet-module configuration object | Defaults come from the installed `wdk.config.json` |
| `networks.NETWORK.KEY` | One wallet-module configuration value | Depends on the selected wallet module |
Custom-network records and custom-token records also live in `config.json`. Manage them with `wdk network` and `wdk token` so the CLI can validate their shape and related state.
## Read Configuration
Read one global key:
```bash title="Terminal"
wdk config get --key defaultIndex
```
Read one network configuration:
```bash title="Terminal"
wdk config get --network ethereum
```
Read a key inside one network configuration:
```bash title="Terminal"
wdk config get --network ethereum --key provider
```
Read the full configuration view:
```bash title="Terminal"
wdk config get --all
```
`config get --all` excludes the custom-token registry. Use `wdk token list` to read tokens.
`config.json` is plaintext. The CLI does not request an owner-only mode for this file, so its effective permissions follow the operating system and runtime defaults and may be `0644`. `config get --all` can reveal stored API keys, signing URLs, and credentials embedded in provider URLs. Do not publish the file or command output.
Prefer `WDK_INDEXER_API_KEY` when you do not want to persist the indexer key. No environment override is available for MoonPay configuration in beta.1.
## Set Configuration
Set a string:
```bash title="Terminal"
wdk config set --key indexer.baseUrl --value https://indexer.example.com
```
Set a number:
```bash title="Terminal"
wdk config set --key defaultIndex --value 1
```
Set a JSON object:
```bash title="Terminal"
wdk config set \
--key ramp.moonpay \
--value '{"apiKey":"pk_test_...","signUrl":"https://example.com/sign","environment":"sandbox"}'
```
`config set` parses a valid JSON value into its JSON type. If parsing fails, it stores the value as a string. Quote objects and arrays so the shell passes them as one argument.
Set a network-specific key:
```bash title="Terminal"
wdk config set \
--network ethereum \
--key provider \
--value https://ethereum-rpc.publicnode.com
```
Replace a network's complete wallet-module configuration:
```bash title="Terminal"
wdk config set \
--network ethereum \
--value '{"chainId":1,"provider":"https://ethereum-rpc.publicnode.com","transferMaxFee":5000000000000000}'
```
Network configuration is passed to the selected WDK wallet module. Use only keys supported by that module.
## Reset Configuration
Reset one global key:
```bash title="Terminal"
wdk config reset --key indexer.baseUrl
```
Reset one network key:
```bash title="Terminal"
wdk config reset --network ethereum --key provider
```
Reset configuration defaults:
```bash title="Terminal"
wdk config reset --all
```
`config reset --all` preserves the default-wallet selection, custom networks, and custom tokens. It resets the remaining values to their installed defaults.
## Authorization and Wallet Locking
When at least one wallet exists, these configuration mutations verify the current default wallet's passphrase:
- `wdk config set`
- `wdk config reset`
- `wdk network create`
- `wdk network delete`
- `wdk token add`
- `wdk token delete`
Set `WDK_PASSPHRASE` for non-interactive local automation or enter the passphrase at the prompt.
Changing or resetting a key under `networks` locks all wallets so the next unlock initializes WDK with the new wallet-module configuration. `config reset --all` also locks all wallets.
## Indexer
Within the CLI command set, only `wdk get history` uses the WDK Indexer API. The MCP `get_history` tool uses the same history path. [Request an Indexer API key](/tools/indexer-api/get-started) before connecting directly.
### Connect Directly
The default `indexer.baseUrl` is `https://wdk-api.tether.io`. Store your API key:
```bash title="Terminal"
wdk config set --key indexer.apiKey --value YOUR_INDEXER_API_KEY
```
Alternatively, supply it to the current process without writing it to `config.json`:
```bash title="Terminal"
WDK_INDEXER_API_KEY=YOUR_INDEXER_API_KEY \
wdk get history --network ethereum --wallet dev
```
### Use an Indexer Proxy
Point the CLI at your own endpoint when you do not want Indexer API keys on developer machines:
```bash title="Terminal"
wdk config reset --key indexer.apiKey
wdk config set --key indexer.baseUrl --value https://indexer-proxy.example.com
```
Also ensure `WDK_INDEXER_API_KEY` is not set in the CLI process. A non-empty environment value overrides the stored empty value and causes the CLI to send an `x-api-key` header.
The proxy must accept both history request forms:
| Request | Used when |
| --- | --- |
| `GET /api/v1/{blockchain}/{token}/{address}/token-transfers` with optional `limit`, `fromTs`, and `toTs` query parameters | `wdk get history` includes `--token` |
| `POST /api/v1/batch/token-transfers` | The command queries all Indexer-supported tokens |
Forward the query parameters or JSON request body and the Indexer response unchanged. Add the Indexer `x-api-key` header when forwarding the request upstream.
`WDK_INDEXER_BASE_URL` is not read by beta.1.
## MoonPay
`wdk buy` and `wdk sell` derive the selected wallet address and build a signed MoonPay widget URL. The CLI prints the URL, or returns it in JSON output; it does not open the browser or execute the fiat transaction. Open the URL to continue on MoonPay, which processes the transaction through the integration associated with your MoonPay account.
Get a publishable key from **Developers → API Keys** in the [MoonPay dashboard](https://dashboard.moonpay.com/). Use a `pk_test_...` or `pk_live_...` publishable key here, never the `sk_test_...` or `sk_live_...` secret key.
The commands require all three MoonPay values:
```bash title="Terminal"
wdk config set --key ramp.moonpay.apiKey --value pk_test_...
wdk config set --key ramp.moonpay.signUrl --value https://example.com/moonpay/sign
wdk config set --key ramp.moonpay.environment --value sandbox
```
### Sign Widget URLs
Because the CLI includes a wallet address in the MoonPay widget URL, it sends the unsigned URL to your signing service. That service signs the URL with your MoonPay secret key and returns the complete signed URL. See MoonPay's [on-ramp URL signing](https://dev.moonpay.com/widget/on-ramp/customization/url-signing) and [off-ramp URL signing](https://dev.moonpay.com/widget/off-ramp/customization/url-signing) guides.
The CLI sends this request to `ramp.moonpay.signUrl`:
```http
POST /moonpay/sign HTTP/1.1
Content-Type: application/json
{"urlForSignature":"https://..."}
```
Return a successful JSON response with the complete signed URL:
```json
{"signedUrl":"https://...&signature=..."}
```
Returning only the signature is not supported.
Keep the MoonPay secret key in the signing service. Beta.1 does not send configurable authentication headers to `signUrl`, so bind the service locally, keep it on a private network, or restrict access with network-level controls. Do not expose an unauthenticated public signing endpoint.
### Select an Environment
| Environment | Publishable key | Network |
| --- | --- | --- |
| `sandbox` | `pk_test_...` | Testnet |
| `production` | `pk_live_...` | Mainnet |
The CLI rejects a sandbox/mainnet or production/testnet mismatch. It does not validate the publishable-key prefix, so configure the matching key yourself.
***
## Need Help?
***
## Custom Networks
URL: https://docs.wdk.tether.io/cli/guides/custom-networks
Description: Add and remove blockchain networks in WDK CLI
Use a custom network when WDK CLI already supports the network's wallet-module type but does not include the specific chain in its built-in registry.
`wdk network create` cannot introduce a new wallet-module implementation. Its `module` field must match a wallet-module type already used by a built-in network.
## Inspect Available Networks
List built-in and custom networks:
```bash title="Terminal"
wdk network list
```
Use JSON output to inspect both the versioned `module` and its unversioned `type`:
```bash title="Terminal"
wdk --json network list
```
Use a returned `type`, such as `@tetherto/wdk-wallet-evm`, as the `module` value in a custom network spec.
Inspect the effective metadata and SDK configuration for one network:
```bash title="Terminal"
wdk network info --network ethereum
```
## Network Spec
`wdk network create ` accepts either an inline JSON object or the path to a JSON file.
| Field | Required | Rules and effect |
| --- | --- | --- |
| `network` | Yes | Unique identifier containing lowercase letters, numbers, and hyphens. The first character must be a letter or number. |
| `module` | Yes | Unversioned wallet-module type already used by a built-in network. |
| `displayName` | No | Non-empty display label. Defaults to the `network` value. |
| `testnet` | No | Boolean. Defaults to `false`. |
| `indexerSlug` | No | Non-empty WDK Indexer chain identifier. Without it, `get history` is unavailable for the custom network. |
| `config` | No | Object passed to the selected wallet module, such as a provider URL and chain ID. The wallet module validates these values when used. |
| `tokens` | No | Array of token specs to store with the network. Token keys must be unique, and at most one entry can be native. |
Each item in `tokens` uses the fields documented in [Manage Tokens](/cli/guides/manage-tokens/#token-spec), with `network` omitted because the parent network supplies it.
## Create A Custom Network
Create `optimism.json`:
```json title="optimism.json"
{
"network": "optimism",
"module": "@tetherto/wdk-wallet-evm",
"displayName": "Optimism",
"testnet": false,
"config": {
"provider": "https://mainnet.optimism.io",
"chainId": 10
},
"tokens": [
{
"token": "eth",
"symbol": "ETH",
"decimals": 18,
"isNative": true
}
]
}
```
Create the network and its native-token entry:
```bash title="Terminal"
wdk network create ./optimism.json
```
Verify the result:
```bash title="Terminal"
wdk network info --network optimism
wdk token list --network optimism
```
The example uses a public mainnet RPC endpoint. Verify the chain ID, provider, token addresses, and provider-specific limits before using a custom network with funds. Omit `indexerSlug` unless you know the WDK Indexer identifier for that chain.
## Creation Side Effects
The CLI validates the complete network and token spec before storing it. On success, it writes:
- The custom network metadata
- The network's SDK `config`
- Every entry in `tokens`
If storing a token fails, the command rolls back the custom network, its SDK config, and token entries already written by that command.
If at least one wallet exists, `network create` prompts for the current default wallet's passphrase. It does not require the wallet to be unlocked. With no wallets, it does not prompt.
`network create` and `network delete` do not unlock, extend, or lock an existing daemon wallet session. `wdk config reset --all` preserves custom network entries.
## Update Network Configuration
Use `config set` to replace the complete SDK config object:
```bash title="Terminal"
wdk config set \
--network optimism \
--value '{"provider":"https://mainnet.optimism.io","chainId":10}'
```
Or change one nested key:
```bash title="Terminal"
wdk config set \
--network optimism \
--key provider \
--value https://mainnet.optimism.io
```
Changing `networks.*` configuration locks every unlocked wallet so the daemon drops cached wallet managers. Unlock the wallet again before reading balances or sending.
## Delete A Custom Network
Deleting a custom network also deletes its SDK configuration and every custom token under that network. Export or record the spec first if you may need to recreate it.
Delete the network:
```bash title="Terminal"
wdk network delete --name optimism
```
Deletion prompts for the default wallet's passphrase when wallets exist. Built-in networks cannot be deleted.
Deleting registry configuration does not move blockchain assets or delete a wallet seed, but the CLI can no longer access that network until you recreate the registry entry.
## Next Steps
- [Manage Tokens](/cli/guides/manage-tokens/) - Add token contracts and provider mappings
- [Configuration](/cli/configuration/) - Review paths, environment variables, and precedence
- [API Reference](/cli/api-reference/) - Review all network command flags
***
## Need Help?
***
## Get Started
URL: https://docs.wdk.tether.io/cli/guides/get-started
Description: Install WDK CLI and complete an Ethereum mainnet wallet flow
Install WDK CLI globally:
```bash title="Terminal"
npm install -g --allow-scripts=@tetherto/wdk-cli @tetherto/wdk-cli@1.0.0-beta.1
```
The current release is `1.0.0-beta.1`. Beta releases can change before the stable release.
This guide uses Ethereum mainnet and can spend real funds. Create a dedicated wallet, fund it with only enough ETH for this example and network fees, and verify the network, address, and amount before sending funds.
## Prerequisites
- Node.js `22.18.0` or later
- npm
- Enough ETH on Ethereum mainnet to fund the new wallet and pay the example transaction fee
- A second Ethereum mainnet address you control to receive the example transfer
Verify the installation:
```bash title="Terminal"
wdk --version
wdk --help
```
The package installs the `wdk`, `wdk-mcp`, and `wdk-daemon` binaries. Use `wdk` for the steps below.
## 1. Create A Wallet
Create a named 12-word wallet:
```bash title="Terminal"
wdk wallet create --name quickstart --words 12
```
The CLI then asks for the wallet-encryption passphrase:
1. At `Passphrase (empty for none):`, enter a strong, unique passphrase. The input is hidden.
2. At `Confirm passphrase:`, enter the same passphrase again.
3. After the wallet is stored, the CLI displays the generated seed phrase once. Record it offline before continuing.
Store the seed phrase and passphrase separately. Do not put either value in a terminal command, source file, screenshot, agent transcript, or online note. The current CLI permits an empty passphrase, but an empty value provides no meaningful protection for the encrypted seed file.
If this is your first wallet, it becomes the default automatically. You do not need to change an existing default wallet because the wallet-dependent commands below select `quickstart` explicitly.
## 2. Unlock The Wallet
Unlock the wallet for five minutes:
```bash title="Terminal"
wdk wallet unlock --name quickstart --ttl 5
```
At `Enter passphrase of 'quickstart' wallet to unlock:`, enter the wallet passphrase. The input is hidden.
The TTL starts at unlock and does not restart when you use the wallet. Unlock it again if the session expires while you complete this guide.
Set `--ttl 0` to disable automatic expiry:
```bash title="Terminal"
wdk wallet unlock --name quickstart --ttl 0
```
An unlimited session remains unlocked until you explicitly lock it, the daemon stops, its process fails, or the machine restarts. Any process running as the same operating-system user that can connect to the daemon can use an unlocked wallet without entering the passphrase again. Prefer a finite TTL and use `--ttl 0` only in a controlled local environment.
## 3. Get The Ethereum Address
Derive the wallet's Ethereum mainnet address:
```bash title="Terminal"
wdk get address --network ethereum --wallet quickstart
```
Copy the returned address and verify every character before using it as the funding destination.
## 4. Fund The Wallet
From another wallet or exchange you trust:
1. Select Ethereum mainnet.
2. Paste the address from step 3 as the destination.
3. Send more than `0.0001 ETH` so the new wallet can send `0.0001 ETH` and pay the Ethereum gas fee in step 6.
4. Account for any separate withdrawal or network fee charged by the sending wallet or exchange.
5. Verify the network, destination, and amount, then submit the transfer.
Receiving funds is an on-chain operation outside WDK CLI. The wallet does not need to remain unlocked while you wait for the transfer to confirm.
Wait for the funding transaction to confirm before checking the balance. The five-minute session may expire while you wait.
## 5. Check The Balance
Read the wallet's native Ethereum balance:
```bash title="Terminal"
wdk get balance --network ethereum --wallet quickstart
```
If the command reports that the wallet is locked, unlock it again and repeat the balance check.
## 6. Send ETH
Use an Ethereum address you control as the recipient.
The next command broadcasts an irreversible Ethereum mainnet transaction and spends real ETH. WDK CLI does not ask for another confirmation after the wallet is unlocked. Replace the placeholder, then review the network, recipient, amount, and available balance before running it.
```bash title="Terminal"
wdk send \
--network ethereum \
--to YOUR_ETHEREUM_ADDRESS \
--amount 0.0001 \
--wallet quickstart
```
The command broadcasts immediately and prints the resulting transaction identifier.
### Optional: Estimate Without Broadcasting
Add `--dry-run` to estimate the fee and return a transaction summary without broadcasting:
```bash title="Terminal"
wdk send \
--network ethereum \
--to YOUR_ETHEREUM_ADDRESS \
--amount 0.0001 \
--wallet quickstart \
--dry-run
```
## 7. Lock The Wallet
End the wallet session when you finish:
```bash title="Terminal"
wdk wallet lock --name quickstart
```
Lock every unlocked wallet with:
```bash title="Terminal"
wdk wallet lock --all
```
## Next Steps
- [Manage Wallets](/cli/guides/manage-wallets/) - Import, export, rename, unlock, and delete wallets
- [Manage Tokens](/cli/guides/manage-tokens/) - Inspect and extend the token registry
- [Use the MCP Server](/cli/guides/use-mcp-server/) - Connect an MCP-compatible AI client
- [Security Model](/cli/reference/security-model/) - Understand the daemon trust boundary and session trade-offs
***
## Need Help?
***
## Handle Errors
URL: https://docs.wdk.tether.io/cli/guides/handle-errors
Description: Handle WDK CLI exit statuses, JSON output, stderr, and error codes
Use a command's exit status as the primary success signal. When you request `--json`, parse stdout separately from stderr and allow for commands that still produce text.
This page describes `@tetherto/wdk-cli@1.0.0-beta.1`.
## Handled Error Envelope
Errors that reach the WDK CLI command handler use this JSON shape:
```json
{
"error": "Wallet 'dev' is not unlocked.",
"code": "WALLET_NOT_UNLOCKED",
"suggestion": "Run: wdk wallet unlock --name dev"
}
```
| Field | Present | Description |
| --- | --- | --- |
| `error` | Always | Human-readable message |
| `code` | Always | Machine-readable code |
| `suggestion` | Sometimes | Recovery guidance |
| `stack` | Only with `--verbose` when available | JavaScript stack trace |
`--verbose` adds error stacks. It does not enable general debug logging.
## Exit Statuses
| Status | Meaning in beta.1 |
| --- | --- |
| `0` | Command, help, or version output completed |
| `1` | A handled CLI/runtime error or a command-line parsing error occurred |
| `2` | An unexpected non-`Error` value or an error outside the normal command handler reached the top level |
Do not treat the presence of stdout as success. Some handled failures emit a JSON object to stdout and exit with status `1`.
## JSON Output Contract
For most successful commands, `--json` emits one JSON value followed by a newline on stdout. Handled WDK CLI errors also emit one JSON object on stdout.
The current exceptions are:
| Case | Stdout | Stderr | Status |
| --- | --- | --- | --- |
| Most successful commands with `--json` | JSON | Usually empty | `0` |
| Handled command error with `--json` | JSON error envelope | Usually empty | `1`, or `2` for a non-`Error` value |
| Unknown command, unknown option, or missing required option | Empty | Human-readable Commander error and help | `1` |
| Help or version, even with `--json` | Human-readable text | Empty | `0` |
| Successful `wdk mcp setup`, `remove`, `verify-setup`, or `list` with `--json` | Human-readable text | Empty | `0` |
| Wallet command that opens an interactive prompt with `--json` | Prompt text and terminal-control bytes, followed by JSON if the command completes | Normal notices or errors can still appear | Depends on result |
| `wdk send --json` | JSON on completion or a handled error | May contain spinner, success, or failure text | Depends on result |
| A command using non-empty `WDK_PASSPHRASE` | Normal command output | Includes a passphrase-source notice | Depends on result |
Do not combine stdout and stderr before parsing JSON. Spinner output and notices can make a combined stream invalid JSON.
`--json` changes output formatting; it does not make every command non-interactive or guarantee JSON-only stdout when a prompt opens. Wallet create, import, export, unlock, delete, default, and rename flows can still request secret input. Set a non-empty `WDK_PASSPHRASE` only when you accept the environment-variable exposure described in [Configuration](/cli/configuration#environment-variables). `wallet import` still prompts for the seed phrase, so its stdout is not a clean JSON stream even when the environment variable supplies the passphrase.
## Handle Output in a Shell Script
Capture the streams separately and inspect the exit status before parsing. This example requires `jq`:
```bash title="check-balance.sh"
#!/usr/bin/env bash
set -euo pipefail
stdout_file=$(mktemp)
stderr_file=$(mktemp)
trap 'rm -f "$stdout_file" "$stderr_file"' EXIT
if wdk get balance \
--network ethereum \
--wallet dev \
--json >"$stdout_file" 2>"$stderr_file"; then
jq . "$stdout_file"
elif jq -e 'type == "object" and has("code")' "$stdout_file" >/dev/null 2>&1; then
jq . "$stdout_file" >&2
exit 1
else
cat "$stderr_file" >&2
exit 1
fi
```
This pattern handles both JSON command errors and text-only argument parsing errors.
## Error Codes
The following names are defined by beta.1. A code can appear only on commands that reach the corresponding behavior.
| Area | Codes |
| --- | --- |
| Wallet and key state | `KEY_NOT_FOUND`, `INVALID_SEED_PHRASE`, `WRONG_PASSPHRASE`, `WALLET_NOT_UNLOCKED`, `WALLET_EXISTS`, `WALLET_LOCKED`, `PASSPHRASE_MISMATCH` |
| Arguments and configuration | `INVALID_ARGUMENT`, `INVALID_INDEX`, `INVALID_CONFIG`, `MISSING_CONFIG`, `INVALID_AMOUNT`, `INVALID_TOKEN` |
| Networks and tokens | `NETWORK_NOT_SUPPORTED`, `TOKEN_NOT_SUPPORTED`, `NETWORK_ERROR` |
| Transactions and providers | `INSUFFICIENT_BALANCE`, `TRANSACTION_FAILED`, `UNSUPPORTED_MODULE`, `ENVIRONMENT_MISMATCH`, `SIGN_FAILED`, `PROVIDER_UNAVAILABLE`, `QUOTE_REJECTED` |
| Fallbacks | `UNKNOWN_ERROR`, `UNEXPECTED_ERROR` |
The code set is not a closed protocol enum. The daemon and WDK dependencies can pass through additional codes. Beta.1 also recognizes and formats several dependency codes, including `INSUFFICIENT_FUNDS`, `SERVER_ERROR`, and `TIMEOUT`. Consumers should preserve unknown code strings instead of rejecting the response.
## Common Recovery Paths
| Code or symptom | Check |
| --- | --- |
| `WALLET_NOT_UNLOCKED` | Run `wdk wallet unlock --name NAME`; confirm that its TTL has not expired |
| `KEY_NOT_FOUND` | Run `wdk wallet list`; verify the wallet name or default wallet |
| `WRONG_PASSPHRASE` | Retry through the hidden prompt; do not print or log the passphrase |
| `NETWORK_NOT_SUPPORTED` | Run `wdk network list`; check spelling and custom-network state |
| `TOKEN_NOT_SUPPORTED` | Run `wdk token list --network NETWORK`; use the registered ticker |
| `MISSING_CONFIG` for history | Configure `indexer.baseUrl` and, when required, `WDK_INDEXER_API_KEY` |
| `MISSING_CONFIG` for buy or sell | Configure all `ramp.moonpay` values |
| `ENVIRONMENT_MISMATCH` | Use MoonPay `sandbox` with a testnet or `production` with a mainnet |
| `NETWORK_ERROR`, `TIMEOUT`, or `SERVER_ERROR` | Check the RPC/indexer endpoint and retry only when repeating the operation is safe |
| Text error with empty stdout | Treat it as an argument/help parsing failure; inspect stderr |
## Partial Results
Some successful aggregate operations omit failed items:
- `wdk get address --all` skips networks that fail address derivation.
- `wdk get balance --all` skips networks that fail address or balance lookup.
- `wdk get history` without `--token` ignores failed token batch items and returns successful transfers.
These commands can exit `0` with an incomplete aggregate result. If completeness matters, query each required network or token separately and track failures in your application.
***
## Need Help?
***
## Manage Tokens
URL: https://docs.wdk.tether.io/cli/guides/manage-tokens
Description: Inspect, add, override, and remove WDK CLI token registry entries
WDK CLI resolves token names such as `usdt` through a local registry. Each entry defines how the CLI formats amounts, selects a native or contract transfer, and connects the token to optional indexer, MoonPay, and price-provider features.
The effective registry combines built-in entries with your custom entries. A custom entry with the same network and token key replaces the built-in entry until you remove the override.
## Inspect The Registry
List tokens across every network:
```bash title="Terminal"
wdk token list
```
Filter the list to one network or inspect one entry:
```bash title="Terminal"
wdk token list --network ethereum
wdk token info --network ethereum --token usdt
```
Use the lowercase registry key shown by these commands with `--token` on `get balance`, `send`, `buy`, and `sell`.
`get history --token` is different: pass the exact `metadata.indexerSlug` value, which may differ from the registry key.
## Token Spec
`wdk token add ` accepts either an inline JSON object or the path to a JSON file.
| Field | Required | Rules and effect |
| --- | --- | --- |
| `network` | Yes | Must name an existing built-in or custom network. |
| `token` | Yes | Registry key. Use lowercase letters, numbers, and hyphens; the first character must be a letter or number. |
| `symbol` | Yes | Non-empty display symbol, such as `DAI`. |
| `decimals` | Yes | Integer from `0` through `24`; used to convert decimal amounts to base units. |
| `isNative` | Yes | `true` uses the network's native transfer path; `false` uses a token contract or mint. |
| `address` | For non-native tokens | Non-empty contract or mint address. Optional for a native token. |
| `metadata` | No | Object containing supported provider mappings. |
A network can have at most one effective entry with `isNative: true`.
### Provider Metadata
| Field | Used by | Effect when omitted |
| --- | --- | --- |
| `metadata.indexerSlug` | `wdk get history` | Pass this exact value to `--token`. Without it, a token-specific history request cannot use the token and an all-token history request skips it. The network also needs its own `indexerSlug`. |
| `metadata.moonpaySlug` | `wdk buy` and `wdk sell` | MoonPay operations do not support that token. |
| `metadata.bitfinexSlug` | USD price conversion | Balance and preview operations can continue without that provider's USD estimate. |
Only `indexerSlug`, `moonpaySlug`, and `bitfinexSlug` are retained inside `metadata`.
## Add A Custom Token
Create a file named `dai-on-ethereum.json`:
```json title="dai-on-ethereum.json"
{
"network": "ethereum",
"token": "dai",
"symbol": "DAI",
"decimals": 18,
"isNative": false,
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
}
```
Add the entry:
```bash title="Terminal"
wdk token add ./dai-on-ethereum.json
```
You can also pass the JSON inline:
```bash title="Terminal"
wdk token add '{"network":"ethereum","token":"dai","symbol":"DAI","decimals":18,"isNative":false,"address":"0x6B175474E89094C44Da98b954EedeAC495271d0F"}'
```
Verify the stored entry:
```bash title="Terminal"
wdk token info --network ethereum --token dai
```
Adding a registry entry does not transfer tokens or interact with the blockchain. Verify contract addresses, decimals, and provider identifiers independently before using the entry to query or send assets.
## Understand Mutation Confirmation
`token add` and `token delete` protect registry changes with passphrase confirmation:
- If at least one wallet exists, the CLI prompts for the current default wallet's passphrase.
- If no wallet exists, the registry command does not prompt for a passphrase.
- Confirmation does not unlock, extend, or lock an existing daemon wallet session.
Set a valid default wallet before changing the registry if wallets already exist.
## Override A Built-In Entry
Adding a custom entry with the same `network` and `token` as a built-in entry replaces the complete effective entry; it does not merge individual fields. The CLI reports that the custom entry overrides the built-in entry.
Use overrides carefully. An incorrect address, decimal count, or native-token flag can route later balance or send operations incorrectly.
Remove the custom override to restore the built-in entry:
```bash title="Terminal"
wdk token delete --network ethereum --token usdt
```
## Delete A Custom Entry
Delete the DAI entry created above:
```bash title="Terminal"
wdk token delete --network ethereum --token dai
```
`token delete` removes only custom entries. It rejects deletion of a built-in entry when no custom override exists. Deleting a custom network also removes every custom token stored under that network.
`wdk config reset --all` preserves custom token entries.
## Next Steps
- [Custom Networks](/cli/guides/custom-networks/) - Create a network and its initial token entries together
- [Configuration](/cli/configuration/) - Configure provider and indexer access
- [API Reference](/cli/api-reference/) - Review token command flags and JSON results
***
## Need Help?
***
## Manage Wallets
URL: https://docs.wdk.tether.io/cli/guides/manage-wallets
Description: Create, import, select, unlock, lock, export, rename, and delete WDK CLI wallets safely.
WDK CLI stores independent named wallets and keeps only explicitly unlocked wallets in the daemon. This guide covers the complete wallet lifecycle.
Wallet create, import, and export handle a BIP-39 seed phrase. Use a private terminal with logging, screen sharing, and AI assistants disabled. Anyone who obtains the phrase can control the wallet.
## Wallet names
Wallet names may contain letters, numbers, hyphens, and underscores. Other characters are rejected.
Examples in this guide use a wallet named `dev`:
```bash title="Terminal"
wdk wallet list
```
## Create a wallet
Create a 12-word wallet:
```bash title="Terminal"
wdk wallet create --name dev
```
Create a 24-word wallet:
```bash title="Terminal"
wdk wallet create --name dev --words 24
```
The CLI asks for a passphrase twice, writes the encrypted mnemonic to `wallets/dev/seed.enc`, and displays the generated phrase once. Record both the mnemonic and passphrase in separate recoverable locations before continuing.
The first wallet becomes the default automatically. You do not need to run `wdk wallet default` after creating the first wallet.
The current CLI permits an empty passphrase. Although `seed.enc` remains AES-GCM ciphertext, an empty passphrase provides no meaningful confidentiality. Use a strong, unique passphrase.
## Import a wallet
Import an existing 12- or 24-word BIP-39 phrase:
```bash title="Terminal"
wdk wallet import --name recovered
```
The CLI prompts for the phrase and then for a new local encryption passphrase. The seed-phrase input is interactive but is not masked, so import only in a private terminal.
Import creates another encrypted local copy. It does not remove or change the source wallet or any existing backup.
## List wallets and sessions
Show stored wallets, the default wallet, lock status, and TTL remaining:
```bash title="Terminal"
wdk wallet list
```
When a wallet is unlocked, the table reports either the approximate remaining time or `unlimited` for a `--ttl 0` session.
In JSON mode, an unlocked entry includes `ttlMs` and `ttlRemaining` in milliseconds:
```bash title="Terminal"
wdk --json wallet list
```
## Select the default wallet
Commands use the default wallet when their `--wallet` option is omitted.
```bash title="Terminal"
wdk wallet default --name dev
```
If a default wallet already exists, changing it requires that wallet's passphrase. This prevents an unconfirmed configuration change, but it does not unlock either wallet.
To target another wallet for one supported operation without changing the default, pass its command-level option:
```bash title="Terminal"
wdk get balance --network ethereum --wallet recovered
```
## Unlock a wallet
Unlock with the default five-minute TTL:
```bash title="Terminal"
wdk wallet unlock --name dev
```
Specify an absolute TTL in minutes:
```bash title="Terminal"
wdk wallet unlock --name dev --ttl 15
```
Unlock starts the daemon if needed. The CLI verifies the passphrase, then the daemon decrypts the wallet, creates its WDK instance, and starts the timer.
After unlock, any process running as the same operating-system user that can connect to the daemon endpoint can request signing or sending without entering the passphrase. Use a short TTL, avoid running untrusted code, and lock immediately after use.
### Control the unlock lifetime
TTL is per wallet and starts at unlock:
| Command or event | Result |
| --- | --- |
| Omit `--ttl` | Wallet locks after five minutes |
| `--ttl 15` | Wallet locks 15 minutes after unlock |
| Use the wallet | Timer continues; activity does not restart it |
| Unlock the wallet again | Timer resets to the newly requested TTL |
| `--ttl 0` | No automatic expiry |
| TTL expires | That wallet is disposed and locked |
| Last wallet locks | Daemon exits |
`--ttl 0` accepts the risk of a session that remains unlocked until explicit lock, daemon shutdown, process failure, or machine restart:
```bash title="Terminal"
wdk wallet unlock --name dev --ttl 0
```
Use it only for a controlled local workflow. Normal wallet operations do not refresh any TTL, so a finite session can expire during a long task.
## Lock wallets
Lock one wallet:
```bash title="Terminal"
wdk wallet lock --name dev
```
Lock every wallet:
```bash title="Terminal"
wdk wallet lock --all
```
Locking disposes the wallet's WDK instance and removes the session from the daemon. The daemon exits after the last wallet locks.
Verify the result:
```bash title="Terminal"
wdk wallet list
```
Cleanup is best effort. The CLI zeroes retained mutable key and seed buffers on normal disposal, but JavaScript strings, dependency-internal copies, swap, core dumps, and abrupt termination cannot be guaranteed to be erased. See the [security model](/cli/reference/security-model#seed-and-passphrase-lifetime).
## Export a seed phrase
Export prints the decrypted mnemonic. Do not run this command in CI, a recorded terminal, an agent session, or any environment that captures stdout.
Export a wallet:
```bash title="Terminal"
wdk wallet export --name dev
```
The CLI asks for the wallet passphrase and prints the phrase. `--json` also includes the phrase in stdout; JSON does not make secret output safe to log.
Use export to create or verify an offline recovery backup. If WDK CLI itself is unavailable, use the standalone [version 1 manual-recovery procedure](/cli/reference/storage-format#recover-without-wdk-cli).
## Rename a wallet
Rename a stored wallet:
```bash title="Terminal"
wdk wallet rename --name dev --new-name development
```
The command verifies the old wallet's passphrase, locks that wallet if it is unlocked, and moves its storage directory. If it was the default, the new name becomes the default. Unlock it again under the new name before using it:
```bash title="Terminal"
wdk wallet unlock --name development
```
Renaming does not decrypt or re-encrypt `seed.enc`.
## Delete a wallet
Deletion is irreversible through WDK CLI. Confirm that you have tested the mnemonic and passphrase backup before deleting the only local copy.
Delete a wallet:
```bash title="Terminal"
wdk wallet delete --name development
```
The command:
1. verifies the wallet passphrase
2. attempts to lock an active session
3. recursively removes the wallet directory
4. chooses another stored wallet as the default when necessary
Deletion is ordinary filesystem removal, not secure erase. Copies can remain in backups, snapshots, journals, swap, or recoverable storage blocks.
## Passphrases in automation
Commands that prompt for a passphrase read a non-empty `WDK_PASSPHRASE` value when it is set:
```bash title="Terminal"
WDK_PASSPHRASE='YOUR_PASSPHRASE' wdk --json wallet unlock --name dev --ttl 5
```
Environment variables can leak through process inspection, child-process inheritance, command tracing, crash reports, or automation logs. Prefer the hidden interactive prompt. If automation is necessary:
- inject the value from a secret manager for one process
- disable command tracing and output capture
- do not commit the value to a script or configuration file
- lock the wallet and remove the environment variable immediately after use
Never pass the passphrase or mnemonic as a CLI argument.
## Related pages
- [API reference](/cli/api-reference#wallet-commands)
- [Architecture](/cli/reference/architecture)
- [Storage format and manual recovery](/cli/reference/storage-format)
- [Security model](/cli/reference/security-model)
***
## Use the MCP Server
URL: https://docs.wdk.tether.io/cli/guides/use-mcp-server
Description: Connect WDK CLI to an MCP-compatible AI client
WDK CLI includes the `wdk-mcp` Model Context Protocol server. It exposes wallet operations as structured tools over stdio and routes wallet-dependent operations to the same local daemon used by the CLI.
Use a dedicated development wallet with limited funds. On Unix-like systems, while a wallet is unlocked, any process running as the same OS user that can connect to the owner-only daemon socket can request signing or sending without another passphrase.
## Prepare A Wallet
Install WDK CLI with the command in [Install](/cli/#install). This guide deliberately uses a dedicated development wallet and Sepolia test funds; the general Get Started guide uses Ethereum mainnet.
If `agent-dev` is your first wallet, it becomes the default automatically:
```bash title="Terminal"
wdk wallet create --name agent-dev --words 12
wdk wallet unlock --name agent-dev --ttl 5
```
Enter and confirm a strong, non-empty passphrase when the create command prompts, then record the generated seed phrase offline.
If another wallet is already the default, select `agent-dev` explicitly:
```bash title="Terminal"
wdk wallet default --name agent-dev
```
Wallet-dependent MCP tools use the default wallet unless the request includes `wallet`. The wallet must be unlocked before those tools run.
The TTL is absolute from unlock; MCP activity does not extend it. Use a short TTL during development and lock the wallet when the session ends.
## Configure An MCP Client
Choose one setup target:
```bash title="Claude Desktop"
wdk mcp setup --ai-tool claude-desktop
```
```bash title="Claude Code"
wdk mcp setup --ai-tool claude-code
```
```bash title="OpenClaw"
wdk mcp setup --ai-tool openclaw
```
| Target | Setup behavior |
| --- | --- |
| Claude Desktop | Adds `wdk-wallet` under `mcpServers` in the platform-specific Claude Desktop JSON config. |
| Claude Code | Runs `claude mcp add -s user wdk-wallet -- ...` with the current Node.js executable and installed MCP script. |
| OpenClaw | Runs `openclaw mcp set wdk-wallet ...` with the current Node.js executable and installed MCP script. |
Setup checks whether the target appears to be installed, tests the MCP server when possible, and reports the restart action required by that client.
Verify one target:
```bash title="Terminal"
wdk mcp verify-setup --ai-tool claude-desktop
```
List the configuration status of all three targets:
```bash title="Terminal"
wdk mcp list
```
Remove a target configuration with:
```bash title="Terminal"
wdk mcp remove --ai-tool claude-desktop
```
Replace `claude-desktop` with `claude-code` or `openclaw` as needed.
### Other MCP Clients
`wdk-mcp` is a standard stdio MCP server. For clients that accept the common `mcpServers` JSON shape, add:
```json
{
"mcpServers": {
"wdk-wallet": {
"command": "wdk-mcp"
}
}
}
```
This configuration requires the client process to find `wdk-mcp` on its `PATH`. If the client uses a different configuration shape or does not inherit your shell `PATH`, configure a stdio server named `wdk-wallet` and point its command to the installed `wdk-mcp` executable.
## Available MCP Tools
| Tool | Wallet required | Purpose |
| --- | --- | --- |
| `get_networks` | No | List networks, optionally filtered to mainnets or testnets. |
| `list_tokens` | No | List every registered token or filter by network. |
| `get_token` | No | Read one network and token registry entry. |
| `get_address` | Yes | Derive one address or addresses across networks. |
| `get_balance` | Yes | Read one balance or aggregate native balances with USD estimates. |
| `get_history` | Yes | Query indexer-backed token-transfer history for one network. |
| `send_token` | Yes | Preview or execute a native or registered-token transfer. |
| `buy_crypto` | Yes | Create a signed MoonPay buy URL. |
| `sell_crypto` | Yes | Create a signed MoonPay sell URL. |
`get_history` needs indexer configuration. `buy_crypto` and `sell_crypto` need MoonPay configuration. See [Configuration](/cli/configuration/).
## CLI Capabilities Not Exposed Over MCP
`wdk-mcp` registers only the nine tools listed above. Through this server, an agent can operate a wallet that a person has already unlocked, but it cannot call the CLI's wallet administration or persistent-configuration commands.
- Wallet administration: `wdk wallet create`, `import`, `export`, `unlock`, `lock`, `delete`, `rename`, and `default`. These commands create, import, or reveal seed material; confirm wallet ownership; change wallet identity or default selection; or control an unlock session. `wdk wallet unlock` is deliberately the human authorization moment, and `wdk wallet lock` ends that session.
- Persistent configuration: `wdk config set` and `reset`, `wdk network create` and `delete`, and `wdk token add` and `delete`. These commands change durable local configuration and remain user-driven decisions.
This is a boundary of the MCP tool surface, not an operating-system sandbox. An AI client with separate shell access is outside this boundary and may be able to invoke `wdk` directly.
## Preview And Confirm Transfers
`send_token` defaults to `dryRun: true`. The recommended client workflow is:
1. Call `send_token` without `dryRun`, or with `dryRun: true`.
2. Show the network, token, recipient, amount, and estimated fee to the user.
3. Ask the user to confirm those exact values.
4. Call `send_token` with `dryRun: false` only after confirmation.
Example preview request:
```json
{
"network": "sepolia",
"to": "",
"amount": "0.0001",
"wallet": "agent-dev"
}
```
Example execution request after confirmation:
```json
{
"network": "sepolia",
"to": "",
"amount": "0.0001",
"wallet": "agent-dev",
"dryRun": false
}
```
The preview-and-confirm sequence is guidance for the AI client; the daemon does not enforce it. A direct `send_token` call with `dryRun: false` broadcasts when the selected wallet is unlocked and the request is otherwise valid.
## Security And Session Boundaries
- The AI client sends tool parameters to `wdk-mcp`; it does not send the wallet seed or passphrase as tool input.
- Wallet operations cross the local daemon endpoint. On Unix-like systems, owner-only socket permissions block other OS users, not other processes running as the wallet owner.
- An unlocked wallet behaves like a local hot wallet for the session. Read, signing, and send operations do not ask for the passphrase again.
- `--ttl 0` disables automatic expiry for that wallet. Prefer a finite TTL.
- Prompts or client-side confirmation dialogs are not daemon authorization controls.
Read [Security Model](/cli/reference/security-model/) before exposing an unlocked wallet to an AI client or local automation.
Lock the wallet when the session is finished:
```bash title="Terminal"
wdk wallet lock --name agent-dev
```
## When To Use The MCP Toolkit
Use [MCP Toolkit](/ai/mcp-toolkit/) when you need to build and control an MCP server in application code, select individual WDK tools, or add custom tools.
Use the WDK CLI MCP server when you want the bundled tool set and local wallet daemon, with automated setup for Claude Desktop, Claude Code, and OpenClaw or manual stdio configuration for another MCP-compatible client.
***
## Need Help?
***
## Architecture
URL: https://docs.wdk.tether.io/cli/reference/architecture
Description: Understand how the WDK CLI, MCP server, daemon, wallet store, and WDK modules work together.
WDK CLI separates short-lived command and MCP processes from a background wallet daemon. The daemon owns unlocked WDK instances; the command and MCP processes ask it to derive addresses, read balances, estimate fees, and sign transactions over local inter-process communication (IPC).
```text
wdk command ─┐
├─ local IPC ─> wdk-daemon ─> WDK wallet modules ─> RPC provider
wdk-mcp ─────┘ │
└─ in-memory unlocked wallets
wdk command ─> WDK Indexer API (history, after deriving the address through the daemon)
```
## Components
| Component | Lifetime | Responsibility |
| --- | --- | --- |
| `wdk` | One command | Parses arguments, prompts for secrets, reads and writes local configuration, formats output, and calls the daemon |
| `wdk-mcp` | MCP client session | Exposes structured wallet tools and calls the same command actions and daemon used by `wdk` |
| `wdk-daemon` | While at least one wallet is unlocked | Holds unlocked WDK instances, derives accounts, estimates fees, signs and sends transactions, and enforces wallet TTLs |
| `seed.enc` | Until the wallet is deleted | Stores one encrypted BIP-39 mnemonic for each named wallet |
| `config.json` | Until configuration is reset or removed | Stores non-seed CLI configuration, including the default wallet, networks, tokens, and provider settings |
The CLI and MCP server share the same wallet store, configuration, and daemon. A wallet unlocked with `wdk wallet unlock` is therefore also available to `wdk-mcp` running as the same operating-system user.
## Wallet creation and import
Creating or importing a wallet does not require the daemon:
```text
generated or entered mnemonic
│
├─ scrypt(passphrase, random salt) ─> AES-256-GCM
│
└─> wallets/NAME/seed.enc
```
The command process validates that the mnemonic is a 12- or 24-word BIP-39 phrase. It then encrypts the phrase and writes [`seed.enc`](/cli/reference/storage-format). The first wallet becomes the default wallet automatically.
The generated or imported mnemonic and passphrase are JavaScript strings in the command process during this flow. JavaScript strings cannot be reliably overwritten after use. See the [security model](/cli/reference/security-model#seed-and-passphrase-lifetime) for the resulting memory limitations.
## Wallet unlock
Unlocking crosses both the command and daemon processes:
1. `wdk` reads the passphrase from a hidden prompt or `WDK_PASSPHRASE`.
2. The command process decrypts `seed.enc` to verify the passphrase.
3. The command process starts `wdk-daemon` if it is not already running.
4. It sends the wallet name, passphrase, and TTL to the daemon over local IPC.
5. The daemon decrypts `seed.enc`, derives the BIP-39 master-seed buffer, creates a WDK instance, and starts that wallet's TTL.
The passphrase and mnemonic therefore do not exist only inside the daemon. They are briefly present in the command process during normal unlock, and the daemon decrypts the mnemonic again to create the long-lived wallet session.
An unlocked wallet is a local hot wallet for the operating-system user that owns the daemon. Any process running as that user that can connect to the daemon endpoint can request wallet operations without entering the passphrase again. Socket permissions separate operating-system users; they do not authenticate individual programs running as the same user.
## Requests while unlocked
The daemon creates wallet accounts lazily. The first request for a network loads the configured wallet module and obtains the requested account index; later requests reuse cached accounts for that wallet session.
| Operation | Where it runs |
| --- | --- |
| Address derivation | Daemon |
| Native and token balance reads | Daemon, followed by optional price lookup in the caller |
| Fee estimation | Daemon |
| Transaction signing and broadcast | Daemon |
| Transaction history | Caller queries the Indexer API after obtaining the address from the daemon |
| Wallet file creation, import, export, rename, and deletion | `wdk` command process |
| Network, token, and general configuration | `wdk` command process |
CLI and MCP callers use the same signing path. A dry run estimates fees through the daemon but does not broadcast. A later request that executes a send goes directly to the daemon; the daemon does not implement a second confirmation or passphrase challenge.
## IPC and operating systems
On macOS and Linux, the daemon listens on a Unix domain socket:
```text
~/.config/wdk-cli/daemon.sock
```
If `XDG_CONFIG_HOME` is set, the socket is under `$XDG_CONFIG_HOME/wdk-cli`. The daemon creates it under an owner-only `0077` umask; the current socket mode is `0700`.
On Windows, the daemon uses the named pipe:
```text
\\.\pipe\wdk-cli-daemon
```
POSIX file modes do not apply to the Windows named pipe. The current implementation relies on the platform's default named-pipe access control rather than creating an explicit security descriptor.
IPC messages are newline-delimited JSON and are limited to 64 KiB. The protocol is internal to the CLI package and is not a versioned public API. Use `wdk` or `wdk-mcp` instead of building another client against it.
## Session and shutdown lifecycle
Each unlocked wallet has an independent TTL. The default is five minutes.
```text
unlock or explicit re-unlock ─> start absolute TTL
│
normal wallet use does not refresh it
│
TTL expires or wallet is locked
│
dispose wallet and remove its session
│
last wallet locked ─> daemon exits
```
An explicit `wdk wallet unlock` for an already unlocked wallet resets that wallet's timer to the requested value. `--ttl 0` creates a session without automatic expiry. See [Manage wallets](/cli/guides/manage-wallets#control-the-unlock-lifetime) for commands and [Security model](/cli/reference/security-model#session-lifecycle) for the accepted trade-offs.
On a normal lock, TTL expiry, `SIGINT`, or `SIGTERM`, the daemon disposes the affected WDK instances. When the last wallet is locked, it closes the IPC server, removes the socket and PID file, and exits. Abrupt process termination, system crashes, swap, and core dumps are outside this graceful-cleanup path.
## Configuration changes
Network configuration is captured by WDK instances when they are created. `wdk config set` and `wdk config reset` changes under `networks.*` lock the current wallet sessions so the next unlock creates fresh instances with the new settings. `wdk network create` and `wdk network delete` change the registry without automatically locking current sessions.
Configuration and storage locations are covered in [Configuration](/cli/configuration) and [Storage format](/cli/reference/storage-format).
## Next steps
- [Manage wallets](/cli/guides/manage-wallets)
- [Storage format and manual recovery](/cli/reference/storage-format)
- [Security model](/cli/reference/security-model)
- [API reference](/cli/api-reference)
***
## Security Model
URL: https://docs.wdk.tether.io/cli/reference/security-model
Description: Understand seed protection, daemon trust boundaries, memory lifetime, TTL auto-locking, and operational trade-offs.
WDK CLI protects a locked wallet's mnemonic with passphrase-based encryption and restricts security-critical local artifacts to the owning operating-system user on macOS and Linux. After unlock, it deliberately trusts processes running as that user.
Treat an unlocked WDK CLI wallet as a local hot wallet. Use a dedicated wallet with limited funds, keep the unlock TTL short, and lock it before running untrusted code.
## Security boundaries
| Boundary | Current protection | Not protected |
| --- | --- | --- |
| Locked seed at rest | AES-256-GCM with a scrypt-derived key; `seed.enc` mode `0600` on macOS and Linux | Weak or empty passphrases, compromised owner account, root/administrator, backups, or storage capture while unlocked |
| Daemon endpoint | Unix socket mode `0700` under an owner-only umask | Another process running as the same owner; there is no per-program daemon credential |
| PID file | Mode `0600` on macOS and Linux | Process discovery through other operating-system interfaces |
| Wallet session | Per-wallet absolute TTL and explicit lock | Activity does not shorten or refresh exposure; `--ttl 0` has no automatic expiry |
| In-memory cleanup | WDK disposal plus best-effort zeroing of retained mutable buffers | Immutable JavaScript strings, copies inside dependencies, swap, core dumps, crashes, or abrupt termination |
| CLI and MCP sends | Dry-run support and caller-level guidance | The daemon does not enforce a second confirmation or passphrase check for an unlocked wallet |
| General configuration | Separate from `seed.enc` | `config.json` has no owner-only guarantee and may contain user-added credentials |
These controls reduce accidental exposure and cross-user access. They do not make a general-purpose computer a hardware wallet or isolate an unlocked wallet from malware running under the same account.
## Seed encryption at rest
Each named wallet stores its BIP-39 mnemonic in `wallets/NAME/seed.enc`. Version 1 uses:
- AES-256-GCM authenticated encryption
- scrypt with `N=65536`, `r=8`, and `p=1`
- a random 32-byte salt and 12-byte IV
- a 16-byte authentication tag
- an owner read/write `0600` file mode on macOS and Linux
See [Storage format](/cli/reference/storage-format#seedenc-version-1) for field encodings and the manual-recovery contract.
A wrong passphrase or modified encrypted payload fails GCM authentication. Encryption does not protect a mnemonic after a process has decrypted it.
### Passphrase handling
The interactive passphrase prompt hides input. For automation, `WDK_PASSPHRASE` overrides the prompt when it contains a non-empty value.
Environment variables can be inherited by child processes and may be visible through process inspection, crash reports, shell tooling, or automation logs. Prefer the interactive prompt for manual use. If automation requires `WDK_PASSPHRASE`, scope it to one trusted process, prevent command tracing, and remove it immediately after use.
The current CLI accepts an empty passphrase. The file is still AES-GCM ciphertext, but an empty passphrase provides no meaningful confidentiality because anyone who obtains the file knows the value required to derive its key.
## Same-user daemon access
On macOS and Linux, `daemon.sock` is available only to the owning operating-system user. This stops a different local user from connecting through the socket. It does not identify or authorize individual programs owned by that user.
Once a wallet is unlocked:
- a same-user process that can connect to `daemon.sock` can derive addresses, read balances, estimate fees, and request signed transactions
- it does not need to know or re-enter the wallet passphrase
- it can speak directly to the internal daemon endpoint instead of using the normal CLI or MCP user flow
- CLI dry runs and MCP instructions to preview and confirm are caller behavior, not daemon authorization controls
This same-user signing capability is an accepted design trade-off: the daemon provides a reusable local wallet session, and the operating-system user is the session's trust boundary.
Do not run downloaded scripts, unreviewed packages, browser automation, plugins, or AI agents under the wallet owner's account while a valuable wallet is unlocked. Owner-only socket permissions do not protect against code that you run as the owner.
For stronger practical separation, run WDK CLI under a dedicated non-administrator operating-system account and do not run unrelated tools under that account. This does not protect against root/administrator compromise, but it narrows which processes can reach the owner-only endpoint.
## Session lifecycle
Each wallet has its own absolute unlock timer:
| Action | Timer effect |
| --- | --- |
| `wdk wallet unlock --name NAME` | Starts the requested TTL; default is five minutes |
| Normal address, balance, history, fee, or send request | Does not refresh the TTL |
| Explicitly unlock an already unlocked wallet | Resets its timer to the new TTL |
| `wdk wallet unlock --name NAME --ttl 0` | Disables automatic expiry for that wallet |
| `wdk wallet lock --name NAME` | Immediately disposes that wallet session |
| `wdk wallet lock --all` | Disposes all wallet sessions |
| TTL expires | Disposes that wallet session |
| Last wallet locks or expires | Shuts down the daemon |
An absolute timer limits the maximum duration from unlock without silently extending the session on every operation. It can also expire during a longer workflow because activity does not refresh it. Re-unlock explicitly when more time is needed.
Use `--ttl 0` only in a controlled environment where you accept an unlocked session that lasts until explicit lock, daemon shutdown, process failure, or machine restart. It is not appropriate as a convenience default.
## Seed and passphrase lifetime
Normal unlock handles sensitive values in more than one place:
1. The `wdk` command process receives the passphrase as a JavaScript string.
2. It decrypts `seed.enc` and receives the mnemonic as a JavaScript string to verify the passphrase.
3. It sends the passphrase through owner-restricted local IPC to the daemon.
4. The daemon decrypts the mnemonic as a JavaScript string.
5. The daemon derives a mutable BIP-39 master-seed `Buffer` and retains it with the WDK instance until lock.
The encryption key buffers are zeroed after encryption or decryption. On normal lock or graceful daemon shutdown, the CLI disposes the WDK instance and zeroes the retained master-seed buffer.
These are best-effort language-level controls, not a guarantee that every copy is erased:
- JavaScript strings are immutable and garbage-collected
- WDK or wallet modules may hold internal copies while in use
- operating-system swap, hibernation, crash dumps, and debugger access can capture process memory
- `SIGKILL`, a power loss, or a runtime crash can bypass normal cleanup
Use full-disk encryption, restrict crash dumps and debugger access, and keep the host patched and free of untrusted software. Locking promptly reduces exposure but cannot retroactively erase copies outside the CLI's control.
## Wallet export, logs, and automation
`wdk wallet create` displays the generated mnemonic, and `wdk wallet export` displays the decrypted mnemonic. With `--json`, that secret appears in structured stdout.
Do not:
- run create or export in CI
- capture their output in logs or agent transcripts
- paste output into tickets, chat, or AI tools
- include `WDK_PASSPHRASE` in a committed script
- pass a mnemonic or passphrase as a shell argument
Use a private terminal, create an offline backup, and clear terminal scrollback after handling a mnemonic.
## Configuration is not secret storage
`config.json` is a normal plaintext file. It primarily contains public WDK-style configuration, but user-supplied values can include indexer keys or credentials embedded in provider and signing URLs.
- Prefer `WDK_INDEXER_API_KEY` over storing the indexer key when your environment can protect it.
- Protect any credentials embedded in custom provider URLs separately.
- Review `wdk config get --all` before copying its output.
- Do not assume `config.json` has the same `0600` mode as `seed.enc`.
See [Configuration](/cli/configuration) for supported settings and precedence.
## Deletion and recovery
Wallet deletion performs ordinary recursive filesystem removal after passphrase verification. It is not secure erase, and it cannot remove copies from backups, snapshots, swap, journals, or previously copied files.
Maintain an independently tested backup and the passphrase. The documented [`seed.enc` version 1 recovery procedure](/cli/reference/storage-format#recover-without-wdk-cli) remains available even if a future format does not provide automated migration.
## Operational checklist
Before unlock:
- use a dedicated wallet with only the funds needed for the task
- stop untrusted same-user processes
- confirm the wallet name and requested TTL
- prefer a hidden interactive passphrase prompt
While unlocked:
- preview recipient, amount, network, token, and fees
- remember that normal use does not refresh the timer
- do not install packages or run unreviewed scripts under the same user
- treat MCP clients and agents as capable of requesting real sends
After use:
- run `wdk wallet lock --name NAME` or `wdk wallet lock --all`
- verify `wdk wallet list` reports the wallet as locked
- clear terminals or files that displayed the mnemonic
## Related pages
- [Architecture](/cli/reference/architecture)
- [Storage format and manual recovery](/cli/reference/storage-format)
- [Manage wallets](/cli/guides/manage-wallets)
- [Use the MCP server](/cli/guides/use-mcp-server)
***
## Storage Format
URL: https://docs.wdk.tether.io/cli/reference/storage-format
Description: Inspect WDK CLI storage, seed.enc version 1, file permissions, and the standalone manual-recovery procedure.
WDK CLI stores each named wallet as an encrypted `seed.enc` file. This page defines the current version 1 format and provides a recovery path that uses only Node.js built-in modules if the CLI is unavailable.
Possession of a recovered seed phrase gives control of the wallet. Perform recovery on a trusted, offline computer. Do not upload `seed.enc` to a website, paste it into an AI assistant, or use an online decryption tool.
## Storage layout
The default storage root is `~/.config/wdk-cli`. If `XDG_CONFIG_HOME` is non-empty, the root is `$XDG_CONFIG_HOME/wdk-cli`.
```text
wdk-cli/
├── config.json
├── daemon.pid
├── daemon.sock # macOS and Linux only
└── wallets/
└── WALLET_NAME/
└── seed.enc
```
Windows uses the named pipe `\\.\pipe\wdk-cli-daemon` instead of `daemon.sock`.
Run the following command to print the exact `config.json` path:
```bash title="Terminal"
wdk config path
```
## Current file permissions
The current permissions are intentional and protect the artifacts that enforce seed-at-rest and daemon access controls.
| Artifact | macOS and Linux | Purpose |
| --- | --- | --- |
| `wallets/NAME/seed.enc` | `0600` | Owner read and write only |
| `daemon.pid` | `0600` | Owner read and write only |
| `daemon.sock` | `0700` | Owner-only daemon endpoint |
| `config.json` | No owner-only mode is set by WDK CLI | Ordinary plaintext configuration; its resulting mode follows the config library and process environment and may be `0644` |
| Storage and wallet directories | No explicit mode is set by WDK CLI | Resulting modes depend on the process umask |
POSIX modes do not apply on Windows. The CLI uses a named pipe for daemon IPC and relies on Windows access controls.
`config.json` is not seed storage. However, values that you add can still be sensitive, including API keys and provider URLs containing credentials. Do not treat the absence of seed material as permission to publish the file. Prefer supported environment-variable overrides for secrets, and review output from `wdk config get --all` before sharing it.
The Unix socket's owner-only mode excludes other operating-system users. It does not distinguish between programs running as the owner. See the [same-user trust boundary](/cli/reference/security-model#same-user-daemon-access).
## `seed.enc` version 1
`seed.enc` is a UTF-8 JSON object with five fields:
```json title="seed.enc shape"
{
"version": 1,
"salt": "64 hexadecimal characters",
"iv": "24 hexadecimal characters",
"tag": "32 hexadecimal characters",
"ciphertext": "variable-length hexadecimal ciphertext"
}
```
All binary fields use hexadecimal encoding, not Base64.
| Property | Version 1 value |
| --- | --- |
| Cipher | AES-256-GCM |
| Password KDF | scrypt |
| scrypt `N` | `65536` (`2^16`) |
| scrypt `r` | `8` |
| scrypt `p` | `1` |
| scrypt maximum memory | `134217728` bytes (128 MiB) |
| Derived key | 32 bytes |
| Salt | 32 random bytes |
| IV | 12 random bytes |
| Authentication tag | 16 bytes |
| Ciphertext encoding | Hexadecimal |
| Additional authenticated data | None |
| Plaintext | UTF-8 BIP-39 mnemonic |
Only `version` and the four binary fields are stored. The algorithm and scrypt parameters are implicit in version 1.
Each write generates a new random salt and IV. AES-GCM authenticates the ciphertext: a wrong passphrase or a modified salt, IV, tag, or ciphertext causes decryption to fail.
Users can rely on the documented version 1 recovery procedure. If a future format is introduced, an automated migration is not guaranteed, but the version 1 recovery procedure will remain documented so existing files can be recovered.
## Empty passphrases
The current CLI accepts an empty passphrase. It still runs scrypt and writes AES-256-GCM ciphertext, so the mnemonic is not stored as plaintext. An empty passphrase is nevertheless known to any reader of the file and provides no meaningful confidentiality. File permissions become the only practical at-rest barrier.
Use a strong, unique passphrase and keep a recoverable record separate from `seed.enc`.
## Recover without WDK CLI
This procedure requires Node.js `22.18.0` or later but does not import WDK CLI or any third-party package.
Before recovery:
1. Copy `seed.enc` and its backup to a trusted, offline computer.
2. Preserve the original file; do not edit it in place.
3. Close screen-sharing, logging, terminal recording, clipboard managers, and AI assistants.
4. Use a private terminal. The recovered mnemonic will appear in terminal output and may remain in scrollback.
5. Do not put the passphrase in a command argument or exported environment variable.
Save the following as `recover-wdk-seed.mjs`:
```javascript title="recover-wdk-seed.mjs"
import { createDecipheriv, scryptSync } from 'node:crypto'
import { readFile } from 'node:fs/promises'
import { emitKeypressEvents } from 'node:readline'
import process from 'node:process'
const SCRYPT = {
N: 2 ** 16,
r: 8,
p: 1,
maxmem: 128 * 1024 * 1024
}
function decodeHex(field, value, expectedBytes) {
if (typeof value !== 'string' || value.length === 0) {
throw new Error(`${field} must be a non-empty hexadecimal string`)
}
if (value.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(value)) {
throw new Error(`${field} is not valid hexadecimal`)
}
const decoded = Buffer.from(value, 'hex')
if (decoded.length * 2 !== value.length) {
throw new Error(`${field} is not valid hexadecimal`)
}
if (expectedBytes !== undefined && decoded.length !== expectedBytes) {
throw new Error(`${field} must decode to ${expectedBytes} bytes`)
}
return decoded
}
function validatePayload(payload) {
if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) {
throw new Error('seed.enc must contain a JSON object')
}
if (payload.version !== 1) {
throw new Error(`unsupported seed.enc version: ${String(payload.version)}`)
}
return {
salt: decodeHex('salt', payload.salt, 32),
iv: decodeHex('iv', payload.iv, 12),
tag: decodeHex('tag', payload.tag, 16),
ciphertext: decodeHex('ciphertext', payload.ciphertext)
}
}
function promptHidden(message) {
if (!process.stdin.isTTY || !process.stdout.isTTY ||
typeof process.stdin.setRawMode !== 'function') {
throw new Error('run this script directly in a private interactive terminal')
}
emitKeypressEvents(process.stdin)
const wasRaw = process.stdin.isRaw
let secret = ''
process.stdin.setRawMode(true)
process.stdin.resume()
return new Promise((resolve, reject) => {
function finish(error) {
process.stdin.removeListener('keypress', onKeypress)
process.stdin.setRawMode(Boolean(wasRaw))
if (!wasRaw) process.stdin.pause()
process.stdout.write('\n')
if (error) reject(error)
else resolve(secret)
}
function onKeypress(text, key = {}) {
if (key.ctrl && key.name === 'c') {
finish(new Error('recovery cancelled'))
return
}
if (key.name === 'return' || key.name === 'enter') {
finish()
return
}
if (key.name === 'backspace') {
secret = secret.slice(0, -1)
return
}
if (typeof text === 'string' && !key.ctrl && !key.meta &&
!/[\u0000-\u001f\u007f]/.test(text)) {
secret += text
}
}
process.stdin.on('keypress', onKeypress)
process.stdout.write(message)
})
}
function writeOutput(chunk) {
return new Promise((resolve, reject) => {
process.stdout.write(chunk, (error) => {
if (error) reject(error)
else resolve()
})
})
}
async function main() {
const args = process.argv.slice(2)
if (args.length !== 1) {
throw new Error('usage: node recover-wdk-seed.mjs /path/to/seed.enc')
}
const file = await readFile(args[0], 'utf8')
let payload
try {
payload = JSON.parse(file)
} catch {
throw new Error('seed.enc is not valid JSON')
}
const { salt, iv, tag, ciphertext } = validatePayload(payload)
const passphrase = await promptHidden('Passphrase: ')
const key = scryptSync(passphrase, salt, 32, SCRYPT)
let decryptedChunk
let authenticatedTail
let plaintext
try {
const decipher = createDecipheriv('aes-256-gcm', key, iv)
decipher.setAuthTag(tag)
decryptedChunk = decipher.update(ciphertext)
authenticatedTail = decipher.final()
plaintext = Buffer.concat([decryptedChunk, authenticatedTail])
await writeOutput('Recovered seed phrase:\n')
await writeOutput(plaintext)
await writeOutput('\n')
} finally {
key.fill(0)
decryptedChunk?.fill(0)
authenticatedTail?.fill(0)
plaintext?.fill(0)
}
}
main().catch((error) => {
console.error(`Recovery failed: ${error.message}`)
process.exitCode = 1
})
```
Run it with only the `seed.enc` path as an argument:
```bash title="Terminal"
chmod 700 recover-wdk-seed.mjs
node recover-wdk-seed.mjs "$HOME/.config/wdk-cli/wallets/WALLET_NAME/seed.enc"
```
Enter the passphrase at the hidden prompt. The script intentionally refuses non-interactive input so the passphrase is not supplied through a pipe, argument, or environment variable.
The script validates the version, field encodings, and fixed field lengths before deriving the key. It makes a best-effort attempt to clear the derived key, decrypted chunks, and concatenated plaintext buffer after success or failure. The passphrase and displayed mnemonic still pass through JavaScript, OpenSSL internals, and terminal-managed memory, where reliable zeroization is not possible.
After recovery:
1. Verify the phrase by importing it into trusted wallet software while still offline.
2. Clear the terminal and close it to reduce scrollback exposure.
3. If the original computer or passphrase may be compromised, move funds to a newly generated seed.
4. Securely remove any temporary copies according to the storage medium and backup system you used.
## Rename and deletion behavior
Renaming a wallet moves its directory; it does not decrypt or re-encrypt `seed.enc`.
Deleting a wallet removes its wallet directory after passphrase verification and attempts to lock its active daemon session first. This is ordinary filesystem deletion, not cryptographic erasure. Copies may remain in backups, snapshots, filesystem journals, swap, or recoverable storage blocks.
Always keep an independently tested recovery backup before deleting or changing the only working copy.
## Related pages
- [Manage wallets](/cli/guides/manage-wallets)
- [Architecture](/cli/reference/architecture)
- [Security model](/cli/reference/security-model)
- [Configuration](/cli/configuration)
***
## 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/) | ✅ |
| [Ethereum EIP-7702 Gasless](/sdk/wallet-modules/wallet-evm-7702-gasless/) | ✅ |
| [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/) | ✅ |
| [Aptos](/sdk/wallet-modules/wallet-aptos/) | ✅ |
| [Spark/Lightning](/sdk/wallet-modules/wallet-spark/) | ✅ |
| Protocol/Module | Support |
| -------------------------------------------------------------- | ------- |
| [velora (EVM)](/sdk/swap-modules/swap-velora-evm/) | ✅ |
| [Orchestra Swidge](/sdk/swidge-modules/swidge-orchestra/) | Community |
| [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.
---
### July 30, 2026
**What's New**
- **wdk-core** ([v1.0.0-beta.15](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.15)): Add global and account-scoped SDA protocol registration, `getSdaProtocol()`, and policy coverage for deposit-address creation, renewal, recovery, and disablement. Governed proxies now hide `keyPair` and underscore-prefixed members from direct access and own-property reflection and reject freezing; retained raw references, prototype inspection, and nested module calls remain outside that proxy boundary.
- **wallet-evm-erc-4337** ([v1.0.0-beta.14](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.14)): Add opt-in ERC-4337 nonce lanes with `parallel` and `nonceKey` for sign, send, and transfer flows, replacing local sequential nonce reservation. Lane operations rebuild rather than reuse quote-cache UserOperations; same-lane operations require sequential inclusion and overlapping calls can collide. The beta.14 per-call declarations omit the runtime-supported lane fields.
- **create-wdk-module** ([v1.0.0-beta.3](https://github.com/tetherto/create-wdk-module/releases/tag/v1.0.0-beta.3)): Add the `sda` scaffold type and `wdk-protocol-sda-` template. The generated provider contains empty method stubs and todo-only tests, so implement both required methods and implement or remove every optional override before publishing.
- **wdk-utils** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.11)): [Breaking] Add CAIP-2-aware `validateAddress()` dispatch for Bitcoin, EVM, Solana, Spark, and Tron. Bitcoin success results replace `network` with `compatibleNetworks` and rename mainnet to `bitcoin`; Spark successes also add `compatibleNetworks`.
**Fixes**
- **worklet-bundler** ([v1.0.0-beta.7](https://github.com/tetherto/wdk-worklet-bundler/releases/tag/v1.0.0-beta.7)): Check root, nested, scoped, and symlinked package trees before deferring an optional peer, preventing installed dependencies from being omitted and later failing with `MODULE_NOT_FOUND`. Public exports and configuration signatures are unchanged.
**Changes**
- **wallet-btc** ([v1.0.0-beta.12](https://github.com/tetherto/wdk-wallet-btc/releases/tag/v1.0.0-beta.12)): Publish a version-only source update plus a repository lockfile refresh from `valibot` 1.4.1 to 1.4.2. Runtime source, declarations, declared dependencies, and the Node.js requirement are unchanged.
- **wallet-ton** ([v1.0.0-beta.12](https://github.com/tetherto/wdk-wallet-ton/releases/tag/v1.0.0-beta.12)): Publish a version-only source update plus a development lockfile refresh from `fast-uri` 3.1.2 to 3.1.4. Runtime source, declarations, declared dependencies, and existing mainnet endpoint caveats are unchanged.
- **wallet-ton-gasless** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-ton-gasless/releases/tag/v1.0.0-beta.8)): Publish a version-only source update plus a development lockfile refresh from `fast-uri` 3.1.2 to 3.1.4. Runtime source, declarations, declared dependencies, and existing mainnet endpoint caveats are unchanged.
- **swidge-symbiosis** ([v1.2.0](https://github.com/symbiosis-finance/wdk-protocol-swidge-symbiosis/tree/v1.2.0)): Add a 30-second default API timeout and configurable `X-Partner-Id`, validate a positive integer input amount, reset non-zero insufficient EVM allowances before approval, and recognize partner fees by their `Partner fee` description. TON joins Tron and Solana as quote-only because WDK TON accounts cannot submit the raw BoC route payload; Monero and Zcash custodial routes are excluded from discovery and resolution.
- **swidge-symbiosis** ([v1.1.2](https://github.com/symbiosis-finance/wdk-protocol-swidge-symbiosis/tree/v1.1.2)): Move publishing to version-tagged npm trusted publishing without changing the public module API.
---
### July 29, 2026
**What's New**
- **rgb-lightning** ([v0.1.0-beta.15](https://github.com/UTEXO-Protocol/wdk-rgb-lightning/releases/tag/v0.1.0-beta.15)): Add the community-maintained RGB Lightning wallet and node module for BTC and RGB balances, peers and channels, Lightning and RGB invoices and payments, LSP and Lightning Address flows, APay, and VSS backup. The beta requires one platform-specific native peer, persistent node state, and an explicit unlock step before account operations.
**Changes**
- **lending-aave-evm** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-protocol-lending-aave-evm/releases/tag/v1.0.0-beta.5)): Refresh the wallet and ethers dependencies without changing the Aave adapter's public runtime or type surface. ERC-4337 integrations must now provide the dependency's required `safeModulesVersion`; token-paymaster mode also requires a paymaster address, URL, and token configuration.
---
### July 28, 2026
**What's New**
- **swidge-symbiosis** ([v1.1.1](https://github.com/symbiosis-finance/wdk-protocol-swidge-symbiosis/tree/v1.1.1)): Add typed provider errors, expose named `SymbiosisProtocol` and `ISwidgeProtocol` exports alongside the default export, and map Symbiosis status code `2` to WDK `pending`.
---
### July 27, 2026
**What's New**
- **wallet-solana-gasless** ([v1.0.0-beta.2](https://github.com/tetherto/wdk-wallet-solana-gasless/releases/tag/v1.0.0-beta.2)): Let owned accounts quote and submit the fully signed transaction returned by `signTransaction()`. Quoting decodes the embedded paymaster-token fee without broadcasting; sending applies `transactionMaxFee` and submits the exact base64 wire transaction through Solana RPC without contacting the paymaster again. The signed payload keeps its existing blockhash or durable-nonce lifetime and is not refreshed or re-signed.
- **WDK CLI** ([v1.0.0-beta.1](https://github.com/tetherto/wdk-cli/releases/tag/v1.0.0-beta.1)): Introduce the public `@tetherto/wdk-cli` beta with the `wdk` command-line interface, a local wallet daemon, and a bundled MCP server. The release supports encrypted named wallets, multi-network reads and sends, custom networks and tokens, indexer history, MoonPay buy/sell links, and MCP setup for supported AI tools.
---
### July 24, 2026
**What's New**
- **bridge-usdt0-evm** ([v1.0.0-beta.7](https://github.com/tetherto/wdk-protocol-bridge-usdt0-evm/releases/tag/v1.0.0-beta.7)): Extend the ERC-4337 transaction-value helper to Ethereum, Plasma, and Polygon in addition to Arbitrum. On supported helper routes, an ERC-4337 bridge call bundles approval and bridging into one UserOperation instead of requiring a separate `approve()` submission. ERC-4337 network-fee units depend on the selected paymaster mode, while the bridge fee remains in bridged-token base units; use `bridgeMaxFee` only where those denominations are coherent, and note that the release rejects a total equal to the cap.
- **react-native-core** ([v1.0.0-beta.15](https://github.com/tetherto/wdk-react-native-core/releases/tag/v1.0.0-beta.15)): Add `swidge` to `useProtocol()` and align the worklet dependency with Pear Worklet beta.10. The npm artifact still omits the declared default `dist/index.js` and `dist/index.d.ts` files, so only resolvers that select the React Native source condition can load the published package as declared.
---
### July 23, 2026
**What's New**
- **pear-wrk-wdk** ([v1.0.0-beta.10](https://github.com/tetherto/pear-wrk-wdk/releases/tag/v1.0.0-beta.10)): Add `swidge` routing to the shared `callMethod()` handler for both HRPC and JSON-RPC. Pass `protocolName` in the call options so the worklet can resolve the requested account-level Swidge provider.
- **wallet-evm** ([v1.0.0-beta.16](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.16)): Widen the declared writable quote and send inputs to accept serialized transaction strings. Quoting a serialized transaction is non-broadcasting and uses its parsed fields. Do not pass a serialized transaction to `sendTransaction()` in this release: the runtime repopulates and signs a new transaction rather than broadcasting the supplied bytes.
**Changes**
- **wallet-evm-erc-4337** ([v1.0.0-beta.13](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.13)): Add Hardhat and regression-test coverage without changing the beta.12 production API or runtime behavior.
- **lending-morpho-evm** ([v1.0.5](https://github.com/morpho-org/sdks/releases/tag/%40morpho-org/wdk-protocol-lending-morpho-evm-v1.0.5)): Raise `@morpho-org/morpho-sdk` from `^5.3.2` to `^5.4.0` without changing the WDK adapter's compiled JavaScript, declarations, exports, configuration, or README.
---
### July 22, 2026
**What's New**
- **wdk-utils** ([v1.0.0-beta.10](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.10)): Add structural Solana address validation plus `splitMnemonic()` and `combineMnemonic()` for threshold-based English BIP-39 recovery shares. Shares are unencrypted sensitive material, and their embedded checksum detects accidental corruption rather than authenticating participants. React Native must provide secure `crypto.getRandomValues` before encryption or share generation.
**Fixes**
- **wallet-evm-7702-gasless** ([v1.0.0-beta.2](https://github.com/tetherto/wdk-wallet-evm-7702-gasless/releases/tag/v1.0.0-beta.2)): Check the current EntryPoint v0.8 account nonce before consuming a cached quote, and rebuild the UserOperation when the cached nonce is stale. Cache identity still excludes per-call fee-mode configuration, so use the same fee-mode and paymaster settings for a quote and its matching send or transfer.
- **failover-provider** ([v1.0.0-beta.3](https://github.com/tetherto/wdk-failover-provider/releases/tag/v1.0.0-beta.3)): Fix getter forwarding, proxy invariants, callable-thenable detection, and concurrent provider switching for synchronous and asynchronous reads. The proxy still forwards reads and method calls only, not writes, setters, enumeration, or reflection.
**Changes**
- **fiat-moonpay** ([v1.0.0-beta.3](https://github.com/tetherto/wdk-protocol-fiat-moonpay/releases/tag/v1.0.0-beta.3)): Refresh package and publishing dependencies without changing the public MoonPay runtime API. The current config accepts `apiKey`, optional backend `signUrl`, `cacheTime`, and `environment`; it does not accept `secretKey`.
- **react-native-secure-storage** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-react-native-secure-storage/releases/tag/v1.0.0-beta.5)): Publish dependency and workflow maintenance with no source API change. The npm artifact omits the `dist/` JavaScript and declaration files still referenced by its default and type entrypoints; pin beta.4 unless the consuming resolver explicitly uses the React Native source entry.
---
### July 21, 2026
**What's New**
- **wdk-wallet** ([v1.0.0-beta.15](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.15)): Export `UnsupportedOperationError`, `ValueError`, and `NoSuchElementError` from the base wallet package.
---
### July 20, 2026
**What's New**
- **wallet-aptos** ([v1.0.0-beta.1](https://github.com/tetherto/wdk-wallet-aptos/releases/tag/v1.0.0-beta.1)): Introduce Aptos wallets with hardened SLIP-0010 Ed25519 derivation, APT and fungible-asset balances and transfers, provider failover, provider-backed transaction preparation and signing, message signatures, and read-only accounts.
- **p2p-address-book** ([v1.0.0-beta.2](https://github.com/tetherto/wdk-p2p-address-book/releases/tag/v1.0.0-beta.2)): Publish the encrypted, multi-writer P2P wallet contact store and make replication startup asynchronous so local initialization no longer waits for the initial swarm flush. Treat construction as local readiness rather than proof that peers are connected or records have converged.
---
### July 16, 2026
**What's New**
- **wallet-evm-erc-4337** ([v1.0.0-beta.12](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.12)): Let writable accounts quote and submit a signed EntryPoint v0.7 `UserOperationV7`. Submission preserves its baked nonce and gas fields and skips a fresh `transactionMaxFee` check, so accept only a trusted operation prepared for the same account and configuration and submit it promptly.
- **wallet-solana** ([v1.0.0-beta.12](https://github.com/tetherto/wdk-wallet-solana/releases/tag/v1.0.0-beta.12)): Let writable accounts quote a fully signed transaction without broadcasting and send its exact serialized wire payload after applying `transactionMaxFee`. A signed transaction seals its recent blockhash or durable-nonce state, so submit it within the relevant validity window.
- **wallet-ton** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-wallet-ton/releases/tag/v1.0.0-beta.11)): Let writable accounts quote and submit the signed transfer-body `Cell` returned by `signTransaction()`. The cell seals the wallet sequence number; rebuild it after intervening account activity.
- **wallet-tron** ([v1.0.0-beta.9](https://github.com/tetherto/wdk-wallet-tron/releases/tag/v1.0.0-beta.9)): Export `TronSignedTransaction` and let writable accounts quote it without broadcasting or submit the exact signed transaction through TronWeb after applying `transactionMaxFee`.
- **worklet-bundler** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-worklet-bundler/releases/tag/v1.0.0-beta.6)): Defer missing optional peer dependencies through `bare-pack --defer` by default. Use `--no-defer-optional-peers` or `deferOptionalPeers: false` for a strict build; otherwise an optional feature can build successfully and fail later when its deferred peer is first required.
- **@lifi/wdk-protocol-swidge-lifi** ([v0.5.1](https://www.npmjs.com/package/@lifi/wdk-protocol-swidge-lifi/v/0.5.1)): Map each quoted fee to its LI.FI cost-token chain, and omit the optional chain when LI.FI does not provide one instead of assigning every fee to the source chain.
**Fixes**
- **react-native-core** ([v1.0.0-beta.14](https://github.com/tetherto/wdk-react-native-core/releases/tag/v1.0.0-beta.14)): Align Pear Worklet to beta.9 so the beta.13 generic-module HRPC methods are available, and move `react-native-bare-kit` to an explicit peer dependency that applications must install.
**Changes**
- **asset-registry** ([v1.0.0-beta.2](https://github.com/tetherto/wdk-asset-registry/releases/tag/v1.0.0-beta.2)): [Breaking] Make `getTokenByAddress()` case-sensitive by default. Pass `{ caseSensitive: false }` for normalized case-insensitive address lookup; symbol and chain helpers remain case-insensitive by default.
---
### July 09, 2026
**What's New**
- **pear-wrk-wdk** ([v1.0.0-beta.9](https://github.com/tetherto/pear-wrk-wdk/releases/tag/v1.0.0-beta.9)): Add HRPC-only generic-module construction, method calls, lifecycle handling, and host events, plus a separate length-prefixed JSON-RPC server entrypoint for native hosts. JSON-RPC supports the existing WDK, wallet, protocol, and secret operations but not wallet resets or generic modules in this release. Production logging now defaults to ERROR, JSC object logging is serialized safely, and temporary secret-buffer cleanup is improved.
- **wdk-utils** ([v1.0.0-beta.9](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.9)): Add `deriveSeedKey()` for domain-separated HKDF-SHA256 byte keys and `deriveSeedKeyPair()` for deterministic Ed25519 keypairs. Both require caller-supplied `salt` and `info` values and expect high-entropy seed bytes rather than mnemonic words.
**Changes**
- **wallet-tron-gasfree** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-tron-gasfree/releases/tag/v1.0.0-beta.8)): Add `transactionMaxFee` to the shared config type while native quote, sign, and send methods remain unsupported, so the field has no runtime enforcement path. GasFree TRC20 transfers continue to use per-call `transferMaxFee`, and provider transfer and activation fee fields are converted to `bigint` before addition.
---
### July 08, 2026
**What's New**
- **wdk-wallet** ([v1.0.0-beta.14](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.14)): Add optional `minAmountOut` to shared swap and swidge options, in destination-token base units, and forward it through the Swidge swap adapters. The base package does not validate or enforce the minimum; concrete provider behavior remains provider-defined.
- **wallet-btc** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-wallet-btc/releases/tag/v1.0.0-beta.11)): Let writable Bitcoin accounts quote and broadcast signed raw transaction hex. Signed-hex quotes fetch referenced previous transactions through the configured client without broadcasting, while signed-hex sends broadcast the exact payload and apply `transactionMaxFee` when configured. Read-only accounts still quote transaction objects only. An updated descriptor dependency raises the Node.js minimum to 20.19.0.
- **react-native-core** ([v1.0.0-beta.13](https://github.com/tetherto/wdk-react-native-core/releases/tag/v1.0.0-beta.13)): Add the `useModule()` hook, `ModuleService`, generic-module event subscriptions, and runtime `WdkConfigs.modules`. The published package still pins Pear Worklet beta.8, which lacks the required module HRPC methods, so the new API is not runnable through the default dependency graph. The React Native source entry is present, but the declared default JavaScript and type outputs under `dist/` are missing from this tag.
- **worklet-bundler** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-worklet-bundler/releases/tag/v1.0.0-beta.5)): Restore the built CLI and API files missing from beta.4, making the published HRPC/JSON-RPC transport, HRPC generic-module config, native addon linking, `addons.yml`, and ESM-to-CJS options available. Native linking now includes `bare-posix` automatically.
**Fixes**
- **wdk-core** ([v1.0.0-beta.14](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.14)): Export the type-only `WdkAccount` intersection and use it as the declared return type of `getAccount()` and `getAccountByPath()`, so writable wallet and protocol methods are represented together. Runtime account behavior is unchanged.
---
### July 07, 2026
**What's New**
- **swidge-rhinofi** ([v1.0.0-beta.2](https://www.npmjs.com/package/@rhino.fi/wdk-protocol-swidge-rhinofi/v/1.0.0-beta.2)): Introduce the Rhino.fi swidge provider for authenticated cross-chain quotes, EVM source-chain execution, live chain and token discovery, status polling, fee-cap checks, and typed Rhino.fi error handling.
**Changes**
- **pricing-bitfinex-http** ([v1.0.0-beta.4](https://github.com/tetherto/wdk-pricing-bitfinex-http/releases/tag/v1.0.0-beta.4)): Remove the USD-pivot fallback from Bitfinex price lookups. Current-price and price-data methods now return `null` for pairs Bitfinex cannot quote directly, while historical lookups return an empty series when Bitfinex has no matching history.
---
### July 04, 2026
**Changes**
- **wdk-utils** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.8)): Refresh Noble crypto dependencies and import paths used by address validation, Lightning invoice helpers, BIP-21 parsing, and seed-encryption internals. No new public helper API is introduced in this release.
---
### July 03, 2026
**What's New**
- **wallet-evm** ([v1.0.0-beta.15](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.15)): Add signer-backed EVM accounts with the new `@tetherto/wdk-wallet-evm/signers` entrypoint, `SeedSignerEvm`, `PrivateKeySignerEvm`, signer-based account retrieval, standalone private-key accounts via `WalletAccountEvm.fromPrivateKey()`, contract-creation transactions with omitted or `null` `to`, and ERC-7702 authorization/delegation helpers. The release also updates `ethers` to `6.17.0` and aligns with `@tetherto/wdk-wallet` v1.0.0-beta.13.
- **wallet-tron** ([v1.0.0-beta.8](https://github.com/tetherto/wdk-wallet-tron/releases/tag/v1.0.0-beta.8)): Add arbitrary transaction support. `quoteSendTransaction()`, `signTransaction()`, and `sendTransaction()` now accept native TRX transfers, smart-contract call descriptors, or pre-built TronWeb transactions. Fee quotes cover bandwidth, smart-contract energy, and activation fees where applicable.
**Fixes**
- **wdk-wallet** ([v1.0.0-beta.13](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.13)): Default `IWalletAccount` to `unknown`, restoring bare `IWalletAccount` TypeScript compatibility for downstream packages without runtime behavior changes.
- **wallet-evm-erc-4337** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.11)): Fix UserOperation signing compatibility with the `@tetherto/wdk-wallet-evm` v1.0.0-beta.15 signer model and align dependencies with `@tetherto/wdk-wallet` v1.0.0-beta.13 and `ethers` 6.17.0.
**Changes**
- **worklet-bundler** ([v1.0.0-beta.4](https://github.com/tetherto/wdk-worklet-bundler/releases/tag/v1.0.0-beta.4)): Publish source changes for JSON-RPC transport, native addon linking, ESM-to-CJS conversion, generic `modules` config support, lazy wallet-module loading, and the generated `./.wdk` import path. The npm artifact for this tag is missing built CLI/API files, so the Worklet Bundler usage pages remain on the prior documented workflow until a fixed package is available.
- **wdk-core** ([v1.0.0-beta.13](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.13)): Align the core package with `@tetherto/wdk-wallet` v1.0.0-beta.13. No new core runtime API changes were found in this tag diff.
- **bridge-usdt0-evm** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-protocol-bridge-usdt0-evm/releases/tag/v1.0.0-beta.6)): Align dependencies with the latest base wallet, EVM wallet, and ERC-4337 wallet releases. No production bridge API changes were found in this tag diff.
---
### July 01, 2026
**What's New**
- **wallet-evm-erc-4337** ([v1.0.0-beta.10](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.10)): Add `transactionMaxFee` for non-sponsored `sendTransaction()` and `signTransaction()` UserOperation flows, separate from `transferMaxFee` for token transfers. The module also reserves local nonces for rapid or concurrent sends from the same account instance and releases them when a submission fails before bundler acceptance.
- **react-native-core** ([v1.0.0-beta.12](https://github.com/tetherto/wdk-react-native-core/releases/tag/v1.0.0-beta.12)): Add `useProtocol()` for calling bridge, swap, lending, and fiat protocol methods from React Native through the active WDK worklet account.
---
### June 30, 2026
**Changes**
- **swap-velora-evm** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-protocol-swap-velora-evm/releases/tag/v1.0.0-beta.6)): Document the optional ERC-4337 per-call config argument for `swap()` and `quoteSwap()`, plus the `swap()` per-call `swapMaxFee` override. The package also refreshes dependency and repository metadata.
---
### June 29, 2026
**What's New**
- **wdk-wallet** ([v1.0.0-beta.12](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.12)): Remove `signTransaction()` from the base `ISigner` interface. Transaction signing remains an account-level wallet operation, and the base account types now let modules accept unsigned or module-specific signed payloads in `sendTransaction()` and `quoteSendTransaction()` where supported.
- **wallet-solana** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-wallet-solana/releases/tag/v1.0.0-beta.11)): Add `transactionMaxFee` for native SOL `sendTransaction()` and `signTransaction()` flows. The Solana module keeps `transferMaxFee` scoped to SPL token transfers, and fee caps reject estimated fees above the configured cap.
**Changes**
- **wallet-ton-gasless** ([v1.0.0-beta.7](https://github.com/tetherto/wdk-wallet-ton-gasless/releases/tag/v1.0.0-beta.7)): Add `transactionMaxFee` to the shared gasless wallet config typing for base-wallet alignment while `sendTransaction()`, `quoteSendTransaction()`, and `signTransaction()` remain unsupported on the gasless module. Gasless Jetton transfers continue to use `transferMaxFee`, fee caps reject estimated fees above the configured cap, and the package refreshes its TON wallet and security dependency set.
---
### June 25, 2026
**Changes**
- **wallet-spark** ([v1.0.0-beta.22](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.22)): Keep Spark send fee behavior unchanged after the beta.21 package. Spark does not expose `transactionMaxFee` because the module does not charge a configurable chain fee for Spark sends.
---
### June 23, 2026
**What's New**
- **wallet-btc** ([v1.0.0-beta.10](https://github.com/tetherto/wdk-wallet-btc/releases/tag/v1.0.0-beta.10)): Add `transactionMaxFee` to cap fees for BTC `sendTransaction()` and `signTransaction()` operations.
- **wallet-evm** ([v1.0.0-beta.14](https://github.com/tetherto/wdk-wallet-evm/releases/tag/v1.0.0-beta.14)): Add `transactionMaxFee` for native EVM `sendTransaction()` and provider-backed `signTransaction()` flows, separate from `transferMaxFee` for token transfers.
- **wallet-ton** ([v1.0.0-beta.10](https://github.com/tetherto/wdk-wallet-ton/releases/tag/v1.0.0-beta.10)): Add `transactionMaxFee` for TON `sendTransaction()` and `signTransaction()` flows, and refresh the `form-data` dependency.
- **wallet-tron** ([v1.0.0-beta.7](https://github.com/tetherto/wdk-wallet-tron/releases/tag/v1.0.0-beta.7)): Add `transactionMaxFee` for native TRX `sendTransaction()` and `signTransaction()` flows, keep TRC20 `transferMaxFee` separate, and return `activationFee` from native send quotes and results.
**Changes**
- **wallet-spark** ([v1.0.0-beta.21](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.21)): Publish an intermediate Spark package update before the beta.22 follow-up. Use beta.22 as the current Spark baseline.
---
### June 20, 2026
**What's New**
- **pricing-coingecko-http** ([v1.0.0-beta.1](https://github.com/tetherto/wdk-pricing-coingecko-http/releases/tag/v1.0.0-beta.1)): Introduce `@tetherto/wdk-pricing-coingecko-http`, a CoinGecko-backed `PricingClient` with current price lookups, batched current prices, price data with derived 24-hour change, historical price ranges with optional downsampling, Demo/Pro API key support, and configurable CoinGecko ID mappings.
---
### June 19, 2026
**What's New**
- **wdk-utils** ([v1.0.0-beta.7](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.7)): Add passphrase-based seed encryption helpers with AES-256-GCM, scrypt key derivation, `encrypt()`, `decrypt()`, `deriveKey()`, `decryptWithKey()`, and persisted scrypt cost parameters on encrypted payloads.
- **wallet-spark** ([v1.0.0-beta.20](https://github.com/tetherto/wdk-wallet-spark/releases/tag/v1.0.0-beta.20)): Add the `enableLogging` wallet config option for Spark SDK logging, update the Spark SDK and Bare dependencies, and avoid creating duplicate Spark wallet instances during account setup.
**Fixes**
- **wallet-tron-gasfree** ([v1.0.0-beta.7](https://github.com/tetherto/wdk-wallet-tron-gasfree/releases/tag/v1.0.0-beta.7)): Return `activationFee` alongside `fee` from gas-free transfer quotes and transfer results, so apps can show the activation portion separately when the GasFree account is not active yet.
---
### June 18, 2026
**What's New**
- **wdk-wallet** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.11)): Add the base `ISigner` interface, `SignerError`, default-signer construction, named signer registration, signer lookup helpers, and signer-aware account retrieval hooks for modules that support external signing.
**Fixes**
- **wdk-core** ([v1.0.0-beta.12](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.12)): Harden transaction policy enforcement by snapshotting governed method arguments once, evaluating policies against that snapshot, forwarding the same approved values to the wallet method, and failing closed with `PolicyConfigurationError` for non-cloneable governed arguments.
- **wallet-solana** ([v1.0.0-beta.10](https://github.com/tetherto/wdk-wallet-solana/releases/tag/v1.0.0-beta.10)): Align the Solana `keyPair` behavior and types so key arrays are documented as read-only views, while `keyPair.privateKey` still returns `null` after `dispose()` clears the internal private key.
---
### June 14, 2026
**What's New**
- **wdk-utils** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.6)): Add BIP-21 Bitcoin payment URI helpers, including `isBip21Request()`, `parseBip21Request()`, and `encodeBip21Request()` with Bitcoin address validation, optional `amount`, `label`, and `message` fields, and unsupported required-parameter errors.
- **react-native-core** ([v1.0.0-beta.11](https://github.com/tetherto/wdk-react-native-core/releases/tag/v1.0.0-beta.11)): Add `useBalancesForWallets()` for fetching balances across multiple account indices, preload addresses for every requested account/network pair, and return per-account token failures without failing the whole query.
---
### June 12, 2026
**What's New**
- **wdk-core** ([v1.0.0-beta.11](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.11)): Add local transaction policies with `registerPolicy()`, `PolicyViolationError`, `PolicyConfigurationError`, scoped ALLOW/DENY rules for wallet account and protocol write methods, and `account.simulate.*` mirrors for dry-run policy evaluation.
- **wdk-asset-registry** ([v1.0.0-beta.1](https://github.com/tetherto/wdk-asset-registry/releases/tag/v1.0.0-beta.1)): Introduce `@tetherto/wdk-asset-registry` with in-memory base and token asset registries, Zod-backed `BaseAsset` and `TokenAsset` schemas, JSON schema exports, Uniswap token-list normalization helpers, and bundled `common-tokens` metadata.
- **wallet-evm-7702-gasless** ([v1.0.0-beta.1](https://github.com/tetherto/wdk-wallet-evm-7702-gasless/releases/tag/v1.0.0-beta.1)): Introduce `@tetherto/wdk-wallet-evm-7702-gasless` for EIP-7702 delegated EVM accounts with ERC-4337 UserOperation submission, sponsored and paymaster-token fee modes, provider failover, quote helpers, and UserOperation receipt lookup.
---
### June 10, 2026
**What's New**
- **wdk-wallet** ([v1.0.0-beta.10](https://github.com/tetherto/wdk-wallet/releases/tag/v1.0.0-beta.10)): Add `transactionMaxFee` to the base `WalletConfig` type so wallet modules can expose separate fee caps for `sendTransaction()` / `signTransaction()` flows and token `transfer()` flows.
- **wallet-tron** ([v1.0.0-beta.6](https://github.com/tetherto/wdk-wallet-tron/releases/tag/v1.0.0-beta.6)): Add ordered Tron provider failover through `provider` arrays and `retries`, expose `keyPair` as a read-only view of the account keys, and return more precise TRX/TRC20 fee quotes including TRX activation fee details.
**Fixes**
- **wallet-evm-erc-4337** ([v1.0.0-beta.9](https://github.com/tetherto/wdk-wallet-evm-erc-4337/releases/tag/v1.0.0-beta.9)): Validate cached quote nonces at send time before reusing a quoted UserOperation, re-quoting when the on-chain nonce has moved, and propagate UserOperation gas overrides through `transfer()`, `quoteTransfer()`, and `approve()`.
---
### June 09, 2026
**What's New**
- **pricing-bitfinex-http** ([v1.0.0-beta.3](https://github.com/tetherto/wdk-pricing-bitfinex-http/releases/tag/v1.0.0-beta.3)): Add batch FX conversion through Bitfinex's `/calc/fx/batch` endpoint, including USD-pivot fallback for fiat pairs Bitfinex does not quote directly, plus `getMultiCurrentPrices()` and `getMultiPriceData()` coverage.
**Changes**
- **pricing-provider** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-pricing-provider/releases/tag/v1.0.0-beta.5)): Mark unresolved current-price and batch-price results as nullable in the `PricingClient` base type so custom clients can return `null` for unsupported pairs without losing type information.
---
### June 04, 2026
**What's New**
- **wdk-utils** ([v1.0.0-beta.5](https://github.com/tetherto/wdk-utils/releases/tag/v1.0.0-beta.5)): Restore BOLT11 invoice support with exported `validateLightningInvoice()`, `decode()`, `getHashToSign()`, `sign()`, and `encode()` helpers, including fallback address, routing info, feature bits, and `lightning:` prefix handling.
- **wdk-core** ([v1.0.0-beta.10](https://github.com/tetherto/wdk/releases/tag/v1.0.0-beta.10)): Add swidge protocol support to global and account-level `registerProtocol()` calls, expose `getSwidgeProtocol()`, preserve account-scoped protocols across repeated account lookups, and reject duplicate `registerWallet()` calls until the existing wallet is disposed.
---
### 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 modules](/sdk/swidge-modules)**: Add a catalog for released swap, bridge, and combined-route provider modules.
---
### 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-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-erc-4337` - EVM chains with ERC-4337 standard
- `@tetherto/wdk-wallet-ton-gasless` - TON blockchain with gasless Jetton transfers
- `@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
### Seed Lifecycle
WDK uses the seed provided by your app, but it does not decide where the seed is stored or when the seed should be cleared. Treat the seed as app-owned material: decrypt or load it only when needed, use WDK for the wallet session, call [`dispose()`](/sdk/core-module/guides/error-handling#seed-lifecycle), and clear your own seed buffer when the session ends.
If your app needs explicit cleanup, pass seed bytes in a mutable `Uint8Array` where possible. JavaScript strings cannot be reliably zeroed.
### 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 can clear private keys they manage with [`dispose()`](/sdk/core-module/guides/error-handling#seed-lifecycle)
## 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 available WDK wallet, pricing, swidge, swap, bridge, lending, and fiat interfaces and 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`](https://github.com/tetherto/wdk) | 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-erc-4337`](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-evm-7702-gasless`](https://github.com/tetherto/wdk-wallet-evm-7702-gasless) | EVM | EIP-7702 gasless account abstraction for EVM chains | [Docs](/sdk/wallet-modules/wallet-evm-7702-gasless/) |
| [`@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 Jetton transfers 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-solana-gasless`](https://github.com/tetherto/wdk-wallet-solana-gasless) | Solana | Gasless Solana transactions through a Kora-compatible paymaster | [Docs](/sdk/wallet-modules/wallet-solana-gasless/) |
| [`@tetherto/wdk-wallet-aptos`](https://github.com/tetherto/wdk-wallet-aptos) | Aptos | Aptos blockchain wallet with native APT and fungible asset support | [Docs](/sdk/wallet-modules/wallet-aptos/) |
| [`@tetherto/wdk-wallet-spark`](https://github.com/tetherto/wdk-wallet-spark) | Spark | Spark/Lightning Bitcoin L2 wallet | [Docs](/sdk/wallet-modules/wallet-spark/) |
## Swidge Modules
Swidge providers can quote and execute asset routes. A route can be swap-only, bridge-only, or a combined swap and bridge route.
Rows marked Community 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.
| Module | Provider | Ownership | Description | Documentation |
|--------|----------|-----------|-------------|---------------|
| [`wdk-protocol-swidge-orchestra`](https://www.npmjs.com/package/wdk-protocol-swidge-orchestra) | Flashnet Orchestra | Community | Swidge provider for BTC and stablecoin routes returned by Orchestra. Treat the live [Orchestra route matrix](https://orchestration.flashnet.xyz/v1/orchestration/routes) as provider-level data and filter through package discovery before exposing routes. | [Docs](/sdk/swidge-modules/swidge-orchestra/) |
| [`@rhino.fi/wdk-protocol-swidge-rhinofi`](https://www.npmjs.com/package/@rhino.fi/wdk-protocol-swidge-rhinofi) | Rhino.fi | Community | Swidge routes for Rhino.fi cross-chain swap and bridge operations | [Docs](/sdk/swidge-modules/swidge-rhinofi/) |
| [`@symbiosis-finance/wdk-protocol-swidge-symbiosis`](https://www.npmjs.com/package/@symbiosis-finance/wdk-protocol-swidge-symbiosis) | Symbiosis | Community | Runtime-discovered exact-input quotes with EVM and Bitcoin source execution through the Symbiosis API | [Docs](/sdk/swidge-modules/swidge-symbiosis/) |
| [`@lifi/wdk-protocol-swidge-lifi`](https://www.npmjs.com/package/@lifi/wdk-protocol-swidge-lifi) | LI.FI | Community | Swidge routes for LI.FI swap, bridge, and combined swap-plus-bridge operations | [Docs](/sdk/swidge-modules/swidge-lifi/) |
## Pricing Modules
Pricing modules provide `PricingClient` implementations for market data sources.
| Module | Provider | Description | Documentation |
|--------|----------|-------------|---------------|
| [`@tetherto/wdk-pricing-coingecko-http`](https://github.com/tetherto/wdk-pricing-coingecko-http) | CoinGecko | CoinGecko HTTP pricing client for current prices, price data, and historical series | [Docs](/sdk/pricing-modules/pricing-coingecko-http/) |
| [`@tetherto/wdk-pricing-bitfinex-http`](https://github.com/tetherto/wdk-pricing-bitfinex-http) | Bitfinex | Bitfinex HTTP pricing client for current prices, batched price data, and historical series | [Docs](/tools/price-rates/) |
## 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.
Rows marked Community 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.
| Module | Blockchain | Ownership | Description | Documentation |
|--------|------------|-----------|-------------|---------------|
| [`@tetherto/wdk-protocol-lending-aave-evm`](https://github.com/tetherto/wdk-protocol-lending-aave-evm) | EVM | Tether | 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.
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.
| Module | Category | Description | Documentation |
|--------|----------|-------------|---------------|
| [`@utexo/wdk-wallet-rgb`](https://github.com/UTEXO-Protocol/wdk-wallet-rgb) | Wallet | RGB protocol wallet integration | [Docs](/sdk/community-modules/wdk-wallet-rgb/) |
| [`@utexo/wdk-rgb-lightning`](https://www.npmjs.com/package/@utexo/wdk-rgb-lightning) | Wallet | RGB Lightning node and wallet integration | [Docs](/sdk/community-modules/wdk-rgb-lightning/) |
| [`@base58-io/wdk-wallet-cosmos`](https://github.com/base58-io/wdk-wallet-cosmos) | Wallet | Cosmos-compatible wallet integration | [Docs](/sdk/community-modules/wdk-wallet-cosmos/) |
| [`@morpho-org/wdk-protocol-lending-morpho-evm`](https://www.npmjs.com/package/@morpho-org/wdk-protocol-lending-morpho-evm) | Lending | Morpho Vault V2 and Morpho Blue lending integration | [Docs](/sdk/lending-modules/lending-morpho-evm/) |
| [`wdk-protocol-swidge-orchestra`](https://github.com/flashnetxyz/wdk-protocol-swidge-orchestra) | Swidge | Flashnet Orchestra BTC and stablecoin route integration | [Docs](/sdk/swidge-modules/swidge-orchestra/) |
| [`@rhino.fi/wdk-protocol-swidge-rhinofi`](https://www.npmjs.com/package/@rhino.fi/wdk-protocol-swidge-rhinofi) | Swidge | Rhino.fi cross-chain route integration | [Docs](/sdk/swidge-modules/swidge-rhinofi/) |
| [`@symbiosis-finance/wdk-protocol-swidge-symbiosis`](https://www.npmjs.com/package/@symbiosis-finance/wdk-protocol-swidge-symbiosis) | Swidge | Same-chain and cross-chain exact-input routes through Symbiosis | [Docs](/sdk/swidge-modules/swidge-symbiosis/) |
| [`@lifi/wdk-protocol-swidge-lifi`](https://www.npmjs.com/package/@lifi/wdk-protocol-swidge-lifi) | Swidge | LI.FI swap and bridge route integration | [Docs](/sdk/swidge-modules/swidge-lifi/) |
***
## 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.
## 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
## Swidge provider routes
For new swap or bridge provider integrations, choose a released [Swidge provider module](/sdk/swidge-modules). Swidge can represent bridge-only routes, swap-only routes, and combined bridge-and-swap routes. Existing standalone bridge module references remain available for released modules that have not moved to Swidge.
For a released community provider, see [Orchestra](/sdk/swidge-modules/swidge-orchestra), which implements Swidge for BTC and stablecoin routes returned by Flashnet Orchestra.
***
## Bridge tokens with USD₮0
URL: https://docs.wdk.tether.io/sdk/bridge-modules/bridge-usdt0-evm
Description: Bridge USD₮0 and XAU₮0 across supported EVM and non-EVM destinations from WDK accounts.
Use the USD₮0 bridge module to move USD₮0 and XAU₮0 across supported chains from WDK EVM wallet accounts.
## 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**: Discover 25 configured EVM and non-EVM network keys
- **Non-EVM Destinations**: Bridge toward Solana, TON, and TRON when the source token has a compatible route contract
- **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 |
### Non-EVM route candidates
For auto-resolved routes toward Solana, TON, or TRON, beta.7 skips the source chain's ordinary `oftContract`. The bundled configuration can instead consider these source contracts:
| Source token contract family | EVM source chains with a bundled candidate |
|---|---|
| USD₮0 legacy mesh | Ethereum, Arbitrum, Celo |
| XAU₮0 OFT | Ethereum, Arbitrum, Avalanche, Celo, HyperEVM, Ink, Monad, Plasma, Polygon, Stable |
This table shows source-side contract availability, not a guarantee that every listed source reaches every non-EVM destination. The selected contract must support the destination endpoint on-chain. Confirm the exact source-token-destination route with `quoteBridge()` before execution, or provide a verified route-specific `oftContractAddress` and optional `dstEid`.
`getSupportedChains()` and `getSupportedTokens()` read the static chain and token configuration. They do not validate a source-to-destination pair or prove that its LayerZero peer is configured.
Token support is determined by the contracts deployed on each chain. The protocol checks for `oftContract`, `legacyMeshContract`, and `xautOftContract` to determine available tokens.
Standard EVM accounts can use every supported EVM source route with a matching token deployment. ERC-4337 helper bridging is available from Ethereum, Arbitrum, Plasma, and Polygon. The ERC-4337 flow bundles token approval and the helper call into one UserOperation.
ERC-4337 `fee` and `bridgeFee` values can use different denominations in `1.0.0-beta.7`. Review the [fee-unit limitation](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#fee-units-and-bridgemaxfee) before configuring `bridgeMaxFee`.
## 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): Rejects `bridge()` when the implementation's combined fee value is at or above this cap
**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 // Standard Ethereum account: source native base units
})
```
### Methods
| Method | Description | Returns | Throws |
|--------|-------------|---------|--------|
| `bridge(options, config?)` | Bridges tokens to another blockchain | `Promise` | If no provider or the combined fee is at or above the cap |
| `quoteBridge(options, config?)` | Estimates the cost of a bridge operation | `Promise>` | If no provider |
| `getSupportedChains()` | Returns chain descriptors from bundled configuration | `Promise` | — |
| `getSupportedTokens(options?)` | Returns configured USD₮0 or XAU₮0 descriptors, with optional filters | `Promise` | — |
#### `bridge(options, config?)`
Bridges tokens to a different blockchain using the USD₮0 protocol.
With a standard EVM account, approve the source-chain bridge spender before calling `bridge()`. If you pass `oftContractAddress`, use the same address as the approval `spender`. Supported ERC-4337 accounts do not need a separate approval call; the protocol bundles an approval and helper call into one UserOperation.
**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` (`Erc4337BridgeConfig`, optional): ERC-4337 gas-payment overrides plus optional `bridgeMaxFee`
- `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 the combined fee value is equal to or greater than `bridgeMaxFee`
**Example:**
```javascript
// Standard EVM account
const standardBridgeProtocol = new Usdt0ProtocolEvm(standardAccount)
await standardAccount.approve({
token: '0x...', // USDT contract address
spender: '0x...', // OFT or bridge spender address
amount: 1000000n
})
const result = await standardBridgeProtocol.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('Account transaction fee:', result.fee)
console.log('Bridge fee:', result.bridgeFee)
// ERC-4337 account: approval is bundled automatically
const erc4337BridgeProtocol = new Usdt0ProtocolEvm(erc4337Account)
const result2 = await erc4337BridgeProtocol.bridge({
targetChain: 'arbitrum',
recipient: '0x...', // Recipient address
token: '0x...', // USDT contract address
amount: 1000000n,
oftContractAddress: '0x...' // Optional custom OFT contract
}, {
paymasterToken: { address: '0x...' } // Paymaster token configuration
})
console.log('Bridge hash:', result2.hash) // Single hash for bundled operations
console.log('Account fee:', result2.fee)
console.log('Bridge fee:', result2.bridgeFee)
```
#### `quoteBridge(options, config?)`
Estimates the cost of a bridge operation without executing it.
For standard EVM accounts, 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()`. ERC-4337 quotes include the bundled approval transaction.
**Parameters:**
- `options` (BridgeOptions): Bridge operation options (same as bridge method)
- `config` (`Erc4337QuoteConfig`, optional): ERC-4337 gas-payment overrides
**Returns:** `Promise>` - Bridge cost estimate
**Throws:** Error if no provider is configured
**Standard-account example:**
```javascript
const quote = await bridgeProtocol.quoteBridge({
targetChain: 'polygon',
recipient: '0x...', // Recipient address
token: '0x...', // USDT contract address
amount: 1000000n
})
console.log('Estimated transaction fee:', quote.fee)
console.log('Bridge fee:', quote.bridgeFee)
// 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
})
}
```
#### `getSupportedChains()`
Returns the chain descriptors from the package's bundled configuration. This method does not make a network request.
```javascript
const chains = await bridgeProtocol.getSupportedChains()
// [{ id: 'ethereum', name: 'Ethereum', type: 'evm', nativeToken: 'ETH' }, ...]
```
#### `getSupportedTokens(options?)`
Returns USD₮0 and XAU₮0 descriptors for chains that have a matching bridge contract in bundled configuration. Optional `fromChain` or `toChain` filters accept a chain key, chain ID, or endpoint ID. `fromToken` filters by token symbol.
```javascript
const allTokens = await bridgeProtocol.getSupportedTokens()
const ethereumTokens = await bridgeProtocol.getSupportedTokens({
fromChain: 'ethereum'
})
const xaut0Tokens = await bridgeProtocol.getSupportedTokens({
fromToken: 'XAUT0'
})
```
The returned descriptors omit on-chain token addresses because this discovery method does not resolve them. Solana, TON, and TRON are destination-only entries and are not returned by `getSupportedTokens()`.
These discovery methods expose separate static chain and token lists. They do not prove that a source token has an on-chain peer for a destination. Confirm the exact pair with `quoteBridge()` before execution.
## 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; // Account quote fee; unit depends on account gas-payment mode
bridgeFee: bigint; // Standard: source native unit; ERC-4337 helper: bridged-token base units
}
```
### BridgeProtocolConfig
```typescript
interface BridgeProtocolConfig {
bridgeMaxFee?: number | bigint; // Reject when the implementation's fee + bridgeFee is at or above this value
}
```
```typescript
type Erc4337QuoteConfig = Partial<
| EvmErc4337WalletPaymasterTokenConfig
| EvmErc4337WalletSponsorshipPolicyConfig
| EvmErc4337WalletNativeCoinsConfig
>
type Erc4337BridgeConfig = Erc4337QuoteConfig & {
bridgeMaxFee?: number | bigint
}
```
### Fee units and `bridgeMaxFee`
| Account flow | `fee` | `bridgeFee` |
|---|---|---|
| Standard EVM | Source-chain native base units | Source-chain native base units |
| ERC-4337 with native gas | Source-chain native base units | Bridged-token base units |
| ERC-4337 with token-paid gas | Paymaster-token base units | Bridged-token base units |
| ERC-4337 with sponsored gas | `0` | Bridged-token base units |
In `1.0.0-beta.7`, the ERC-4337 path numerically adds `fee + bridgeFee` when checking `bridgeMaxFee`, even when the fields have different denominations. Do not interpret that sum as a total monetary cost. Set an ERC-4337 cap only after confirming that the selected account payment mode produces compatible units. The equality boundary is rejected.
`Erc4337QuoteConfig` is a partial operation-level override of the wallet's token-paid, sponsored, or native-gas configuration. Sponsored gas uses `isSponsored: true` and can include `sponsorshipPolicyId`; native gas uses `useNativeCoins: true`. See the [ERC-4337 wallet configuration](/sdk/wallet-modules/wallet-evm-erc-4337/configuration) for the complete account requirements.
### Discovery types
```typescript
type SwidgeSupportedChain = {
id: string | number
name: string
type: string
nativeToken: string
}
type SwidgeSupportedToken = {
token: string
chain: string | number
symbol: string
decimals: number
address?: string
name?: string
}
type SwidgeSupportedTokensOptions = {
fromChain?: string | number
fromToken?: string
toChain?: string | number
}
```
### Supported Chains
The bridge protocol supports the following chains:
**Source Chains (EVM):**
- `'ethereum'` (Chain ID: 1) - ERC-4337 helper support
- `'arbitrum'` (Chain ID: 42161) - ERC-4337 helper support
- `'optimism'` (Chain ID: 10)
- `'polygon'` (Chain ID: 137) - ERC-4337 helper support
- `'berachain'` (Chain ID: 80094)
- `'ink'` (Chain ID: 57073)
- `'plasma'` (Chain ID: 9745) - ERC-4337 helper support
- `'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)
**Configured destination keys:**
- **EVM destinations**: same as source-chain set above
- `'solana'` (EID: 30168)
- `'ton'` (EID: 30343)
- `'tron'` (EID: 30420)
The configured keys are not a Cartesian route matrix. Route execution still requires a matching source contract and a destination peer configured on-chain.
## 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)
// 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...' // Optional custom OFT contract
})
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/42161',
safeModulesVersion: '0.3.0',
paymasterUrl: 'https://api.candide.dev/public/v3/42161',
paymasterAddress: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
paymasterToken: { address: '0x...' } // Paymaster token configuration
})
// Create bridge protocol
const bridgeProtocol = new Usdt0ProtocolEvm(account)
// The protocol bundles approval and the helper call in one UserOperation.
const result = await bridgeProtocol.bridge({
targetChain: 'polygon',
recipient: '0x...', // Recipient address
token: '0x...', // USDT contract address
amount: 1000000n,
oftContractAddress: '0x...' // Optional custom OFT contract
}, {
paymasterToken: { address: '0x...' } // Paymaster token configuration
})
console.log('ERC-4337 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 standard-account cap in source native base units
})
```
## 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 rejects `bridge()` when the implementation's `fee + bridgeFee` value is equal to or greater than the cap.
**Type:** `number | bigint` (optional)
For a standard EVM account, both fields use the source chain's native base unit. For example, Ethereum and Arbitrum report the values in wei.
For an ERC-4337 helper flow, `bridgeFee` is in bridged-token base units. The account's `fee` uses native base units for native gas, paymaster-token base units for token-paid gas, or zero for sponsored gas. In `1.0.0-beta.7`, the protocol numerically adds these values when enforcing `bridgeMaxFee`. Do not treat that sum as one currency or set a cap until your payment mode uses compatible units.
**Examples:**
```javascript
const config = {
// Standard Ethereum account: reject fee + bridgeFee at or above 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
})
```
Pass either an RPC URL string or a genuine EIP-1193 provider. An ethers `JsonRpcProvider` is not an EIP-1193 provider and is not accepted by this wallet release.
## ERC-4337 Configuration
When using ERC-4337 accounts, you can override configuration options during bridge operations:
```javascript
// Bridge with ERC-4337 account
const result = await bridgeProtocol.bridge({
targetChain: 'arbitrum',
recipient: '0x...', // Recipient address
token: '0x...', // USDT contract address
amount: 1000000n,
oftContractAddress: '0x...' // Optional custom OFT contract
}, {
paymasterToken: { address: '0x...' } // Paymaster token for gasless transactions
})
```
The protocol builds the token approval and transaction-value-helper call and submits them as one UserOperation. Do not call `account.approve()` separately for this flow.
ERC-4337 helper bridging is configured for these source chains:
| Source chain | Chain ID |
|---|---:|
| Ethereum | 1 |
| Arbitrum | 42161 |
| Plasma | 9745 |
| Polygon | 137 |
Other supported EVM source chains require a standard EVM account.
### 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
const result = await bridgeProtocol.bridge({
targetChain: 'arbitrum',
recipient: '0x...', // Recipient address
token: '0x...', // USDT contract address
amount: 1000000n,
oftContractAddress: '0x...' // Optional custom OFT contract
}, {
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. The following allowance step applies to a standard EVM account:
```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. This example uses a standard EVM account, so it approves the bridge spender first:
```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 source tokens and native gas where required. Standard EVM accounts must approve the source-chain bridge spender before calling [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config). Supported ERC-4337 accounts bundle approval into the bridge UserOperation. Recipient strings must match each network’s address encoding.
The examples below use a standard EVM account. For `USDT_BRIDGE_SPENDER_ADDRESS`, use a verified source-chain OFT or bridge contract that supports the selected destination. See [Bridge Tokens](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens#prerequisites) for address sources.
## Verify the route
For Solana, TON, and TRON targets, beta.7 does not auto-resolve the source chain's ordinary USD₮0 OFT. Its bundled auto-resolution candidates are:
| Source token contract family | EVM source chains with a bundled candidate |
|---|---|
| USD₮0 legacy mesh | Ethereum, Arbitrum, Celo |
| XAU₮0 OFT | Ethereum, Arbitrum, Avalanche, Celo, HyperEVM, Ink, Monad, Plasma, Polygon, Stable |
A listed source contract can still lack an on-chain peer for a particular destination. `getSupportedChains()` and `getSupportedTokens()` expose static configuration, not a verified route matrix. After any required standard-account approval, call `quoteBridge()` for the exact source token and destination before calling `bridge()`. For another verified deployment, pass its route-specific `oftContractAddress` and, when needed, `dstEid`.
## 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 standard EVM accounts: [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('Transaction fee:', result.fee, 'source native base units')
console.log('Bridge fee:', result.bridgeFee, 'source native base units')
```
`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 transaction fee:', quote.fee, 'source native base units')
console.log('Bridge fee:', quote.bridgeFee, 'source native base units')
```
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 transaction fee:', customQuote.fee, 'source native base units')
```
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 a standard-account operation when `fee + bridgeFee` is equal to or greater than the cap:
```javascript title="Protocol-level bridgeMaxFee"
import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm'
const cappedBridge = new Usdt0ProtocolEvm(account, {
bridgeMaxFee: 1000000000000000n
})
```
For standard accounts, both values use source-chain native base units. ERC-4337 accounts can also pass `bridgeMaxFee` in the second argument to [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config), but their returned fee fields can use different denominations. See [Bridge with ERC-4337](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337) before applying that cap.
## 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).
* An ERC-4337 source chain with a configured transaction-value helper: Ethereum, Arbitrum, Plasma, or Polygon.
## 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/42161',
safeModulesVersion: '0.3.0',
paymasterUrl: 'https://api.candide.dev/public/v3/42161',
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)
```
## 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 an optional `bridgeMaxFee` override. Do not submit a separate `account.approve()` call for this flow. The protocol builds an ERC20 approval to the source-chain transaction-value helper and the helper bridge call, then submits both in one UserOperation.
```javascript title="Gasless bridge with paymasterToken"
const USDT_TOKEN_ADDRESS = process.env.USDT_SOURCE_TOKEN_ADDRESS
const USDT0_OFT_ADDRESS = process.env.USDT0_OFT_ADDRESS
const amount = 1000000n
const paymasterToken = { address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' }
const result = await bridgeProtocol.bridge(
{
targetChain: 'polygon',
recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
token: USDT_TOKEN_ADDRESS,
amount,
oftContractAddress: USDT0_OFT_ADDRESS
},
{
paymasterToken
}
)
console.log('Bridge hash:', result.hash)
console.log('Account fee:', result.fee)
console.log('Bridge fee:', result.bridgeFee)
```
The bundled approval and helper call produce one UserOperation hash. The protocol approves enough source token for the amount plus its helper-calculated bridge fee and tolerance.
In `1.0.0-beta.7`, `bridgeFee` for this helper flow is in bridged-token base units. The account's `fee` is in native base units for native gas, paymaster-token base units for token-paid gas, or zero for sponsored gas. The protocol numerically adds those values when enforcing `bridgeMaxFee`. Do not interpret the sum as one currency or set an ERC-4337 cap until your integration has confirmed compatible units for its payment mode.
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` rejects a bridge when the implementation's combined fee value is at or above the cap:
```javascript title="Construct Usdt0ProtocolEvm"
import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm'
const bridgeProtocol = new Usdt0ProtocolEvm(account, {
bridgeMaxFee: 1000000000000000n
})
```
For standard EVM accounts, `fee` and `bridgeFee` are source-chain native base units. ERC-4337 fee units depend on the account's gas-payment mode while `bridgeFee` is returned in bridged-token base units. Read [Fee units and `bridgeMaxFee`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#fee-units-and-bridgemaxfee) before setting a cap for an ERC-4337 flow.
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 |
ERC-4337 helper workflows are available when the source chain is Ethereum, Arbitrum, Plasma, or Polygon. Other listed source chains support standard EVM accounts only. 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, the combined fee value is at or above [`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
This standard-account example wraps [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) in `try/catch` and inspects `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 | [Docs](/sdk/community-modules/wdk-wallet-rgb/) | [UTEXO](https://github.com/UTEXO-Protocol) |
| [@utexo/wdk-rgb-lightning](https://www.npmjs.com/package/@utexo/wdk-rgb-lightning) ([GitHub](https://github.com/UTEXO-Protocol/wdk-rgb-lightning)) | Wallet Module | RGB Lightning node, channel, payment, and asset integration | [Docs](/sdk/community-modules/wdk-rgb-lightning/) | [UTEXO](https://github.com/UTEXO-Protocol) |
| [@arkade-os/wdk](https://www.npmjs.com/package/@arkade-os/wdk) ([GitHub](https://github.com/arkade-os/arkade-wdk)) | Wallet Module | Bitcoin wallet module built on the Arkade SDK with Arkade addresses, boarding addresses, BIP21/LNURL/Lightning send routing, and optional Boltz swaps | [README](https://github.com/arkade-os/arkade-wdk#readme) | [Arkade](https://github.com/arkade-os) |
| [@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 | [Docs](/sdk/community-modules/wdk-wallet-cosmos/) | [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/) |
| [wdk-protocol-swidge-orchestra](https://www.npmjs.com/package/wdk-protocol-swidge-orchestra) ([GitHub](https://github.com/flashnetxyz/wdk-protocol-swidge-orchestra)) | Swidge Module | Flashnet Orchestra Swidge provider for BTC and stablecoin routes returned by Orchestra | [Docs](/sdk/swidge-modules/swidge-orchestra/) | [Flashnet](https://github.com/flashnetxyz) |
| [@rhino.fi/wdk-protocol-swidge-rhinofi](https://www.npmjs.com/package/@rhino.fi/wdk-protocol-swidge-rhinofi) ([GitHub](https://github.com/rhinofi/wdk-protocol-swidge-rhinofi)) | Swidge Module | Rhino.fi cross-chain swap and bridge routes through the WDK swidge interface | [Docs](/sdk/swidge-modules/swidge-rhinofi/) | [Rhino.fi](https://rhino.fi/) |
| [@lifi/wdk-protocol-swidge-lifi](https://www.npmjs.com/package/@lifi/wdk-protocol-swidge-lifi) ([GitHub](https://github.com/lifinance/wdk-lifi-swidge-protocol)) | Swidge Module | LI.FI swap and bridge routes through the WDK swidge interface | [Docs](/sdk/swidge-modules/swidge-lifi/) | [LI.FI](https://li.fi/) |
| [@moonpay/wdk-protocol-swidge-moonpay-trade](https://www.npmjs.com/package/@moonpay/wdk-protocol-swidge-moonpay-trade) | Swidge Module | Routes through MoonPay Trade | [README](https://www.npmjs.com/package/@moonpay/wdk-protocol-swidge-moonpay-trade#readme) | [MoonPay](https://www.moonpay.com/) |
| [@swapdk/wdk-protocol-swidge-swapdk](https://www.npmjs.com/package/@swapdk/wdk-protocol-swidge-swapdk) ([GitHub](https://github.com/Swap-DK/wdk-protocol-bridges-swapdk)) | Swidge Module | Routes through SwapDK | [README](https://github.com/Swap-DK/wdk-protocol-bridges-swapdk#readme) | [SwapDK](https://swapdk.com/) |
| [@gobob/wdk-protocol-swidge-gateway](https://www.npmjs.com/package/@gobob/wdk-protocol-swidge-gateway) ([GitHub](https://github.com/bob-collective/wdk-protocol-swidge-gateway)) | Swidge Module | Routes through the BOB Gateway | [README](https://github.com/bob-collective/wdk-protocol-swidge-gateway#readme) | [BOB](https://www.gobob.xyz/) |
| [@symbiosis-finance/wdk-protocol-swidge-symbiosis](https://www.npmjs.com/package/@symbiosis-finance/wdk-protocol-swidge-symbiosis) ([GitHub](https://github.com/symbiosis-finance/wdk-protocol-swidge-symbiosis)) | Swidge Module | Runtime-discovered exact-input quotes with EVM and Bitcoin source execution through the Symbiosis API | [Docs](/sdk/swidge-modules/swidge-symbiosis/) | [Symbiosis](https://symbiosis.finance/) |
---
## 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 supported wallet and protocol module types. 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
***
## RGB Lightning wallet
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning
Description: Run a community-maintained RGB-over-Lightning wallet with LDK channels, invoices, payments, VSS, and LSP flows.
`@utexo/wdk-rgb-lightning` is a community-maintained WDK wallet module that runs an LDK and `rgb-lib` node behind the WDK manager/account interface.
These pages describe the released [`@utexo/wdk-rgb-lightning@0.1.0-beta.15`](https://github.com/UTEXO-Protocol/wdk-rgb-lightning/releases/tag/v0.1.0-beta.15).
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.
This is a pre-1.0 beta. Its native payloads, peer compatibility, LSP behavior, and operational recovery model can change between beta releases. Pin exact versions and test the full lifecycle on the target host.
## Choose the correct RGB module
| Requirement | Module |
|---|---|
| Channels, BOLT11 payments, RGB invoices and transfers, Lightning Address, APay, or VSS | `@utexo/wdk-rgb-lightning` |
| On-chain NIA issuance and an independent on-chain RGB wallet | [`@utexo/wdk-wallet-rgb`](/sdk/community-modules/wdk-wallet-rgb) |
| Bitcoin without RGB or Lightning node state | [`@tetherto/wdk-wallet-btc`](/sdk/wallet-modules/wallet-btc) |
The two UTEXO RGB modules derive different wallet identities and own different `rgb-lib` databases. They do not share asset records. Give each a separate persistent `dataDir`.
## Runtime architecture
The package uses conditional exports:
| Host | Required optional peer | Released native artifacts verified for the peer |
|---|---|---|
| Node.js 18 or newer | `@utexo/rgb-lightning-node-nodejs` in `>=0.1.0-beta.10 <0.2.0` | macOS arm64/x64; Linux arm64 GNU; Linux x64 GNU/musl |
| Bare / mobile worklet | `@utexo/rgb-lightning-node-bare` in `>=0.1.0-beta.14 <0.2.0` | Android arm/arm64/x64; macOS arm64; iOS arm64 and arm64/x64 simulators |
The verified Node peer release was `0.1.0-beta.11`; the verified Bare peer release was `0.1.0-beta.15`. No Windows native artifact was published in this release set.
Each native peer downloads a prebuilt artifact during installation. Validate artifact provenance, platform selection, and native loading in the deployment pipeline.
## Security and state model
- The WDK manager retains the BIP-39 secret boundary.
- An in-process VLS external signer handles channel-state cryptography.
- `keyPair.privateKey` is always `null`; the account does not expose signer private bytes.
- RLN persists public node identity plus LDK and RGB state under `dataDir`.
- Optional VSS payloads are encrypted client-side and still require the original seed for recovery.
- `manager.dispose()` shuts down the binding, destroys the VLS signer, and wipes seed buffers retained by the RGB Lightning binding. Treat disposal as terminal for the node session, and do not reuse the manager, account, read-only adapter, or LSP object afterward.
The account is single-node and single-account: index `0`, path `m`.
## Capabilities and boundaries
- Explicit unlock against Bitcoin RPC, indexer, and RGB proxy services.
- Peer connections, channels, BOLT11 invoices, HODL invoices, payments, and keysend.
- Bitcoin balances, transactions, UTXOs, sends, and fee estimates.
- RGB balances, invoices, transfers, media, and channel-aware payments.
- LSP client, Lightning Address/LNURL-pay helpers, composed UTEXO LSP flows, and APay.
- Optional VSS backup and recovery fencing.
- Message signing and verification through the Lightning node identity.
- Query-only account adapter for least-authority reads.
Runtime JavaScript contains RGB issuance forwarders, but beta.15's public declarations omit them. These docs do not present them as supported public account APIs. Use the on-chain RGB module for issuance. Atomic-swap methods remain native-binding-only and are also outside the WDK account surface.
## Start building
Install the matching native peer, construct the node, and unlock the account.
Open the task-focused guide catalog.
Configure persistent state, signer policy, VSS, LSP, and native unlock services.
Review the released beta.15 manager, account, error, and LSP surface.
Inspect the exact released declarations and implementation.
***
## RGB Lightning wallet API reference
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/api-reference
Description: Public API reference for @utexo/wdk-rgb-lightning 0.1.0-beta.15.
This page covers the package-root declarations and released runtime behavior of [`@utexo/wdk-rgb-lightning@0.1.0-beta.15`](https://github.com/UTEXO-Protocol/wdk-rgb-lightning/releases/tag/v0.1.0-beta.15).
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.
## Package
| Field | Value |
|---|---|
| Package | `@utexo/wdk-rgb-lightning@0.1.0-beta.15` |
| Repository | [UTEXO-Protocol/wdk-rgb-lightning](https://github.com/UTEXO-Protocol/wdk-rgb-lightning) |
| Module format | ESM |
| Node/default entry | `index.js` → `index-node.js` |
| Bare entry | `bare.js` → `index-bare.js` |
| Declarations | `index.d.ts` |
Install the matching optional native peer. The manager, account, error, and LSP surfaces are shared across runtimes; the package root exports only the binding class selected for the active runtime.
## Root exports
| Group | Exports |
|---|---|
| Manager and accounts | Default `WalletManagerRgbLightning`, `WalletAccountRgbLightning`, `WalletAccountReadOnlyRgbLightning` |
| Low-level binding (runtime-selected) | Node: `NodeRgbLightningBinding`; Bare: `BareRgbLightningBinding`; `IRgbLightningBinding` type. The declarations name both classes, but each runtime root exports only its selected class. |
| Wallet errors | `RgbLightningError`, `UnlockError`, `AccountLockedError`, `VssError`, `VssNotConfiguredError`, `ApayError`, `NotImplementedError` |
| LSP | `LspClient`, `LspError`, `UtexoLsp`, LSP result/config types, timeout and settlement errors |
| LNURL / address | `isUmaAddress`, `normalizeLightningAddress`, `parseLightningAddress`, `fetchDiscovery`, `resolveAddressToInvoice`, `LnurlPayError` |
| Account-bound helpers | `payLightningAddress`, `requestLspRgbDeposit`, `payRgbViaLsp` |
Low-level binding classes are advanced escape hatches. Prefer the manager because it owns external-signer attachment, fallback identity handling, shutdown, and cleanup of secrets retained by the RGB Lightning binding.
## `WalletManagerRgbLightning`
| Member | Returns | Behavior |
|---|---|---|
| `constructor(seed, config)` | Manager | Requires BIP-39 mnemonic or seed bytes, `network`, and persistent `dataDir`. |
| `getAccount(index = 0)` | `Promise` | Returns the only account. Nonzero indexes and registered WDK signer names are rejected. |
| `getAccountByPath(path)` | `Promise` | Accepts only `m`. |
| `getFeeRates()` | `Promise` | Fetches mempool.space recommendations without selecting the configured network. |
| `dispose()` | `void` | Terminal for the RGB Lightning node session: shuts down the binding, destroys the VLS signer, and wipes seed buffers retained by the RGB Lightning binding. Do not reuse manager-derived objects after disposal. |
| `static Binding` | Binding constructor | Runtime-selected Node or Bare binding. |
## `WalletAccountReadOnlyRgbLightning`
The read-only adapter exposes queries without signing, broadcasting, channel mutation, VSS recovery, or LSP credentials.
| Group | Methods |
|---|---|
| Bootstrap and node | `getBootstrap()`, `getNodeInfo()`, `getNetworkInfo()` |
| Address | `getAddress()`, `getAddressState()` |
| Channels and peers | `listChannels()`, `getChannelId(tempId)`, `listPeers()` |
| Lightning | `decodeInvoice()`, `getInvoiceStatus()`, `listPayments()`, `getPayment(hash, type)` |
| RGB | `listAssets(filter?)`, `getAssetBalance()`, `getAssetMetadata()`, `listTransfers()`, `listTransfersByTxid()`, `decodeRgbInvoice()`, `getAssetMedia()` |
| Bitcoin | `getBalance(skipSync?)`, `getBalanceDetails(skipSync?)`, `getTransactions(skipSync?)`, `getTransactionsByTxid(txid, skipSync?)`, `listUnspents()`, `estimateFee()` |
| WDK | `getTokenBalance()`, `verify()`, `quoteTransfer()`, `quoteSendTransaction()`, `getTransactionReceipt()` |
| Diagnostics | `checkIndexerUrl()`, `checkProxyEndpoint()`, `vssStatus()` |
`getAddress()` throws `AccountLockedError` before unlock. `getAddressState()` returns `{status:'locked', address:null}` without throwing.
Before unlock, `getBalance()` returns `0n`. Use `getAddressState()` to distinguish a locked account from a ready account with a real zero balance.
`getTransactionReceipt()` returns only terminal confirmed Bitcoin, settled RGB, or non-pending Lightning records; otherwise it returns `null`.
## `WalletAccountRgbLightning`
The full account has fixed identity fields:
| Member | Value |
|---|---|
| `index` | `0` |
| `path` | `m` |
| `keyPair.publicKey` | 33-byte compressed Lightning node public key |
| `keyPair.privateKey` | Always `null`; VLS holds signing material |
### Lifecycle and node
| Method | Behavior |
|---|---|
| `unlock(nativeRequest)` | Unlocks the node with native snake_case RPC, indexer, proxy, and announce fields; wraps failures as `UnlockError`. |
| `getBootstrap()` | Returns public signer/bootstrap metadata. |
| `getNodeInfo()` / `getNetworkInfo()` | Query node and chain information. |
| `sync()` | Synchronizes node state. |
| `getAddress()` / `getAddressState()` | Read current stable address or lock state. |
| `rotateAddress()` | Explicitly advances the Bitcoin address. |
| `shutdown()` | Idempotently shuts down the account binding. |
| `dispose()` | Account no-op; the manager owns terminal cleanup. |
### Peers, channels, and onion messages
| Method | Behavior |
|---|---|
| `connectPeer(pubkeyAndAddress)` | Connects a `pubkey@host:port` peer. |
| `disconnectPeer(request)` | Forwards the native disconnect request. |
| `listPeers()` | Returns native peer records. |
| `openChannel(request)` | Accepts `OpenChannelRequest` or a native object. |
| `closeChannel(request)` | Forwards the native close request. |
| `listChannels()` | Returns native channel records. |
| `getChannelId(temporaryId)` | Resolves a temporary channel ID. |
| `sendOnionMessage(request)` | Forwards the caller-supplied native `JsonSendOnionMessageRequest` unchanged and returns `{ok: true}`. Validate the exact beta.15 request shape before calling; the declaration types it as `object`. |
### Invoices and payments
| Method | Behavior |
|---|---|
| `createInvoice(request)` | Native BOLT11 invoice request. |
| `createLightningInvoice(request)` | Accepts native snake_case or released camelCase convenience fields. |
| `decodeInvoice(invoice)` / `getInvoiceStatus(invoice)` | Query invoice data and status. |
| `createHodlInvoice(params)` | Creates a HODL invoice for a caller-supplied payment hash. |
| `cancelHodlInvoice(request)` / `claimHodlInvoice(request)` | Native HODL lifecycle calls. |
| `sendPayment(request)` / `keysend(request)` | Native Lightning payment calls. |
| `listPayments()` / `getPayment(hash, type)` | Payment history. `type` is `Outbound`, `InboundAutoClaim`, or `InboundHodl`. |
### RGB assets
| Method | Behavior |
|---|---|
| `listAssets(filter?)`, `getAssetBalance()`, `getAssetMetadata()` | Query held RGB assets. |
| `listTransfers(assetId)`, `listTransfersByTxid(txid)` | Query transfer records. |
| `refreshTransfers(request)`, `failTransfers(request)` | Native transfer-state mutation. |
| `createRgbInvoice(request)`, `decodeRgbInvoice(invoice)` | Create or decode an RGB receive invoice. |
| `sendRgbAsset(request)` | Sends native grouped RGB recipients. |
| `getAssetMedia(digest)`, `postAssetMedia(request)` | Reads or uploads asset media. |
Runtime JavaScript includes issuance forwarders that beta.15's public declaration omits. They are intentionally excluded here. Use [`@utexo/wdk-wallet-rgb`](/sdk/community-modules/wdk-wallet-rgb) for released, documented issuance.
### Bitcoin and WDK operations
| Method | Behavior |
|---|---|
| `sendTransaction({to, value, feeRate?, confirmationTarget?})` | WDK Bitcoin send; VLS signs internally and returns `{hash, fee}`. |
| `sendBtc(request)` | Low-level native Bitcoin send. |
| `createUtxos(request)` | Native UTXO-creation request. |
| `quoteSendTransaction(tx)` | Approximate standard-send quote based on 141 vbytes. |
| `transfer(options)` | Routes BOLT11, node ID, Bitcoin address, or RGB invoice recipients. |
| `quoteTransfer(options)` | Routes to an approximate flow-specific quote. |
| `sign(message)` / `verify(message, signature)` | Lightning message signing and verification. |
| `signTransaction()` | Always throws `NotImplementedError`; use operation-specific send methods. |
| `toReadOnlyAccount()` | Returns the cached query-only adapter. |
## Transfer routing and units
`transfer(options)` classifies `recipient`:
| Recipient | Route | `amount` unit | Returned `fee` unit |
|---|---|---|---|
| BOLT11 invoice | `sendPayment` | millisatoshis | millisatoshis |
| 66-character hex node ID | `keysend` | millisatoshis | millisatoshis |
| `rgb:` or `utxob:` invoice | RGB send | RGB asset base units | `0n` because the native fee is not exposed |
| Other valid recipient | Bitcoin send | satoshis | satoshis |
For RGB routing, `token` is the asset ID. The generic router assumes `Fungible`, `donation: false`, and one confirmation. Use `sendRgbAsset()` with the exact native request for other assignment kinds or grouped recipients.
An RGB transfer result of `fee: 0n` does not prove the operation was fee-free. It means beta.15 does not expose the native fee through the WDK result.
The Lightning quote uses a 50-basis-point allowance, not a live route fee. An RGB-routed HTLC has a hard minimum of 3,000,000 msat.
## LSP, Lightning Address, and VSS
| Surface | Key methods |
|---|---|
| `LspClient` | `health`, `getInfo`, LNURL discovery/callback, address resolution, on-chain send bridge, Lightning receive bridge |
| `UtexoLsp` | `connect`, `waitForChannel`, `receiveAsset`, settlement/liquidity waits, `sendAsset`, `payAddress`, `enableLightningAddress`, `claimPendingPayments` |
| APay | `apayNew`, `bootstrapLsp`, `getLspConfig`, `createLsp` |
| VSS | `vssStatus`, `vssBackup`, `clearVssFence` |
In beta.15, `UtexoLsp.sendAsset({rgbInvoice, ln})` requires `ln.amtMsat` and `ln.expirySec` at runtime even though the public declaration marks `ln` and those fields optional. Pass the complete shape until the declaration is corrected.
Public HTTP is rejected for LSP and VSS by default, except loopback where supported. LNURL callbacks remain on the discovery host unless `allowCrossHostCallback` is explicitly enabled. `$user@host` is normalized as UMA-style address syntax only; the package does not implement UMA signing, compliance, or currency negotiation.
## Errors
Selected boundaries use typed errors:
| Error | Boundary |
|---|---|
| `UnlockError` | Node unlock |
| `AccountLockedError` | Locked address/signature operations |
| `VssError`, `VssNotConfiguredError` | VSS operations |
| `ApayError` | APay/bootstrap |
| `NotImplementedError` | Unsupported transaction signing |
| `LspError`, `LnurlPayError` | HTTP LSP and LNURL helpers |
| `LspChannelTimeoutError`, `LspLiquidityTimeoutError`, `LspSettlementError` | Composed LSP waits |
Many native methods still throw raw `Rln(): ` errors. Preserve the original error and operation context.
## Declaration boundary
Many native requests and responses are deliberately typed as `object` or `Record` because RLN owns their shape. Do not invent stable fields from demos, another beta, or an unreleased branch. Validate the exact beta.15 payloads your application consumes.
Atomic swap methods are available only on the native binding, not on the released WDK account. They are outside this reference.
## Guides
- [Configure the node](/sdk/community-modules/wdk-rgb-lightning/configuration)
- [Manage the node account](/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account)
- [Use peers and channels](/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels)
- [Use RGB assets](/sdk/community-modules/wdk-rgb-lightning/guides/rgb-assets)
- [Handle errors and cleanup](/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup)
***
## RGB Lightning wallet configuration
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/configuration
Description: Configure the beta.15 node, persistent state, native unlock services, VSS, LSP, and signer policy.
RGB Lightning has two configuration phases: construct the node with stable local settings, then unlock it with live Bitcoin and RGB service settings.
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.
## Constructor configuration
```js
import WalletManagerRgbLightning from '@utexo/wdk-rgb-lightning'
const manager = new WalletManagerRgbLightning(seedPhrase, {
network: 'regtest',
dataDir: '/app-private/wdk/rgb-lightning',
daemonListeningPort: 0,
ldkPeerListeningPort: 0,
maxMediaUploadSizeMb: 5,
permissiveSignerPolicy: true,
nodeSeedDerivation: 'auto',
})
```
## Consumed constructor fields
| Field | Type | Default | Behavior |
|---|---|---|---|
| `network` | `'mainnet' \| 'testnet' \| 'regtest' \| 'signet'` | None | Required. |
| `dataDir` | `string` | None | Required persistent, app-private RLN/LDK/RGB state path. |
| `daemonListeningPort` | `number` | `0` | RLN daemon port; `0` selects an ephemeral port. |
| `ldkPeerListeningPort` | `number` | `0` | LDK peer port; `0` selects an ephemeral port. |
| `maxMediaUploadSizeMb` | `number` | `5` | Maximum RGB media upload size. |
| `enableVirtualChannelsV0` | `boolean` | `false` | Enables trusted non-broadcast virtual channels; required for production APay. |
| `virtualPeerPubkeys` | `string[]` | None | Node-ID allowlist for virtual channel peers. |
| `permissiveSignerPolicy` | `boolean` | `true` | Relaxes VLS policy checks for the in-process single-user integration. |
| `nodeSeedDerivation` | `'auto' \| 'wdk-seed-v2' \| 'legacy-v1'` | `'auto'` | Selects corrected or legacy beta node-identity derivation. |
| `vssUrl` | `string` | None | Enables remote encrypted VSS snapshots. |
| `vssAllowHttp` | `boolean` | `false` | Allows non-HTTPS VSS; use only for explicitly accepted development risk. |
| `vssAllowEmptyRestore` | `boolean` | `false` | Allows startup without remote state when the VSS store is empty. |
| `lspBaseUrl` | `string` | None | LSP base URL used by APay and no-argument `createLsp()`. |
| `lspBearerToken` | `string` | None | Optional bearer credential for internal LSP endpoints. |
Assess `permissiveSignerPolicy: true` against your threat model. Tightening it can reject operations the integration expects, so test policy changes with real channel lifecycle flows.
## Native unlock request
After `getAccount(0)`, pass live services to `unlock()` in native snake_case:
```js
const account = await manager.getAccount(0)
await account.unlock({
bitcoind_rpc_username: 'rpc-user',
bitcoind_rpc_password: await loadRpcPassword(),
bitcoind_rpc_host: '127.0.0.1',
bitcoind_rpc_port: 18443,
indexer_url: 'tcp://127.0.0.1:50001',
proxy_endpoint: 'rpc://127.0.0.1:3000/json-rpc',
announce_addresses: [],
announce_alias: 'example-node',
})
```
Do not log the unlock request. It can contain RPC credentials and network-identifying values.
The beta.15 `RgbLightningWalletConfig` declaration also lists camelCase Bitcoin RPC, indexer, proxy, and announce fields. `WalletManagerRgbLightning` does not consume or forward those constructor fields. Pass the corresponding native snake_case values to `account.unlock()` instead.
`proxyEndpoint` in constructor config is also not forwarded to native binding configuration. Well-formed RGB invoices carry consignment transport endpoints; explicit native operations should use their released request shapes.
## Persistent state and wallet identity
Use a different `dataDir` from [`@utexo/wdk-wallet-rgb`](/sdk/community-modules/wdk-wallet-rgb):
```text
/app-private/wdk/rgb-onchain
/app-private/wdk/rgb-lightning
```
The two modules derive different wallet fingerprints and do not share RGB records. A shared seed and matching asset ID do not create a shared balance.
Only one live owner should use a Lightning `dataDir` or VSS namespace. LDK state has strict consistency and anti-rollback requirements.
## Node seed derivation
| Value | Use |
|---|---|
| `auto` | Corrected WDK seed derivation for new nodes; retries the beta.14-and-earlier legacy identity only on an exact persisted signer-identity mismatch. |
| `wdk-seed-v2` | Always use corrected derivation. |
| `legacy-v1` | Always use legacy beta derivation for an already-persisted compatible node. |
Do not switch derivation modes casually. A changed node identity can make persisted channel state unusable. Back up and test the exact upgrade path before changing a funded node.
## VSS
```js
const manager = new WalletManagerRgbLightning(seedPhrase, {
network: 'mainnet',
dataDir: '/app-private/wdk/rgb-lightning',
vssUrl: 'https://vss.example.com',
})
```
Non-loopback HTTP is rejected unless `vssAllowHttp` is true. The original seed is required to decrypt recovery state. `vssStatus()` is a local configuration view, not a VSS server health check.
## LSP and APay
```js
const manager = new WalletManagerRgbLightning(seedPhrase, {
network: 'mainnet',
dataDir: '/app-private/wdk/rgb-lightning',
lspBaseUrl: 'https://lsp.example.com',
lspBearerToken: await loadLspToken(),
enableVirtualChannelsV0: true,
virtualPeerPubkeys: [lspNodeId],
})
```
Production APay requires both virtual-channel fields and an LSP that trusts the wallet node. Treat `lspBearerToken` as a secret. The standalone `LspClient` rejects public HTTP by default.
## Runtime binding
Install exactly one optional native peer for the host:
```bash
# Node host
npm install @utexo/rgb-lightning-node-nodejs@0.1.0-beta.11
# Bare/mobile host
npm install @utexo/rgb-lightning-node-bare@0.1.0-beta.15
```
The declared compatible ranges are wider than these verified versions. Pin and validate a known pair rather than allowing an unreviewed beta upgrade.
## Next steps
- [Get started](/sdk/community-modules/wdk-rgb-lightning/guides/get-started)
- [Manage the node account](/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account)
- [VSS backup and recovery](/sdk/community-modules/wdk-rgb-lightning/guides/vss-backup-recovery)
- [API reference](/sdk/community-modules/wdk-rgb-lightning/api-reference)
***
## Read RGB Lightning balances and history
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/balances-history
Description: Query Bitcoin, RGB, Lightning payment, transaction, and terminal receipt state.
Use the account or its read-only adapter to query state owned by the RGB Lightning node.
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.
## Confirm the account is unlocked
```js
const state = await account.getAddressState()
if (state.status !== 'ready') {
throw new Error('Unlock the account before interpreting balances')
}
await account.sync()
```
Before unlock, `getBalance()` returns `0n`; that value is not distinguishable from a real zero without `getAddressState()`.
## Read Bitcoin balances
```js
const spendableSats = await account.getBalance()
const balanceDetails = await account.getBalanceDetails()
console.log({
spendableSats: spendableSats.toString(),
balanceDetails,
})
```
`getBalance()` returns spendable vanilla Bitcoin satoshis. `getBalanceDetails()` returns the native breakdown as an opaque object.
## Read RGB balances and assets
```js
const assets = await account.listAssets()
const balance = await account.getAssetBalance(assetId)
const spendableUnits = await account.getTokenBalance(assetId)
const metadata = await account.getAssetMetadata(assetId)
```
`getTokenBalance()` returns spendable asset base units and falls back to settled units when needed. Format values using validated asset metadata.
The Lightning node and on-chain RGB wallet do not share records. Query the module that actually received or holds the asset.
## Read Bitcoin and RGB history
```js
const [transactions, unspents, rgbTransfers] = await Promise.all([
account.getTransactions(),
account.listUnspents(),
account.listTransfers(assetId),
])
```
You can narrow Bitcoin records with `getTransactionsByTxid(txid)` and RGB records with `listTransfersByTxid(txid)`.
## Read Lightning payments
```js
const payments = await account.listPayments()
const outbound = await account.getPayment(paymentHash, 'Outbound')
```
The accepted payment discriminants are `Outbound`, `InboundAutoClaim`, and `InboundHodl`. Pre-1.0 HTTP names such as `sent` and `received` are not accepted.
## Read a terminal receipt
```js
const receipt = await account.getTransactionReceipt(hash)
```
The method returns:
- a confirmed Bitcoin record;
- a settled RGB transfer;
- a non-pending Lightning payment;
- or `null`.
Treat `null` as pending, unknown, or not yet indexed—not proof that a write failed.
## Validate native payloads
Many responses are intentionally declared as `object` because RLN owns their shape. Validate the fields your application consumes at runtime and pin them to beta.15. Do not copy response fields from an unreleased branch or different RLN beta.
## Next steps
- [Send Bitcoin and manage UTXOs](/sdk/community-modules/wdk-rgb-lightning/guides/send-btc-utxos)
- [Use Lightning payments](/sdk/community-modules/wdk-rgb-lightning/guides/lightning-payments)
- [Use RGB assets](/sdk/community-modules/wdk-rgb-lightning/guides/rgb-assets)
***
## Get started with RGB Lightning
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/get-started
Description: Install @utexo/wdk-rgb-lightning beta.15 with a matching native peer and unlock the node.
This guide creates the single RGB Lightning node account and unlocks it against development services.
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.
## 1. Install the module and one native peer
For a Node.js 18-or-newer host:
```bash
npm install @utexo/wdk-rgb-lightning@0.1.0-beta.15 @utexo/rgb-lightning-node-nodejs@0.1.0-beta.11
```
For a Bare/mobile host:
```bash
npm install @utexo/wdk-rgb-lightning@0.1.0-beta.15 @utexo/rgb-lightning-node-bare@0.1.0-beta.15
```
Install only the peer for the target runtime. No Windows native artifact was published in this release set.
## 2. Construct the manager
```js
import WalletManagerRgbLightning from '@utexo/wdk-rgb-lightning'
const seedPhrase = await loadSeedFromSecretStorage()
const manager = new WalletManagerRgbLightning(seedPhrase, {
network: 'regtest',
dataDir: '/app-private/wdk/rgb-lightning',
nodeSeedDerivation: 'auto',
})
```
Use a persistent directory that is not shared with the on-chain RGB wallet or another live node instance.
## 3. Get and unlock account 0
```js
const account = await manager.getAccount(0)
await account.unlock({
bitcoind_rpc_username: 'rpc-user',
bitcoind_rpc_password: await loadRpcPassword(),
bitcoind_rpc_host: '127.0.0.1',
bitcoind_rpc_port: 18443,
indexer_url: 'tcp://127.0.0.1:50001',
proxy_endpoint: 'rpc://127.0.0.1:3000/json-rpc',
announce_addresses: [],
announce_alias: 'example-node',
})
```
Constructor camelCase RPC, indexer, proxy, and announce fields are not consumed in beta.15. Pass native snake_case fields to `unlock()`.
## 4. Confirm readiness
```js
const addressState = await account.getAddressState()
if (addressState.status !== 'ready') {
throw new Error('RGB Lightning account is still locked')
}
const [nodeInfo, networkInfo] = await Promise.all([
account.getNodeInfo(),
account.getNetworkInfo(),
])
console.log({
address: addressState.address,
nodeInfo,
networkInfo,
})
```
The current address remains stable until `rotateAddress()` is called.
## 5. Dispose at shutdown
```js
try {
await runNodeFlows(account)
} finally {
manager.dispose()
}
```
`manager.dispose()` is terminal for the RGB Lightning node session. It shuts down the binding, destroys the VLS signer, and wipes seed buffers retained by the RGB Lightning binding. Do not reuse the manager, account, read-only adapter, or LSP object after disposal. `account.dispose()` is a no-op in this release.
## Next steps
- [Manage the node account](/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account)
- [Connect peers and open channels](/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels)
- [Configuration](/sdk/community-modules/wdk-rgb-lightning/configuration)
***
## Handle errors and clean up the node
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup
Description: Handle typed RGB Lightning boundaries, raw RLN failures, ambiguous writes, and terminal manager cleanup.
Beta.15 wraps selected lifecycle boundaries in typed errors, while many native calls still return raw `Rln(): ` failures.
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.
## Branch on typed boundaries
```js
import {
AccountLockedError,
ApayError,
UnlockError,
VssError,
VssNotConfiguredError,
} from '@utexo/wdk-rgb-lightning'
try {
await account.unlock(unlockRequest)
} catch (error) {
if (error instanceof UnlockError) {
reportUnlockFailure(error.code, error.cause)
}
throw error
}
```
Typed `RgbLightningError` subclasses preserve the original error as `cause` and expose `code` plus `toJSON()`.
| Error | Handle |
|---|---|
| `UnlockError` | RPC credentials, host reachability, indexer, proxy, VSS initialization, or persisted identity mismatch. |
| `AccountLockedError` | Prompt for unlock or use `getAddressState()` before address/signature operations. |
| `VssNotConfiguredError` | Disable the VSS action or construct with `vssUrl`. |
| `VssError` | Preserve the fence/snapshot state and investigate before retrying. |
| `ApayError` | Inspect peer visibility, LSP auth, virtual-channel config, and partial bootstrap state. |
| `NotImplementedError` | Route to an operation-specific send method. |
`LspError`, `LnurlPayError`, and the composed LSP timeout/settlement errors are separate hierarchies.
## Preserve raw native errors
Many node, channel, payment, Bitcoin, and RGB methods still throw version-specific `Rln(...)` strings.
```js
try {
await account.openChannel(openRequest)
} catch (error) {
reportWalletFailure({
operation: 'open_channel',
message: error instanceof Error ? error.message : String(error),
})
throw error
}
```
Do not branch on raw message text unless the behavior is pinned and covered by tests. Never log the mnemonic, unlock credentials, LSP token, VSS fence password, complete private invoice context, or native requests containing secrets.
## Reconcile writes before retrying
A timeout does not prove failure. After a channel, payment, Bitcoin, RGB, or APay write:
1. Preserve returned IDs and the original error.
2. Query the relevant channel, payment, transaction, transfer, peer, or APay state.
3. Synchronize when appropriate.
4. Retry only after an explicit idempotency rule proves it safe.
`bootstrapLsp()` can leave the LSP peer connected when later APay registration fails. Inspect `listPeers()` before retrying.
## Distinguish lock from zero balance
Before unlock, `getBalance()` returns `0n`:
```js
const state = await account.getAddressState()
if (state.status === 'locked') {
showUnlockRequired()
} else {
showBalance(await account.getBalance())
}
```
## Preserve the primary failure during cleanup
```js
let operationError
try {
await runWalletFlow(account)
} catch (error) {
operationError = error
throw error
} finally {
try {
manager.dispose()
} catch (cleanupError) {
reportCleanupFailure(cleanupError, { operationError })
}
}
```
`account.shutdown()` is idempotent, but `manager.dispose()` is terminal for the RGB Lightning node session. It shuts down the binding, destroys the VLS signer, and wipes seed buffers retained by the RGB Lightning binding. Do not reuse the manager, account, read-only adapter, or LSP object after disposal. `account.dispose()` is a no-op.
## Investigate common failures
| Symptom | Evidence to collect |
|---|---|
| Native binding fails to load | Host OS/architecture, installed peer version, downloaded artifact, Node/Bare entry selected. |
| Persisted identity mismatch | `nodeSeedDerivation`, prior beta version, node public key, isolated backup of `dataDir`. |
| Locked address | `getAddressState()`, unlock request and service reachability without secret values. |
| Channel/payment failure | Peer and channel state, route amount units, RGB 3,000,000-msat minimum, native error cause. |
| VSS fence held | Previous owner lifecycle, VSS namespace, last checkpoint, proof no other live writer exists. |
| LSP/APay failure | HTTPS/auth, peer visibility, virtual-channel flags, trusted LSP node ID, partial bootstrap state. |
## Next steps
- [Configuration](/sdk/community-modules/wdk-rgb-lightning/configuration)
- [VSS backup and recovery](/sdk/community-modules/wdk-rgb-lightning/guides/vss-backup-recovery)
- [LSP, Lightning Address, and APay](/sdk/community-modules/wdk-rgb-lightning/guides/lsp-lightning-address-apay)
- [API reference](/sdk/community-modules/wdk-rgb-lightning/api-reference)
***
## Create and send Lightning payments
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/lightning-payments
Description: Create BOLT11 and HODL invoices, send payments or keysend, and inspect Lightning payment state.
Use the released invoice and payment methods after the node is unlocked and has a ready channel.
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.
## Create an invoice
The convenience method accepts released camelCase fields:
```js
const created = await account.createLightningInvoice({
amountMsat: 5_000_000,
expirySec: 3_600,
})
```
It also accepts native snake_case objects. The return is intentionally typed as `object`; validate and extract the BOLT11 field according to the pinned beta.15 response.
For an RGB-routed invoice, include `assetId` and `assetAmount`. Such an HTLC must be at least 3,000,000 msat.
## Decode and inspect an invoice
```js
const decoded = await account.decodeInvoice(bolt11)
const status = await account.getInvoiceStatus(bolt11)
```
Before paying, validate network, destination, amount, expiry, description or description hash, asset fields, and any application-specific approval.
## Send a BOLT11 payment
```js
const sent = await account.sendPayment({ invoice: bolt11 })
```
`sendPayment()` forwards the native request and returns a native object. Reconcile with `getPayment()` or `listPayments()` after ambiguous failure.
The generic WDK router also recognizes a BOLT11 recipient:
```js
const result = await account.transfer({
recipient: bolt11,
amount: 5_000_000n,
})
```
Lightning amounts and returned fees are millisatoshis.
## Keysend
`keysend(request)` accepts a native request object. The generic router treats a 66-character hexadecimal recipient as a node public key:
```js
const result = await account.transfer({
recipient: destinationNodeId,
amount: 5_000_000n,
})
```
Validate that the recipient is exactly the intended compressed node ID. A malformed recipient can fall through to a different transfer route and fail later.
## Quote limitations
`quoteTransfer()` uses a 50-basis-point Lightning allowance. It is not a live route probe and does not guarantee the route, liquidity, final fee, or success.
Apply an application policy, then inspect the actual payment record rather than presenting the quote as final.
## HODL invoices
```js
const hodl = await account.createHodlInvoice({
paymentHash,
amtMsat: 5_000_000,
expirySec: 3_600,
})
```
The caller owns preimage generation and custody. Use `claimHodlInvoice(validatedNativeClaimRequest)` only after the intended condition is satisfied, or `cancelHodlInvoice(validatedNativeCancelRequest)` according to a documented timeout policy.
Losing, disclosing, or reusing a preimage can violate the payment contract.
## Inspect payment history
```js
const payments = await account.listPayments()
const payment = await account.getPayment(paymentHash, 'Outbound')
```
Valid payment types are `Outbound`, `InboundAutoClaim`, and `InboundHodl`.
## Next steps
- [Connect peers and channels](/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels)
- [Use RGB assets](/sdk/community-modules/wdk-rgb-lightning/guides/rgb-assets)
- [Use Lightning Address and APay](/sdk/community-modules/wdk-rgb-lightning/guides/lsp-lightning-address-apay)
***
## Use an LSP, Lightning Address, and APay
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/lsp-lightning-address-apay
Description: Use the beta.15 LSP client, composed RGB flows, LNURL-pay helpers, Lightning Address, and asynchronous payments.
The package exposes a standalone HTTP client, account-bound helpers, and a composed `UtexoLsp` flow object.
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.
## Configure a secure LSP endpoint
```js
const manager = new WalletManagerRgbLightning(seedPhrase, {
network: 'mainnet',
dataDir: '/app-private/wdk/rgb-lightning',
lspBaseUrl: 'https://lsp.example.com',
lspBearerToken: await loadLspToken(),
})
```
Treat the bearer token as a secret. Public HTTP is rejected by default. Enable HTTP only for an explicitly accepted loopback/development environment.
## Use the standalone client
```js
import { LspClient } from '@utexo/wdk-rgb-lightning'
const client = new LspClient({
baseUrl: 'https://lsp.example.com',
})
const [health, info] = await Promise.all([
client.health(),
client.getInfo(),
])
```
The client also exposes LNURL discovery/callback, Lightning Address resolution, RGB on-chain send bridging, and Lightning receive bridging. Validate native response objects.
## Use composed RGB flows
With `lspBaseUrl` configured, the no-argument form discovers the peer:
```js
const lsp = await account.createLsp()
await lsp.connect()
const receive = await lsp.receiveAsset({
assetId,
amountRgb: 100,
})
const settlement = await lsp.awaitReceiveSettlement(receive.lnInvoice)
```
`UtexoLsp` also exposes `waitForChannel()`, `waitForOutboundLiquidity()`, `sendAsset()`, `payAddress()`, `enableLightningAddress()`, and `claimPendingPayments()`.
In beta.15, `sendAsset()` requires `ln.amtMsat` and `ln.expirySec` at runtime even though the public declaration marks `ln` and those fields optional.
```js
const sent = await lsp.sendAsset({
rgbInvoice,
ln: {
amtMsat: 5_000_000,
expirySec: 3_600,
},
})
```
Bound every wait with a timeout or `AbortSignal`. Handle `LspChannelTimeoutError`, `LspLiquidityTimeoutError`, and `LspSettlementError` separately.
## Pay a Lightning Address
```js
const paid = await lsp.payAddress({
address: 'alice@example.com',
amtMsat: 5_000_000,
})
```
The lower-level `payLightningAddress()` and LNURL helpers are also exported from the package root.
LNURL callbacks must remain on the discovery host by default. Set `allowCrossHostCallback: true` only after reviewing the delegated host and redirect threat model.
Inputs such as `$alice@example.com` are normalized as UMA-style address syntax. This release does not implement UMA signing, compliance, currency negotiation, or exchange-rate semantics.
## Enable a Lightning Address
```js
const address = await lsp.enableLightningAddress()
console.log(address.address)
```
The LSP owns availability and name assignment. Do not present the address as durable until the LSP confirms it and your application stores the returned mapping.
## Configure production APay
APay receives payments while the wallet is offline. Construct the manager with:
```js
const manager = new WalletManagerRgbLightning(seedPhrase, {
network: 'mainnet',
dataDir: '/app-private/wdk/rgb-lightning',
lspBaseUrl: 'https://lsp.example.com',
lspBearerToken,
enableVirtualChannelsV0: true,
virtualPeerPubkeys: [lspNodeId],
})
```
Then bootstrap the authenticated LSP peer:
```js
const result = await account.bootstrapLsp({
peerPubkeyAndAddr: `${lspNodeId}@${lspHost}:${lspPort}`,
hostNodeId: lspNodeId,
waitForPeerMs: 15_000,
pollIntervalMs: 250,
})
```
`bootstrapLsp()` connects the peer, waits for visibility, then calls `apayNew()` when `hostNodeId` is supplied.
A failed bootstrap can leave a connected peer even when APay registration did not finish. Inspect `listPeers()` and APay state before retrying; do not assume rollback.
Production APay requires mutual trust for `trusted_no_broadcast` virtual channels. Authenticate the LSP node ID independently.
## Next steps
- [Connect peers and channels](/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels)
- [Use RGB assets](/sdk/community-modules/wdk-rgb-lightning/guides/rgb-assets)
- [Handle errors and cleanup](/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup)
***
## Manage the RGB Lightning node account
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account
Description: Manage node identity, lock state, addresses, read-only access, seed derivation, and lifecycle cleanup.
One manager owns one RLN/LDK node, one account at index `0`, and one persistent state directory.
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.
## Use the fixed account identity
```js
const account = await manager.getAccount(0)
console.log(account.index) // 0
console.log(account.path) // m
```
`getAccountByPath('m')` returns the same account. Other paths and nonzero indexes are rejected. The manager's signer-name overload does not enable registered WDK signers; this module requires its attached VLS signer.
`account.keyPair.publicKey` is the compressed Lightning node public key. `account.keyPair.privateKey` is always `null`.
## Handle locked state explicitly
```js
const state = await account.getAddressState()
if (state.status === 'locked') {
showUnlockRequired()
} else {
showAddress(state.address)
}
```
`getAddress()` throws `AccountLockedError` before unlock. `getBalance()` instead returns `0n`, so never infer readiness from balance alone.
## Keep or rotate the address deliberately
```js
const current = await account.getAddress()
const next = await account.rotateAddress()
```
The binding enables address reuse, so repeated `getAddress()` calls return the stable current address. `rotateAddress()` is an explicit state-changing operation; update deposit records and UI only after it succeeds.
## Create a read-only adapter
```js
const readOnly = await account.toReadOnlyAccount()
const [channels, peers, assets] = await Promise.all([
readOnly.listChannels(),
readOnly.listPeers(),
readOnly.listAssets(),
])
```
The adapter can query node, channel, payment, RGB, Bitcoin, fee, receipt, signature-verification, and diagnostic state. It cannot sign, broadcast, mutate channels, recover VSS, or expose LSP credentials.
Do not retain it after `manager.dispose()`, because it uses the manager-owned native query transport.
## Preserve node identity across upgrades
The default `nodeSeedDerivation: 'auto'` uses corrected seed derivation for new nodes and retries legacy beta derivation only on an exact persisted identity mismatch.
Pin `legacy-v1` only for a verified pre-beta.15 node that requires it. Pin `wdk-seed-v2` only after proving the persisted node uses the corrected identity. Changing identity against funded channel state can make the node unusable.
## Shut down safely
`account.shutdown()` is idempotent and stops the binding. `manager.dispose()` is the terminal owner-level cleanup and should still be your application shutdown boundary.
```js
try {
await runNode(account)
} finally {
manager.dispose()
}
```
Cleanup can throw. Preserve the primary operation error and report cleanup failure separately.
## Next steps
- [Read balances and history](/sdk/community-modules/wdk-rgb-lightning/guides/balances-history)
- [Back up with VSS](/sdk/community-modules/wdk-rgb-lightning/guides/vss-backup-recovery)
- [Handle errors and cleanup](/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup)
***
## Connect peers and manage channels
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels
Description: Connect Lightning peers and open, inspect, or close standard, RGB, and trusted virtual channels.
Unlock and synchronize the node before changing peer or channel state.
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.
## Connect a peer
```js
const peerUri = `${peerNodeId}@${peerHost}:${peerPort}`
await account.connectPeer(peerUri)
const peers = await account.listPeers()
```
Validate the node ID, host, port, network, and intended counterparty out of band. A successful connection does not prove channel readiness.
## Open a standard channel
```js
const opened = await account.openChannel({
peer_pubkey_and_opt_addr: peerUri,
capacity_sat: 1_000_000,
push_msat: 0,
public: true,
with_anchors: true,
})
```
Values are forwarded to RLN. Confirm funding, reserves, feerate, public/private policy, anchor support, and counterparty compatibility before opening.
## Open an RGB channel
The released `OpenChannelRequest` also declares `asset_id` and `asset_amount`:
```js
const opened = await account.openChannel({
peer_pubkey_and_opt_addr: peerUri,
capacity_sat: 1_000_000,
asset_id: assetId,
asset_amount: 10_000,
public: false,
with_anchors: true,
})
```
`asset_amount` is in RGB asset base units. The asset must already exist in this Lightning node's independent wallet state.
An RGB-routed HTLC has a hard minimum of 3,000,000 millisatoshis in this release. Smaller RGB-channel invoices or payments fail to route.
## Inspect channel state
```js
const channels = await account.listChannels()
const permanentId = await account.getChannelId(temporaryChannelIdHex)
```
Responses are native objects. Validate status, confirmations, channel IDs, balances, and counterparty before enabling payments.
## Close or disconnect
```js
await account.closeChannel(validatedNativeCloseRequest)
await account.disconnectPeer(validatedNativeDisconnectRequest)
```
The public declaration leaves both request shapes as `object`. Use the exact matching beta.15/RLN schema and validate it at the application boundary.
Do not treat peer disconnection as channel closure. Confirm final channel and on-chain state.
## Trusted virtual channels
Production APay uses non-broadcast trusted channels. Construct the manager with:
```js
import WalletManagerRgbLightning from '@utexo/wdk-rgb-lightning'
const manager = new WalletManagerRgbLightning(seedPhrase, {
network: 'mainnet',
dataDir: '/app-private/wdk/rgb-lightning',
enableVirtualChannelsV0: true,
virtualPeerPubkeys: [lspNodeId],
})
```
Then the native open request can use:
```js
const virtualChannelRequest = {
peer_pubkey_and_opt_addr: peerUri,
capacity_sat: 1_000_000,
virtual_open_mode: 'trusted_no_broadcast',
// Other validated OpenChannelRequest fields.
}
const opened = await account.openChannel(virtualChannelRequest)
```
Only trust an authenticated LSP node ID. Both sides must configure mutual trust; virtual channels change the normal broadcast and counterparty assumptions.
## Next steps
- [Use Lightning payments](/sdk/community-modules/wdk-rgb-lightning/guides/lightning-payments)
- [Use LSP and APay](/sdk/community-modules/wdk-rgb-lightning/guides/lsp-lightning-address-apay)
- [Handle errors and cleanup](/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup)
***
## Receive and send RGB assets
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/rgb-assets
Description: Query existing RGB assets, create receive invoices, and transfer assets through the RGB Lightning node.
The Lightning node holds and transfers its own RGB asset records. It does not share balances with the on-chain RGB wallet.
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.
## Inspect an existing asset
```js
const assets = await account.listAssets()
const balance = await account.getAssetBalance(assetId)
const metadata = await account.getAssetMetadata(assetId)
```
`getTokenBalance(assetId)` returns spendable base units and falls back to settled units when needed.
Use [`@utexo/wdk-wallet-rgb`](/sdk/community-modules/wdk-wallet-rgb) for released, documented NIA issuance. The beta.15 runtime contains issuance forwarders, but its public TypeScript declarations omit them; they are not documented here as supported WDK account APIs.
## Create an RGB invoice
```js
const created = await account.createRgbInvoice({
min_confirmations: 1,
witness: false,
asset_id: assetId,
assignment_kind: 'Fungible',
assignment_amount: 100,
duration_seconds: 3_600,
})
```
`min_confirmations` and `witness` are required. The result is a native object; validate and extract the complete invoice using the pinned beta.15 response shape.
Treat the invoice as single-use and send it over an authenticated channel.
## Send with the WDK router
```js
const transfer = await account.transfer({
recipient: rgbInvoice,
token: assetId,
amount: 100n,
feeRate: 2,
})
console.log(transfer.hash)
```
The router recognizes `rgb:` and `utxob:` invoices. It decodes the invoice and sends one `Fungible` recipient with `donation: false` and `min_confirmations: 1`.
The WDK result returns `fee: 0n` for RGB because beta.15 does not expose the native fee through this route. It does not mean the transfer was fee-free.
## Use the native grouped send
For non-fungible assignment kinds, donation behavior, witness data, multiple recipients, or explicit confirmation policy, use `sendRgbAsset()` with a validated `SendRgbAssetRequest`:
```js
const result = await account.sendRgbAsset({
donation: false,
fee_rate: 2,
min_confirmations: 1,
recipient_groups: validatedRecipientGroups,
})
```
Each recipient group contains an `asset_id` and recipients with a decoded `recipient_id`, assignment kind and amount, transport endpoints, and optional witness data. Do not construct those fields from unvalidated display text.
## Refresh and inspect transfer state
```js
await account.refreshTransfers(validatedNativeRefreshRequest)
const transfers = await account.listTransfers(assetId)
const byTransaction = await account.listTransfersByTxid(txid)
```
`failTransfers(request)` mutates transfer state. Call it only with the exact matching native request after proving the transfer should be failed.
## Media
```js
const media = await account.getAssetMedia(digest)
const posted = await account.postAssetMedia(validatedNativeMediaRequest)
```
Media is untrusted content. Validate digest, size, MIME type, storage, and rendering. `maxMediaUploadSizeMb` defaults to `5`.
## State boundary
Use separate persistent paths:
```text
rgb-onchain/ # @utexo/wdk-wallet-rgb
rgb-lightning/ # @utexo/wdk-rgb-lightning
```
Moving an asset between them requires a real receive/send flow. Copying files or reusing one path is not a migration.
## Next steps
- [Read balances and history](/sdk/community-modules/wdk-rgb-lightning/guides/balances-history)
- [Use peers and channels](/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels)
- [Use LSP flows](/sdk/community-modules/wdk-rgb-lightning/guides/lsp-lightning-address-apay)
***
## Send Bitcoin and manage UTXOs
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/send-btc-utxos
Description: Quote and send Bitcoin, inspect history, and use native UTXO operations through the RGB Lightning account.
The RGB Lightning account exposes a WDK-shaped Bitcoin send and lower-level RLN Bitcoin/UTXO methods.
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.
## Check readiness and balance
```js
const state = await account.getAddressState()
if (state.status !== 'ready') {
throw new Error('Unlock the account before sending Bitcoin')
}
await account.sync()
const balance = await account.getBalance()
const unspents = await account.listUnspents()
```
Validate spendable balance, UTXO status, confirmations, and any channel reserve requirements.
## Quote a standard send
```js
const transaction = {
to: bitcoinAddress,
value: 50_000n,
feeRate: 2,
confirmationTarget: 6,
}
const quote = await account.quoteSendTransaction(transaction)
```
The beta.15 quote approximates a standard transaction as 141 vbytes multiplied by a fee rate. It does not construct the final transaction or guarantee its exact size or fee.
`manager.getFeeRates()` reads main mempool.space recommendations without selecting `testnet`, `signet`, or `regtest`. Use a network-appropriate estimator for policy decisions.
## Send through the WDK shape
```js
const maximumFee = 2_000n
if (quote.fee > maximumFee) {
throw new Error('Quoted Bitcoin fee exceeds the application limit')
}
const result = await account.sendTransaction(transaction)
console.log({ txid: result.hash, feeSats: result.fee.toString() })
```
VLS signs internally. The result fee is in satoshis.
## Use native methods only with pinned shapes
`sendBtc(request)` and `createUtxos(request)` forward native RLN request objects. Their public beta.15 declarations intentionally use `object`; inspect and validate the exact matching RLN payload rather than inventing fields.
```js
const result = await account.sendBtc(validatedNativeSendRequest)
await account.createUtxos(validatedNativeCreateUtxosRequest)
```
`signTransaction()` always throws `NotImplementedError`. Use `sendTransaction()`, `sendBtc()`, `sendPayment()`, or `sendRgbAsset()` so VLS can enforce operation-specific policy.
## Reconcile ambiguous sends
After a timeout:
1. Keep any returned transaction ID.
2. Call `sync()`.
3. Query `getTransactionsByTxid()` and `listUnspents()`.
4. Retry only after proving the first send was not accepted.
## Next steps
- [Read balances and history](/sdk/community-modules/wdk-rgb-lightning/guides/balances-history)
- [Connect peers and channels](/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels)
- [Handle errors and cleanup](/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup)
***
## Sign and verify Lightning messages
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/sign-verify-messages
Description: Sign domain-separated messages through VLS and verify them with full or read-only RGB Lightning accounts.
The account signs with its Lightning node identity through the in-process VLS signer. It never exposes a private key through `keyPair`.
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.
## Build a domain-separated challenge
```js
const message = [
'example-node-auth',
'version=1',
`origin=${expectedOrigin}`,
`nonce=${serverNonce}`,
`expires=${expiresAt}`,
].join('\n')
```
Include purpose, origin, nonce, expiry, and version. Never ask a user to sign opaque bytes or content that could be interpreted as another action.
## Sign after unlock
```js
const state = await account.getAddressState()
if (state.status !== 'ready') {
throw new Error('Unlock the node before signing')
}
const signature = await account.sign(message)
```
`account.keyPair.privateKey` remains `null`; VLS owns signing material and policy checks.
## Verify
The full account can verify:
```js
const valid = await account.verify(message, signature)
```
The read-only adapter can also verify while its originating manager and node transport remain available:
```js
const readOnly = await account.toReadOnlyAccount()
const valid = await readOnly.verify(message, signature)
```
Reject changed messages, reused nonces, expired challenges, unexpected origins, and signatures tied to the wrong node identity.
## Do not confuse message and transaction signing
`sign()` signs a Lightning message. It does not produce a Bitcoin PSBT signature, channel commitment, BOLT11 payment, or RGB consignment authorization.
`signTransaction()` intentionally throws `NotImplementedError`. Use the operation-specific send methods so VLS can apply the correct policy.
## Cleanup
Call `manager.dispose()` when the node session ends. It destroys the attached signer and zeroes retained seed buffers where owned by the module. Disposal cannot erase secrets copied by application code or recover information written to logs.
## Next steps
- [Manage the node account](/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account)
- [Handle errors and cleanup](/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup)
- [API reference](/sdk/community-modules/wdk-rgb-lightning/api-reference)
***
## Back up and recover with VSS
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/guides/vss-backup-recovery
Description: Configure encrypted VSS snapshots, force checkpoints, recover with the original seed, and handle ownership fences safely.
VSS can mirror LDK channel state and RGB wallet data to a remote key-value service. It complements—not replaces—seed custody and local operational recovery.
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.
## Enable VSS at construction
```js
const manager = new WalletManagerRgbLightning(seedPhrase, {
network: 'mainnet',
dataDir: '/app-private/wdk/rgb-lightning',
vssUrl: 'https://vss.example.com',
vssAllowHttp: false,
vssAllowEmptyRestore: false,
})
```
Non-loopback HTTP is rejected unless `vssAllowHttp` is explicitly enabled. Keep production VSS on an authenticated, monitored HTTPS service.
VSS payloads are encrypted client-side. Recovery still requires the original BIP-39 seed; VSS ciphertext alone is insufficient.
## Inspect local configuration state
```js
const status = await account.vssStatus()
console.log({
configured: status.configured,
url: status.url,
lastBackupVersion: status.lastBackupVersion,
})
```
`vssStatus()` is a local view. It does not contact the server or prove that the latest remote snapshot exists, is readable, or can be decrypted.
## Force a checkpoint
```js
const { version } = await account.vssBackup()
recordCheckpointVersion(version)
```
Use forced checkpoints at controlled lifecycle boundaries such as before app suspension or a planned upgrade. A returned version proves only that this call completed; recovery testing remains necessary.
Calls without `vssUrl` throw `VssNotConfiguredError`. Server and encryption failures throw `VssError` at the wrapped boundaries.
## Plan recovery
The released account has no separate `restoreFromVss()` method. Recovery belongs to native node initialization with:
- the original seed;
- the intended network;
- the same VSS namespace and configuration;
- a safe local `dataDir`;
- compatible beta.15 module and native binding versions.
Follow the matching RLN/VSS recovery runbook and validate node identity, channels, payments, RGB state, Bitcoin state, and the latest checkpoint before resuming writes.
`vssAllowEmptyRestore: true` permits an empty remote store. Use it only when creating a deliberately new node; otherwise it can turn missing recovery state into a fresh start.
## Clear a stale ownership fence only when proven safe
```js
await account.clearVssFence(vssFencePassword)
```
The fence prevents two live writers from using one VSS store.
Call `clearVssFence()` only when you are certain the previous owner is permanently stopped. Two live nodes writing the same channel state can corrupt or roll back state and put funds at risk.
Before clearing:
1. Stop and isolate the previous host.
2. Confirm no background service, mobile worklet, or failover instance can restart.
3. Preserve local logs and state for incident analysis.
4. Verify the VSS namespace, seed identity, and expected checkpoint.
5. Start exactly one replacement owner.
## Recovery testing
Test on the exact runtime and native peer:
- seed retrieval and decryption;
- native artifact loading;
- VSS authentication and snapshot retrieval;
- node public key stability;
- channel and payment reconciliation;
- RGB asset and transfer state;
- Bitcoin address, balance, and UTXOs;
- a new forced backup after recovery.
## Next steps
- [Manage the node account](/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account)
- [Handle errors and cleanup](/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup)
- [Configuration](/sdk/community-modules/wdk-rgb-lightning/configuration)
***
## RGB Lightning wallet usage
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-rgb-lightning/usage
Description: Task-focused guides for @utexo/wdk-rgb-lightning 0.1.0-beta.15.
Use these guides for the single-node RGB Lightning account in `@utexo/wdk-rgb-lightning@0.1.0-beta.15`.
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.
Install the module and matching native binding, then unlock the node.
Manage identity, lock state, address rotation, read-only access, and disposal.
Read Bitcoin, RGB, Lightning, transaction, and receipt state.
Use WDK Bitcoin sends and native UTXO operations.
Connect peers and open, inspect, or close Lightning channels.
Create invoices, send payments, keysend, and handle HODL invoices.
Create RGB invoices and transfer existing assets through the node.
Use the LSP client, LNURL-pay, composed flows, and asynchronous payments.
Configure encrypted VSS snapshots and handle ownership fences.
Use the Lightning identity signer without exposing private key bytes.
Branch on typed boundaries, preserve raw native failures, and shut down safely.
## Reference
- [Configuration](/sdk/community-modules/wdk-rgb-lightning/configuration)
- [API reference](/sdk/community-modules/wdk-rgb-lightning/api-reference)
- [beta.15 release](https://github.com/UTEXO-Protocol/wdk-rgb-lightning/releases/tag/v0.1.0-beta.15)
***
## Cosmos wallet
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-wallet-cosmos
Description: Create Cosmos-compatible accounts, read balances, and send bank or IBC transfers with the Base58 community wallet module.
Use [`@base58-io/wdk-wallet-cosmos@1.0.0-beta.4`](https://www.npmjs.com/package/@base58-io/wdk-wallet-cosmos) to derive Bech32 accounts and interact with Cosmos SDK chains through WDK wallet interfaces.
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.
These pages describe the published [`v1.0.0-beta.4`](https://github.com/base58-io/wdk-wallet-cosmos/releases/tag/v1.0.0-beta.4) package. The repository's default branch can contain unreleased APIs that are not available in this version.
## What you can build
| Capability | Released behavior |
|---|---|
| Accounts | Derive and cache secp256k1 accounts with chain-specific Bech32 prefixes |
| Balances | Read one or more Cosmos denominations through RPC |
| Native sends | Sign or broadcast a bank send for the configured native denomination |
| Token transfers | Send a denomination on the same chain |
| IBC transfers | Send through a configured source channel when the recipient prefix differs |
| Message signing | Sign and verify ADR-36 arbitrary messages |
| Network access | Select bundled chain-registry metadata or provide custom RPC endpoints |
## Released beta limitations
- The manager accepts a BIP-39 mnemonic or seed bytes. It does not support named or external signers in `1.0.0-beta.4`.
- `toReadOnlyAccount()` is not implemented. Balance reads therefore use a seed-backed account.
- Fee quotes use a fixed gas limit and configured metadata; they do not simulate the transaction through RPC.
- `signTransaction()` returns a signed Cosmos transaction, but `sendTransaction()` cannot broadcast that signed value in this release.
- `transferMaxFee` is checked by `transfer()` only after broadcast. Quote and enforce an application limit before every write.
## Next steps
Choose the account, balance, transfer, signing, or troubleshooting flow you need.
Install the pinned beta and derive your first Cosmos account.
Configure chain metadata, RPC fallback, fees, and IBC channels.
Review the public API published in `1.0.0-beta.4`.
Inspect the source that corresponds to the documented package.
Review the community module catalog and submission guidance.
***
## Cosmos wallet API reference
URL: https://docs.wdk.tether.io/sdk/community-modules/wdk-wallet-cosmos/api-reference
Description: Public API reference for @base58-io/wdk-wallet-cosmos version 1.0.0-beta.4.
This reference covers the public package surface published in [`@base58-io/wdk-wallet-cosmos@1.0.0-beta.4`](https://www.npmjs.com/package/@base58-io/wdk-wallet-cosmos).
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.
Repository `main` contains APIs that are not part of this release. Use the [`v1.0.0-beta.4` source tag](https://github.com/base58-io/wdk-wallet-cosmos/tree/v1.0.0-beta.4) when comparing this reference with code.
## Package
| Field | Value |
|---|---|
| Package | `@base58-io/wdk-wallet-cosmos` |
| Version | `1.0.0-beta.4` |
| Module format | ESM |
| Default entry | `index.js` |
| Bare conditional entry | `bare.js` |
| Type declarations | `types/index.d.ts` |
| Runtime engines | Not declared in `package.json` |
| Peer dependencies | None |
| WDK wallet dependency | `@tetherto/wdk-wallet@1.0.0-beta.8` |
The package export map exposes the root module and a `./package` subpath for `package.json`. Internal files are not supported public entrypoints.
## Root exports
| Export | Kind | Description |
|---|---|---|
| `default` | Runtime | `WalletManagerCosmos` |
| `WalletAccountCosmos` | Runtime | Seed-backed Cosmos account implementation |
| `resolveChainConfig(config)` | Runtime | Resolves registry or custom chain configuration |
| `getAvailableChains()` | Runtime | Returns bundled registry names whose chain type is `cosmos` |
| `isKnownChain(chainName)` | Runtime | Checks whether a name exists in the bundled registry |
| `FeeRates` | Type only | Normal and fast fee amounts |
| `KeyPair` | Type only | Public key and sensitive private-key fields |
| `TransactionResult` | Type only | Transaction hash and fee |
| `TransferOptions` | Type only | Denomination, recipient, and amount |
| `TransferResult` | Type only | Transfer hash and fee |
| `CosmosWalletConfig` | Type only | Input wallet configuration |
| `ResolvedChainConfig` | Type only | Resolved chain configuration |
## `WalletManagerCosmos`
### Constructor
```js
new WalletManagerCosmos(seed, config?)
```
| Parameter | Type | Required | Description |
|---|---|---:|---|
| `seed` | `string \| Uint8Array` | Yes | BIP-39 mnemonic or seed bytes |
| `config` | `CosmosWalletConfig` | No | Chain, RPC, fee, retry, and IBC configuration |
The released constructor does not accept an external signer. Named signer overloads visible on repository `main` are not published in `1.0.0-beta.4`.
### Inherited static methods
| Method | Returns | Description |
|---|---|---|
| `WalletManagerCosmos.getRandomSeedPhrase(wordCount = 12)` | `string` | Generates a 12- or 24-word BIP-39 mnemonic |
| `WalletManagerCosmos.isValidSeedPhrase(seedPhrase)` | `boolean` | Validates a BIP-39 mnemonic |
### Methods
| Method | Returns | Behavior |
|---|---|---|
| `getAccount(index = 0)` | `Promise` | Derives and caches `0'/0/{index}` below the chain coin type |
| `getAccountByPath(path)` | `Promise` | Derives and caches a relative suffix such as `0'/0/5` |
| `getFeeRates()` | `Promise` | Calculates normal and fast amounts for the fixed gas limit |
| `dispose()` | `void` | Disposes cached accounts, zeros the manager seed bytes, and marks the manager unusable |
### Properties
| Property | Type | Description |
|---|---|---|
| `seed` | `Uint8Array` | Inherited sensitive seed bytes; do not log or retain |
| `isDisposed` | `boolean` | Whether `dispose()` has been called |
`getAccount()` caches by relative derivation path. Disposing a cached account directly does not evict it from the manager; prefer disposing the manager at the end of its lifecycle.
### `getFeeRates()`
```js
const { normal, fast } = await manager.getFeeRates()
```
The returned values are deterministic fee amounts in the selected fee denomination:
- registry configuration uses average and high gas-price tiers;
- explicit gas-price configuration returns the same amount for both priorities;
- final fallback uses `0.025` and `0.04`;
- all calculations use a gas limit of `200000`.
The method requires at least one configured RPC endpoint but does not make an RPC request.
## `WalletAccountCosmos`
Create accounts through `WalletManagerCosmos`. The exported static factory is also public:
```js
const account = await WalletAccountCosmos.create(seed, "0'/0/0", config)
```
Do not call the class constructor directly. Its parameters are implementation details.
### Account methods
| Method | Returns | Behavior |
|---|---|---|
| `getAddress()` | `Promise` | Returns the locally derived Bech32 address |
| `getBalance(denom?)` | `Promise` | Reads one denomination; defaults to `nativeDenom` |
| `getTokenBalance(denom)` | `Promise` | Alias behavior for a denomination-specific balance |
| `getTokenBalances(denoms)` | `Promise>` | Reads all balances and returns requested denominations that are present |
| `quoteTransfer(options)` | `Promise<{ fee: bigint }>` | Calculates a fixed-gas transfer fee without broadcasting |
| `transfer(options)` | `Promise` | Sends a bank transfer or configured IBC transfer |
| `sign(message)` | `Promise` | Returns a JSON-encoded ADR-36 `StdSignature` |
| `verify(message, signature)` | `Promise` | Verifies ADR-36 data against this account |
| `signTransaction(transaction)` | `Promise` | Returns a CosmJS signed `TxRaw` without broadcasting |
| `quoteSendTransaction(transaction)` | `Promise<{ fee: bigint }>` | Calculates a fixed-gas native-send fee |
| `sendTransaction(transaction)` | `Promise` | Signs and broadcasts a native bank send |
| `getTransactionReceipt(hash)` | `Promise