diff --git a/content/docs/ai/agent-skills.mdx b/content/docs/ai/agent-skills.mdx
index 2044834c..87e895dd 100644
--- a/content/docs/ai/agent-skills.mdx
+++ b/content/docs/ai/agent-skills.mdx
@@ -9,7 +9,7 @@ icon: Brain
WDK provides agent skills: structured instruction sets that teach AI agents how to create wallets, send transactions, swap tokens, bridge assets, and interact with DeFi protocols across 20+ blockchains. All operations are self-custodial. Keys stay on your machine, with no third-party custody dependency.
-**Skill vs MCP Toolkit**: Use an **agent skill** when your agent platform works with file-based instructions (e.g., OpenClaw, Cursor). Use the [MCP Toolkit](/ai/mcp-toolkit/) when your agent supports the Model Context Protocol natively (e.g., Claude, Cursor). Use both for maximum coverage.
+**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?
@@ -66,7 +66,8 @@ WDK's agent skills use a self-custodial model where your agent controls its own
| **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 the [MCP Toolkit](/ai/mcp-toolkit/) for structured tool calling |
+| **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
@@ -84,6 +85,7 @@ Community-built projects using WDK's agentic capabilities:
- [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
diff --git a/content/docs/ai/mcp-toolkit/configuration.mdx b/content/docs/ai/mcp-toolkit/configuration.mdx
index 058d0533..c0f2e384 100644
--- a/content/docs/ai/mcp-toolkit/configuration.mdx
+++ b/content/docs/ai/mcp-toolkit/configuration.mdx
@@ -111,8 +111,7 @@ server.registerProtocol('ethereum', 'velora', VeloraProtocolEvm)
server.registerProtocol('ethereum', 'usdt0', Usdt0ProtocolEvm)
server.registerProtocol('ethereum', 'aave', AaveProtocolEvm)
server.registerProtocol('ethereum', 'moonpay', MoonPayProtocol, {
- apiKey: process.env.MOONPAY_API_KEY,
- secretKey: process.env.MOONPAY_SECRET_KEY
+ apiKey: process.env.MOONPAY_API_KEY
})
```
@@ -240,7 +239,6 @@ server.registerTool(
| `WDK_SEED` | Yes | BIP-39 mnemonic for wallet key derivation |
| `WDK_INDEXER_API_KEY` | No | API key for WDK Indexer |
| `MOONPAY_API_KEY` | No | API key for MoonPay fiat on/off-ramp |
-| `MOONPAY_SECRET_KEY` | No | Secret key for MoonPay |
***
diff --git a/content/docs/ai/mcp-toolkit/get-started.mdx b/content/docs/ai/mcp-toolkit/get-started.mdx
index 427ca32f..1748f9e9 100644
--- a/content/docs/ai/mcp-toolkit/get-started.mdx
+++ b/content/docs/ai/mcp-toolkit/get-started.mdx
@@ -264,7 +264,6 @@ server.registerProtocol('ethereum', 'velora', VeloraProtocolEvm)
server.registerProtocol('ethereum', 'usdt0', Usdt0ProtocolEvm)
server.registerProtocol('ethereum', 'aave', AaveProtocolEvm)
server.registerProtocol('ethereum', 'moonpay', MoonPayProtocol, {
- secretKey: process.env.MOONPAY_SECRET_KEY,
apiKey: process.env.MOONPAY_API_KEY
})
@@ -287,7 +286,6 @@ server.registerTools([
| `WDK_SEED` | Yes | BIP-39 seed phrase for wallet derivation |
| `WDK_INDEXER_API_KEY` | No | Enables `INDEXER_TOOLS` - [get a key](/tools/indexer-api/get-started/#request-api-key) |
| `MOONPAY_API_KEY` | No | Enables `FIAT_TOOLS` - [MoonPay Dashboard](https://dashboard.moonpay.com/) |
-| `MOONPAY_SECRET_KEY` | No | Required with `MOONPAY_API_KEY` |
***
diff --git a/content/docs/ai/mcp-toolkit/index.mdx b/content/docs/ai/mcp-toolkit/index.mdx
index 74526976..9cc19622 100644
--- a/content/docs/ai/mcp-toolkit/index.mdx
+++ b/content/docs/ai/mcp-toolkit/index.mdx
@@ -60,6 +60,9 @@ 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
+
***
diff --git a/content/docs/ai/mcp-toolkit/langchain.mdx b/content/docs/ai/mcp-toolkit/langchain.mdx
index f33bf712..bd1c03eb 100644
--- a/content/docs/ai/mcp-toolkit/langchain.mdx
+++ b/content/docs/ai/mcp-toolkit/langchain.mdx
@@ -54,7 +54,7 @@ The command has built-in definitions for 13 chains and 4 protocol modules. When
| [`@tetherto/wdk-protocol-swap-velora-evm`](/sdk/swap-modules/swap-velora-evm) | Swap tools (Ethereum, Arbitrum) | -- |
| [`@tetherto/wdk-protocol-bridge-usdt0-evm`](/sdk/bridge-modules/bridge-usdt0-evm) | Bridge tools (Ethereum, Arbitrum) | -- |
| [`@tetherto/wdk-protocol-lending-aave-evm`](/sdk/lending-modules/lending-aave-evm) | Lending tools (Ethereum) | -- |
-| [`@tetherto/wdk-protocol-fiat-moonpay`](/sdk/fiat-modules/fiat-moonpay) | Fiat tools (Ethereum) | Requires `MOONPAY_*` env vars |
+| [`@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).
@@ -184,8 +184,12 @@ Control `serve` behavior through environment variables:
| `WDK_RPC_\` | No | Built-in defaults | Override the RPC endpoint for a chain (e.g. `WDK_RPC_ETHEREUM=https://my-rpc.com`) |
| `WDK_CONFIG` | No | -- | Path to a `wdk.config.json` file for custom chains and protocols |
| `WDK_INDEXER_API_KEY` | No | -- | Enables indexer tools for balance and transfer history queries |
-| `MOONPAY_API_KEY` | No | -- | Enables fiat on/off-ramp tools |
-| `MOONPAY_SECRET_KEY` | No | -- | Required with `MOONPAY_API_KEY` |
+| `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
diff --git a/content/docs/ai/openclaw.mdx b/content/docs/ai/openclaw.mdx
index a09b74be..924a9c3e 100644
--- a/content/docs/ai/openclaw.mdx
+++ b/content/docs/ai/openclaw.mdx
@@ -18,6 +18,10 @@ Tether and the WDK Team do not endorse or assume responsibility for its code, se
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):
@@ -113,6 +117,7 @@ OpenClaw makes use of artificial intelligence and machine learning technologies.
## 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
diff --git a/content/docs/cli/api-reference.mdx b/content/docs/cli/api-reference.mdx
new file mode 100644
index 00000000..be2cf8ce
--- /dev/null
+++ b/content/docs/cli/api-reference.mdx
@@ -0,0 +1,366 @@
+---
+title: API Reference
+description: Complete WDK CLI beta.1 command and option reference
+docType: reference
+schemaType: TechArticle
+icon: ListChecks
+---
+
+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?
+
+
diff --git a/content/docs/cli/configuration.mdx b/content/docs/cli/configuration.mdx
new file mode 100644
index 00000000..5bce1c91
--- /dev/null
+++ b/content/docs/cli/configuration.mdx
@@ -0,0 +1,294 @@
+---
+title: Configuration
+description: Configure WDK CLI paths, defaults, indexer access, wallet modules, and fiat ramps
+docType: reference
+schemaType: TechArticle
+icon: Settings
+---
+
+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?
+
+
diff --git a/content/docs/cli/guides/custom-networks.mdx b/content/docs/cli/guides/custom-networks.mdx
new file mode 100644
index 00000000..446201f4
--- /dev/null
+++ b/content/docs/cli/guides/custom-networks.mdx
@@ -0,0 +1,155 @@
+---
+title: Custom Networks
+description: Add and remove blockchain networks in WDK CLI
+docType: how-to
+schemaType: TechArticle
+---
+
+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?
+
+
diff --git a/content/docs/cli/guides/get-started.mdx b/content/docs/cli/guides/get-started.mdx
new file mode 100644
index 00000000..669a64de
--- /dev/null
+++ b/content/docs/cli/guides/get-started.mdx
@@ -0,0 +1,167 @@
+---
+title: Get Started
+description: Install WDK CLI and complete an Ethereum mainnet wallet flow
+docType: getting-started
+schemaType: TechArticle
+icon: Rocket
+---
+
+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?
+
+
diff --git a/content/docs/cli/guides/handle-errors.mdx b/content/docs/cli/guides/handle-errors.mdx
new file mode 100644
index 00000000..0285e1aa
--- /dev/null
+++ b/content/docs/cli/guides/handle-errors.mdx
@@ -0,0 +1,138 @@
+---
+title: Handle Errors
+description: Handle WDK CLI exit statuses, JSON output, stderr, and error codes
+docType: how-to
+schemaType: TechArticle
+icon: CircleQuestionMark
+---
+
+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?
+
+
diff --git a/content/docs/cli/guides/manage-tokens.mdx b/content/docs/cli/guides/manage-tokens.mdx
new file mode 100644
index 00000000..283f8f4e
--- /dev/null
+++ b/content/docs/cli/guides/manage-tokens.mdx
@@ -0,0 +1,138 @@
+---
+title: Manage Tokens
+description: Inspect, add, override, and remove WDK CLI token registry entries
+docType: how-to
+schemaType: TechArticle
+---
+
+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?
+
+
diff --git a/content/docs/cli/guides/manage-wallets.mdx b/content/docs/cli/guides/manage-wallets.mdx
new file mode 100644
index 00000000..80f51792
--- /dev/null
+++ b/content/docs/cli/guides/manage-wallets.mdx
@@ -0,0 +1,232 @@
+---
+title: Manage Wallets
+description: Create, import, select, unlock, lock, export, rename, and delete WDK CLI wallets safely.
+docType: how-to
+schemaType: TechArticle
+icon: WalletCards
+---
+
+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)
diff --git a/content/docs/cli/guides/use-mcp-server.mdx b/content/docs/cli/guides/use-mcp-server.mdx
new file mode 100644
index 00000000..320d789c
--- /dev/null
+++ b/content/docs/cli/guides/use-mcp-server.mdx
@@ -0,0 +1,185 @@
+---
+title: Use the MCP Server
+description: Connect WDK CLI to an MCP-compatible AI client
+docType: how-to
+schemaType: TechArticle
+icon: Wand
+---
+
+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?
+
+
diff --git a/content/docs/cli/index.mdx b/content/docs/cli/index.mdx
new file mode 100644
index 00000000..611459b2
--- /dev/null
+++ b/content/docs/cli/index.mdx
@@ -0,0 +1,100 @@
+---
+title: WDK CLI
+description: Install and use the local WDK wallet CLI, daemon, and MCP server
+docType: explanation
+schemaType: TechArticle
+icon: Terminal
+---
+
+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?
+
+
diff --git a/content/docs/cli/reference/architecture.mdx b/content/docs/cli/reference/architecture.mdx
new file mode 100644
index 00000000..4299eb58
--- /dev/null
+++ b/content/docs/cli/reference/architecture.mdx
@@ -0,0 +1,131 @@
+---
+title: Architecture
+description: Understand how the WDK CLI, MCP server, daemon, wallet store, and WDK modules work together.
+docType: explanation
+schemaType: TechArticle
+icon: Network
+---
+
+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)
diff --git a/content/docs/cli/reference/security-model.mdx b/content/docs/cli/reference/security-model.mdx
new file mode 100644
index 00000000..63d0cbb0
--- /dev/null
+++ b/content/docs/cli/reference/security-model.mdx
@@ -0,0 +1,168 @@
+---
+title: Security Model
+description: Understand seed protection, daemon trust boundaries, memory lifetime, TTL auto-locking, and operational trade-offs.
+docType: explanation
+schemaType: TechArticle
+icon: Shield
+---
+
+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)
diff --git a/content/docs/cli/reference/storage-format.mdx b/content/docs/cli/reference/storage-format.mdx
new file mode 100644
index 00000000..93aa526c
--- /dev/null
+++ b/content/docs/cli/reference/storage-format.mdx
@@ -0,0 +1,295 @@
+---
+title: Storage Format
+description: Inspect WDK CLI storage, seed.enc version 1, file permissions, and the standalone manual-recovery procedure.
+docType: reference
+schemaType: TechArticle
+icon: Database
+---
+
+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)
diff --git a/content/docs/overview/about.mdx b/content/docs/overview/about.mdx
index a33eada6..cb0b77b7 100644
--- a/content/docs/overview/about.mdx
+++ b/content/docs/overview/about.mdx
@@ -89,6 +89,7 @@ WDK natively supports a broad set of blockchains and standards out of the box:
| [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/) | ✅ |
diff --git a/content/docs/overview/changelog.mdx b/content/docs/overview/changelog.mdx
index a6efd257..35351ca5 100644
--- a/content/docs/overview/changelog.mdx
+++ b/content/docs/overview/changelog.mdx
@@ -10,6 +10,150 @@ Stay up to date with the latest improvements, new features, and bug fixes across
---
+### August 1, 2026
+
+**What's New**
+- **swidge-symbiosis** ([v1.3.0](https://github.com/symbiosis-finance/wdk-protocol-swidge-symbiosis/tree/v1.3.0)): Execute TON, Tron, and Solana source routes when the bound wallet account supports the required transaction format, probed at execution time: raw BoC message bodies for single-message TON routes, smart contract calls plus TRC-20 approvals for Tron with approval receipts checked for on-chain failure, and base64-serialized transactions for Solana. Wallet versions without the capability keep the previous quote-only behavior and throw `UnsupportedRouteError`. The repository adds a runnable end-to-end example that quotes, executes, and tracks a route.
+
+---
+
+### 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**
@@ -211,7 +355,7 @@ Stay up to date with the latest improvements, new features, and bug fixes across
### May 19, 2026
**What's New**
-- **[Swidge Protocol Interface](/sdk/swidge-modules)**: New page covering WDK's preferred route interface for new swap, bridge, and combined route providers, including discovery, quote, execution, status, fee, and migration guidance for the existing standalone swap and bridge interfaces.
+- **[Swidge modules](/sdk/swidge-modules)**: Add a catalog for released swap, bridge, and combined-route provider modules.
---
diff --git a/content/docs/resources/concepts.mdx b/content/docs/resources/concepts.mdx
index cdb06bcd..e426b99a 100644
--- a/content/docs/resources/concepts.mdx
+++ b/content/docs/resources/concepts.mdx
@@ -14,8 +14,8 @@ Account Abstraction is a blockchain technology that separates the concept of a u
WDK provides Account Abstraction support through specialized wallet modules:
-- `@tetherto/wdk-wallet-evm-erc4337` - EVM chains with ERC-4337 standard
-- `@tetherto/wdk-wallet-ton-gasless` - TON blockchain with gasless transactions
+- `@tetherto/wdk-wallet-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.
diff --git a/content/docs/sdk/all-modules.mdx b/content/docs/sdk/all-modules.mdx
index 45d896e2..07db2e77 100644
--- a/content/docs/sdk/all-modules.mdx
+++ b/content/docs/sdk/all-modules.mdx
@@ -1,6 +1,6 @@
---
title: All Modules
-description: Complete list of all available WDK modules including wallet, pricing, swidge, swap, bridge, lending, and fiat modules.
+description: Complete list of available WDK wallet, pricing, swidge, swap, bridge, lending, and fiat interfaces and modules.
docType: reference
schemaType: TechArticle
---
@@ -13,7 +13,7 @@ The orchestrator that manages all WDK modules.
| Module | Description | Documentation |
|--------|-------------|---------------|
-| [`@tetherto/wdk-core`](https://github.com/tetherto/wdk-core) | Central orchestrator for all WDK modules | [Docs](/sdk/core-module/) |
+| [`@tetherto/wdk`](https://github.com/tetherto/wdk) | Central orchestrator for all WDK modules | [Docs](/sdk/core-module/) |
## Wallet Modules
@@ -23,24 +23,33 @@ Wallet modules provide blockchain-specific wallet functionality for managing add
|--------|------------|-------------|---------------|
| [`@tetherto/wdk-wallet-btc`](https://github.com/tetherto/wdk-wallet-btc) | Bitcoin | Bitcoin SegWit wallet with BIP-39/BIP-44 support | [Docs](/sdk/wallet-modules/wallet-btc/) |
| [`@tetherto/wdk-wallet-evm`](https://github.com/tetherto/wdk-wallet-evm) | EVM | Ethereum and EVM-compatible chains wallet | [Docs](/sdk/wallet-modules/wallet-evm/) |
-| [`@tetherto/wdk-wallet-evm-erc4337`](https://github.com/tetherto/wdk-wallet-evm-erc-4337) | EVM | ERC-4337 Account Abstraction for EVM chains | [Docs](/sdk/wallet-modules/wallet-evm-erc-4337/) |
+| [`@tetherto/wdk-wallet-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 transactions on TON | [Docs](/sdk/wallet-modules/wallet-ton-gasless/) |
+| [`@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 Interface
+## Swidge Modules
-Swidge is the preferred interface for new protocol providers that can quote and execute asset routes. A route can be swap-only, bridge-only, or a combined swap and bridge route.
+Swidge providers can quote and execute asset routes. A route can be swap-only, bridge-only, or a combined swap and bridge route.
-| Module | Provider | Description | Documentation |
-|--------|----------|-------------|---------------|
-| [`wdk-protocol-swidge-orchestra`](https://www.npmjs.com/package/wdk-protocol-swidge-orchestra) | Flashnet Orchestra | 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 | Swidge routes for Rhino.fi cross-chain swap and bridge operations | [Docs](/sdk/swidge-modules/swidge-rhinofi/) |
+
+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, Bitcoin, and capability-gated TON, Tron, and Solana 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
@@ -71,9 +80,15 @@ Cross-chain bridge functionality for token transfers between blockchains.
DeFi lending and borrowing functionality.
-| Module | Blockchain | Status | Description | Documentation |
-|--------|------------|--------|-------------|---------------|
-| [`@tetherto/wdk-protocol-lending-aave-evm`](https://github.com/tetherto/wdk-protocol-lending-aave-evm) | EVM | Ready | Aave protocol integration for EVM | [Docs](/sdk/lending-modules/lending-aave-evm/) |
+
+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
@@ -88,11 +103,19 @@ On-ramp and off-ramp functionality for fiat currency integration.
Modules built by the WDK community. See the [Community Modules](/sdk/community-modules/) page for more details.
-| Module | Blockchain | Description | Documentation |
-|--------|------------|-------------|---------------|
-| [`@utexo/wdk-wallet-rgb`](https://github.com/UTEXO-Protocol/wdk-wallet-rgb) | Bitcoin (RGB) | RGB protocol wallet integration | [Docs](/sdk/community-modules/wdk-wallet-rgb/) |
-| [`@arkade-os/wdk`](https://github.com/arkade-os/arkade-wdk) | Arkade | Bitcoin wallet module built on the Arkade SDK | [README](https://github.com/arkade-os/arkade-wdk#readme) |
-| [`@base58-io/wdk-wallet-cosmos`](https://github.com/base58-io/wdk-wallet-cosmos) | Cosmos | 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) | EVM | Morpho Vault V2 and Morpho Blue lending integration | [Docs](/sdk/lending-modules/lending-morpho-evm/) |
+
+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) | Rhino.fi | Swidge cross-chain route integration | [Docs](/sdk/swidge-modules/swidge-rhinofi/) |
+| [`@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/) |
diff --git a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/api-reference.mdx b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/api-reference.mdx
index 23cd616f..702c16d7 100644
--- a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/api-reference.mdx
+++ b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/api-reference.mdx
@@ -26,7 +26,7 @@ new Usdt0ProtocolEvm(account, config?)
**Parameters:**
- `account` (WalletAccountEvm | WalletAccountEvmErc4337 | WalletAccountReadOnlyEvm | WalletAccountReadOnlyEvmErc4337): The wallet account to use for bridge operations
- `config` (BridgeProtocolConfig, optional): Configuration object
- - `bridgeMaxFee` (number | bigint, optional): Maximum total bridge cost in wei
+ - `bridgeMaxFee` (number | bigint, optional): Rejects `bridge()` when the implementation's combined fee value is at or above this cap
**Example:**
```javascript
@@ -38,7 +38,7 @@ const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
})
const bridgeProtocol = new Usdt0ProtocolEvm(account, {
- bridgeMaxFee: 1000000000000000n // Maximum bridge fee in wei
+ bridgeMaxFee: 1000000000000000n // Standard Ethereum account: source native base units
})
```
@@ -46,13 +46,15 @@ const bridgeProtocol = new Usdt0ProtocolEvm(account, {
| Method | Description | Returns | Throws |
|--------|-------------|---------|--------|
-| `bridge(options, config?)` | Bridges tokens to another blockchain | `Promise` | If no provider or fee exceeds max |
+| `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.
-Before calling `bridge()`, approve the token allowance for the source-chain bridge spender. If you pass `oftContractAddress`, use the same address as the approval `spender`.
+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
@@ -62,8 +64,7 @@ Before calling `bridge()`, approve the token allowance for the source-chain brid
- `amount` (number | bigint): Amount to bridge in token base units
- `oftContractAddress` (string, optional): Custom OFT contract address to use instead of auto-resolving from the source chain
- `dstEid` (number, optional): Custom LayerZero destination endpoint ID override
-- `config` (`Pick & Pick`, optional): Override configuration for ERC-4337 accounts
- - `paymasterToken` (`{ address: string }`, optional): Paymaster token configuration for gas fees
+- `config` (`Erc4337BridgeConfig`, optional): ERC-4337 gas-payment overrides plus optional `bridgeMaxFee`
- `bridgeMaxFee` (number | bigint, optional): Override maximum bridge fee
**Returns:** `Promise` - Bridge operation result
@@ -71,18 +72,20 @@ Before calling `bridge()`, approve the token allowance for the source-chain brid
**Throws:**
- Error if account is read-only
- Error if no provider is configured
-- Error if bridge fee exceeds maximum allowed
+- Error if the combined fee value is equal to or greater than `bridgeMaxFee`
**Example:**
```javascript
// Standard EVM account
-await account.approve({
+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 bridgeProtocol.bridge({
+const result = await standardBridgeProtocol.bridge({
targetChain: 'arbitrum',
recipient: '0x...', // Recipient address
token: '0x...', // ₮ contract address
@@ -91,47 +94,41 @@ const result = await bridgeProtocol.bridge({
})
console.log('Bridge hash:', result.hash)
-console.log('Total fee:', result.fee)
+console.log('Account transaction fee:', result.fee)
console.log('Bridge fee:', result.bridgeFee)
-// ERC-4337 account
-await account.approve({
- token: '0x...', // USDT contract address
- spender: '0x...', // OFT or bridge spender address
- amount: 1000000n
-})
+// ERC-4337 account: approval is bundled automatically
+const erc4337BridgeProtocol = new Usdt0ProtocolEvm(erc4337Account)
-const result2 = await bridgeProtocol.bridge({
+const result2 = await erc4337BridgeProtocol.bridge({
targetChain: 'arbitrum',
recipient: '0x...', // Recipient address
token: '0x...', // USDT contract address
amount: 1000000n,
- oftContractAddress: '0x...' // Same address used as approval spender
+ oftContractAddress: '0x...' // Optional custom OFT contract
}, {
- paymasterToken: { address: '0x...' }, // Paymaster token configuration
- bridgeMaxFee: 1000000000000000n
+ paymasterToken: { address: '0x...' } // Paymaster token configuration
})
console.log('Bridge hash:', result2.hash) // Single hash for bundled operations
-console.log('Total fee:', result2.fee)
+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.
-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()`.
+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` (`Pick`, optional): Override configuration for ERC-4337 accounts
- - `paymasterToken` (`{ address: string }`, optional): Paymaster token configuration for gas fees
+- `config` (`Erc4337QuoteConfig`, optional): ERC-4337 gas-payment overrides
**Returns:** `Promise>` - Bridge cost estimate
**Throws:** Error if no provider is configured
-**Example:**
+**Standard-account example:**
```javascript
const quote = await bridgeProtocol.quoteBridge({
targetChain: 'polygon',
@@ -140,11 +137,11 @@ const quote = await bridgeProtocol.quoteBridge({
amount: 1000000n
})
-console.log('Estimated fee:', quote.fee, 'wei')
-console.log('Bridge fee:', quote.bridgeFee, 'wei')
+console.log('Estimated transaction fee:', quote.fee)
+console.log('Bridge fee:', quote.bridgeFee)
// Check if fees are acceptable
-if (quote.fee + quote.bridgeFee > 1000000000000000n) {
+if (quote.fee + quote.bridgeFee >= 1000000000000000n) {
console.log('Bridge fees too high')
} else {
// Proceed with bridge
@@ -164,6 +161,35 @@ if (quote.fee + quote.bridgeFee > 1000000000000000n) {
}
```
+#### `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
@@ -184,8 +210,8 @@ interface BridgeOptions {
```typescript
interface BridgeResult {
hash: string; // Main bridge transaction hash
- fee: bigint; // Total gas cost in wei
- bridgeFee: bigint; // Bridge protocol fee in wei
+ 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
}
```
@@ -193,17 +219,60 @@ interface BridgeResult {
```typescript
interface BridgeProtocolConfig {
- bridgeMaxFee?: number | bigint; // Maximum total bridge cost in wei
+ 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
}
```
-### EvmErc4337WalletConfig
+### 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
-interface EvmErc4337WalletConfig {
- paymasterToken?: { // Token to use for paying gas fees
- address: string;
- };
+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
}
```
@@ -212,13 +281,13 @@ interface EvmErc4337WalletConfig {
The bridge protocol supports the following chains:
**Source Chains (EVM):**
-- `'ethereum'` (Chain ID: 1)
-- `'arbitrum'` (Chain ID: 42161) - ERC-4337 support
+- `'ethereum'` (Chain ID: 1) - ERC-4337 helper support
+- `'arbitrum'` (Chain ID: 42161) - ERC-4337 helper support
- `'optimism'` (Chain ID: 10)
-- `'polygon'` (Chain ID: 137)
+- `'polygon'` (Chain ID: 137) - ERC-4337 helper support
- `'berachain'` (Chain ID: 80094)
- `'ink'` (Chain ID: 57073)
-- `'plasma'` (Chain ID: 9745)
+- `'plasma'` (Chain ID: 9745) - ERC-4337 helper support
- `'conflux'` (Chain ID: 1030)
- `'corn'` (Chain ID: 21000000)
- `'avalanche'` (Chain ID: 43114)
@@ -235,12 +304,14 @@ The bridge protocol supports the following chains:
- `'unichain'` (Chain ID: 130)
- `'xlayer'` (Chain ID: 196)
-**Destination Chains:**
+**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:
@@ -294,9 +365,7 @@ async function bridgeTokens() {
})
// Create bridge protocol
- const bridgeProtocol = new Usdt0ProtocolEvm(account, {
- bridgeMaxFee: 1000000000000000n
- })
+ const bridgeProtocol = new Usdt0ProtocolEvm(account)
// Get quote first
const quote = await bridgeProtocol.quoteBridge({
@@ -320,7 +389,7 @@ async function bridgeTokens() {
recipient: '0x...', // Recipient address
token: '0x...', // USDT contract address
amount: 1000000n,
- oftContractAddress: '0x...' // Same address used as approval spender
+ oftContractAddress: '0x...' // Optional custom OFT contract
})
console.log('Bridge result:', result)
@@ -397,28 +466,20 @@ async function gaslessBridge() {
})
// Create bridge protocol
- const bridgeProtocol = new Usdt0ProtocolEvm(account, {
- bridgeMaxFee: 1000000000000000n
- })
-
- // Bridge with gasless transactions
- await account.approve({
- token: '0x...', // USDT contract address
- spender: '0x...', // OFT or bridge spender address
- amount: 1000000n
- })
+ const 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...' // Same address used as approval spender
+ oftContractAddress: '0x...' // Optional custom OFT contract
}, {
paymasterToken: { address: '0x...' } // Paymaster token configuration
})
- console.log('Gasless bridge result:', result)
+ console.log('ERC-4337 bridge result:', result)
return result
}
```
diff --git a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/configuration.mdx b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/configuration.mdx
index 4b16a36b..b569d83d 100644
--- a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/configuration.mdx
+++ b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/configuration.mdx
@@ -21,7 +21,7 @@ const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
// Create bridge protocol with configuration
const bridgeProtocol = new Usdt0ProtocolEvm(account, {
- bridgeMaxFee: 1000000000000000n // Optional: Maximum bridge fee in wei
+ bridgeMaxFee: 1000000000000000n // Optional standard-account cap in source native base units
})
```
@@ -60,16 +60,21 @@ const bridgeProtocol = new Usdt0ProtocolEvm(account, {
### Bridge Max Fee
-The `bridgeMaxFee` option sets a maximum limit for total bridge costs to prevent unexpectedly high fees.
+The `bridgeMaxFee` option rejects `bridge()` when the implementation's `fee + bridgeFee` value is equal to or greater than the cap.
**Type:** `number | bigint` (optional)
-**Unit:** Wei (1 ETH = 1000000000000000000 Wei)
+
+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 = {
- // Set maximum bridge fee to 0.001 ETH
+ // Standard Ethereum account: reject fee + bridgeFee at or above 0.001 ETH
bridgeMaxFee: 1000000000000000n,
}
@@ -114,37 +119,40 @@ const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
provider: window.ethereum
})
-// Option 3: Using custom JsonRpcProvider
-import { JsonRpcProvider } from 'ethers'
-const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
- provider: new JsonRpcProvider('https://eth.drpc.org')
-})
```
+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
-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
+ oftContractAddress: '0x...' // Optional custom OFT contract
}, {
- paymasterToken: { address: '0x...' }, // Paymaster token for gasless transactions
- bridgeMaxFee: 1000000000000000n // Override maximum bridge fee
+ 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.
@@ -155,18 +163,12 @@ The `paymasterToken` option specifies which token to use for paying gas fees in
**Example:**
```javascript
-await account.approve({
- token: '0x...', // USDT contract address
- spender: '0x...', // OFT or bridge spender address
- amount: 1000000n
-})
-
const result = await bridgeProtocol.bridge({
targetChain: 'arbitrum',
recipient: '0x...', // Recipient address
token: '0x...', // USDT contract address
amount: 1000000n,
- oftContractAddress: '0x...' // Same address used as approval spender
+ oftContractAddress: '0x...' // Optional custom OFT contract
}, {
paymasterToken: {
address: '0x...' // Paymaster token address
@@ -197,7 +199,7 @@ const polygonAccount = new WalletAccountEvm(seedPhrase, "0'/0/0", {
## Bridge Options
-When calling the bridge method, you need to provide 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 = {
@@ -261,7 +263,7 @@ The optional `dstEid` option lets you override the default LayerZero destination
## Error Handling
-The bridge protocol will throw errors for invalid configurations:
+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 {
diff --git a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-cross-ecosystem.mdx b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-cross-ecosystem.mdx
index 05b51185..2cc02289 100644
--- a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-cross-ecosystem.mdx
+++ b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-cross-ecosystem.mdx
@@ -7,9 +7,20 @@ This guide covers [prerequisites](#prerequisites) and how to [bridge to Solana](
## Prerequisites
-A [`Usdt0ProtocolEvm`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#usdt0protocolevm) backed by a non-read-only EVM account, with enough USD₮ (and native gas for non-4337 accounts) on the source chain. Approve the source-chain bridge spender before calling [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config). Recipient strings must match each network’s address encoding.
+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.
-For `USDT_BRIDGE_SPENDER_ADDRESS`, use the source-chain OFT or bridge contract for the route. See [Bridge Tokens](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens#prerequisites) for address sources.
+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
diff --git a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens.mdx b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens.mdx
index 2ebeb9c8..1e4a7aa3 100644
--- a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens.mdx
+++ b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-tokens.mdx
@@ -3,7 +3,7 @@ title: Bridge Tokens
description: EVM-to-EVM bridging, quotes, fee caps, and optional OFT or endpoint overrides.
---
-This guide covers [prerequisites](#prerequisites), how to [run a standard EVM-to-EVM bridge](#run-a-standard-evm-to-evm-bridge), [quote bridge fees](#quote-bridge-fees), [override OFT routing](#override-oft-contract-and-destination-endpoint), and [set `bridgeMaxFee`](#cap-fees-with-bridgemaxfee) on the protocol.
+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
@@ -37,8 +37,8 @@ const result = await bridgeProtocol.bridge({
})
console.log('Bridge transaction hash:', result.hash)
-console.log('Total fee:', result.fee, 'wei')
-console.log('Bridge fee:', result.bridgeFee, 'wei')
+console.log('Transaction fee:', result.fee, 'source native base units')
+console.log('Bridge fee:', result.bridgeFee, 'source native base units')
```
@@ -57,8 +57,8 @@ const quote = await bridgeProtocol.quoteBridge({
amount: 1000000n
})
-console.log('Estimated fee:', quote.fee, 'wei')
-console.log('Bridge fee:', quote.bridgeFee, 'wei')
+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).
@@ -106,7 +106,7 @@ const customQuote = await bridgeProtocol.quoteBridge({
dstEid: Number(process.env.CUSTOM_DST_EID)
})
-console.log('Custom route quote fee:', customQuote.fee, 'wei')
+console.log('Custom route transaction fee:', customQuote.fee, 'source native base units')
```
@@ -115,7 +115,7 @@ Invalid pairings of `oftContractAddress` and `dstEid` fail at execution time. Va
## Cap fees with bridgeMaxFee
-You can pass `bridgeMaxFee` into the [`new Usdt0ProtocolEvm(account, config?)`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#constructor) constructor so [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) rejects operations whose cost exceeds the cap:
+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'
@@ -126,7 +126,7 @@ const cappedBridge = new Usdt0ProtocolEvm(account, {
```
-If the quote exceeds `bridgeMaxFee`, [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) throws (for example when the message includes `Exceeded maximum fee`). ERC-4337 accounts can also pass `bridgeMaxFee` in the second argument to [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config); see [Bridge with ERC-4337](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337).
+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
diff --git a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337.mdx b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337.mdx
index 6efddbbd..4dc88385 100644
--- a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337.mdx
+++ b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337.mdx
@@ -9,6 +9,7 @@ This guide covers [prerequisites](#prerequisites), how to [create an ERC-4337 ac
* `@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
@@ -35,27 +36,19 @@ You can wrap that account with the [`new Usdt0ProtocolEvm(account, config?)`](/s
```javascript title="Usdt0ProtocolEvm with ERC-4337 account"
import Usdt0ProtocolEvm from '@tetherto/wdk-protocol-bridge-usdt0-evm'
-const bridgeProtocol = new Usdt0ProtocolEvm(account, {
- bridgeMaxFee: 1000000000000000n
-})
+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 optional `bridgeMaxFee` override) as described in the [methods table](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#methods). Approve the source-chain bridge spender first, then call [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config). Each call returns one hash for the submitted user operation.
+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 = '0xdac17f958d2ee523a2206206994597c13d831ec7'
+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' }
-await account.approve({
- token: USDT_TOKEN_ADDRESS,
- spender: USDT0_OFT_ADDRESS,
- amount
-})
-
const result = await bridgeProtocol.bridge(
{
targetChain: 'polygon',
@@ -65,18 +58,21 @@ const result = await bridgeProtocol.bridge(
oftContractAddress: USDT0_OFT_ADDRESS
},
{
- paymasterToken,
- bridgeMaxFee: 1000000000000000n
+ paymasterToken
}
)
console.log('Bridge hash:', result.hash)
-console.log('Total fee:', result.fee)
+console.log('Account fee:', result.fee)
console.log('Bridge fee:', result.bridgeFee)
```
-The approval and bridge calls are separate user operations. Persist both hashes if your app needs to audit the complete flow.
+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.
diff --git a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/get-started.mdx b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/get-started.mdx
index 4f796704..6a6ed0ad 100644
--- a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/get-started.mdx
+++ b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/get-started.mdx
@@ -38,7 +38,7 @@ const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
## Instantiate the bridge protocol
-You can create a [`Usdt0ProtocolEvm`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference) instance with the [`new Usdt0ProtocolEvm(account, config?)`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference) constructor. Optional `bridgeMaxFee` caps the total bridge cost in wei (see [BridgeProtocolConfig](/sdk/bridge-modules/bridge-usdt0-evm/api-reference)):
+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'
@@ -48,6 +48,8 @@ const bridgeProtocol = new Usdt0ProtocolEvm(account, {
})
```
+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).
@@ -83,7 +85,7 @@ Bridge operations use EVM source chains listed in the [API reference](/sdk/bridg
| Unichain | `unichain` | 130 |
| XLayer | `xlayer` | 196 |
-Arbitrum supports ERC-4337 workflows (see [Bridge with ERC-4337](/sdk/bridge-modules/bridge-usdt0-evm/guides/bridge-with-4337)).
+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**
diff --git a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/handle-errors.mdx b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/handle-errors.mdx
index e38838f2..72b65c12 100644
--- a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/handle-errors.mdx
+++ b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/guides/handle-errors.mdx
@@ -7,11 +7,11 @@ This guide describes [errors thrown by the bridge](#errors-from-the-bridge-proto
## Errors from the bridge protocol
-Calls to [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) and [`quoteBridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#quotebridgeoptions-config) throw when the account is read-only, no provider is configured, the route is invalid, fees exceed [`bridgeMaxFee`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeprotocolconfig), or the destination matches the source chain. The [API reference](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#error-handling) lists representative `error.message` substrings.
+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
-You can wrap [`bridge()`](/sdk/bridge-modules/bridge-usdt0-evm/api-reference#bridgeoptions-config) in `try/catch` and inspect `error.message` for stable substrings:
+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 {
diff --git a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/index.mdx b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/index.mdx
index 37caa4e0..4696d29c 100644
--- a/content/docs/sdk/bridge-modules/bridge-usdt0-evm/index.mdx
+++ b/content/docs/sdk/bridge-modules/bridge-usdt0-evm/index.mdx
@@ -11,8 +11,8 @@ Use the USD₮0 bridge module to move USD₮0 and XAU₮0 across supported chain
- **Cross-Chain Bridge**: Move USD₮0 tokens between supported blockchains
- **LayerZero Integration**: Uses LayerZero protocol for secure cross-chain transfers
-- **Expanded Multi-Chain Support**: Bridge across 25+ networks including all major EVM chains
-- **Non-EVM Destinations**: Bridge to Solana, TON, and TRON from any supported EVM chain
+- **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
@@ -60,10 +60,33 @@ All source chains above, plus:
| 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
diff --git a/content/docs/sdk/bridge-modules/index.mdx b/content/docs/sdk/bridge-modules/index.mdx
index 6d9d699c..1e03c3cf 100644
--- a/content/docs/sdk/bridge-modules/index.mdx
+++ b/content/docs/sdk/bridge-modules/index.mdx
@@ -31,6 +31,6 @@ You can also:
## Swidge provider routes
-For new swap or bridge provider integrations, prefer the [Swidge protocol interface](/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 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.
diff --git a/content/docs/sdk/community-modules/index.mdx b/content/docs/sdk/community-modules/index.mdx
index a0d4a118..d0f85c06 100644
--- a/content/docs/sdk/community-modules/index.mdx
+++ b/content/docs/sdk/community-modules/index.mdx
@@ -18,11 +18,17 @@ Tether and the WDK Team do not endorse or assume responsibility for their code,
| 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, Bitcoin, and capability-gated TON, Tron, and Solana source execution through the Symbiosis API | [Docs](/sdk/swidge-modules/swidge-symbiosis/) | [Symbiosis](https://symbiosis.finance/) |
---
@@ -34,7 +40,7 @@ Want to extend WDK with your own custom module? Use the `create-wdk-module` CLI
npx @tetherto/create-wdk-module@latest
```
-The CLI generates source files, tests, TypeScript type definitions, and CI workflows for all six module types (wallet, swap, bridge, lending, fiat, swidge). See the [Create WDK Module documentation](/tools/create-wdk-module) for the full guide, CLI options, and generated project structure.
+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:
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/api-reference.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/api-reference.mdx
new file mode 100644
index 00000000..7da503f9
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/api-reference.mdx
@@ -0,0 +1,214 @@
+---
+title: RGB Lightning wallet API reference
+description: Public API reference for @utexo/wdk-rgb-lightning 0.1.0-beta.15.
+docType: reference
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/configuration.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/configuration.mdx
new file mode 100644
index 00000000..bf1951a9
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/configuration.mdx
@@ -0,0 +1,150 @@
+---
+title: RGB Lightning wallet configuration
+description: Configure the beta.15 node, persistent state, native unlock services, VSS, LSP, and signer policy.
+docType: reference
+schemaType: TechArticle
+icon: Settings
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/balances-history.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/balances-history.mdx
new file mode 100644
index 00000000..d2e47ff7
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/balances-history.mdx
@@ -0,0 +1,100 @@
+---
+title: Read RGB Lightning balances and history
+description: Query Bitcoin, RGB, Lightning payment, transaction, and terminal receipt state.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/get-started.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/get-started.mdx
new file mode 100644
index 00000000..1463ddad
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/get-started.mdx
@@ -0,0 +1,105 @@
+---
+title: Get started with RGB Lightning
+description: Install @utexo/wdk-rgb-lightning beta.15 with a matching native peer and unlock the node.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup.mdx
new file mode 100644
index 00000000..2040a304
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/handle-errors-cleanup.mdx
@@ -0,0 +1,130 @@
+---
+title: Handle errors and clean up the node
+description: Handle typed RGB Lightning boundaries, raw RLN failures, ambiguous writes, and terminal manager cleanup.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/lightning-payments.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/lightning-payments.mdx
new file mode 100644
index 00000000..b740076c
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/lightning-payments.mdx
@@ -0,0 +1,105 @@
+---
+title: Create and send Lightning payments
+description: Create BOLT11 and HODL invoices, send payments or keysend, and inspect Lightning payment state.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/lsp-lightning-address-apay.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/lsp-lightning-address-apay.mdx
new file mode 100644
index 00000000..10f78fe4
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/lsp-lightning-address-apay.mdx
@@ -0,0 +1,143 @@
+---
+title: Use an LSP, Lightning Address, and APay
+description: Use the beta.15 LSP client, composed RGB flows, LNURL-pay helpers, Lightning Address, and asynchronous payments.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account.mdx
new file mode 100644
index 00000000..d64580c6
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/manage-node-account.mdx
@@ -0,0 +1,92 @@
+---
+title: Manage the RGB Lightning node account
+description: Manage node identity, lock state, addresses, read-only access, seed derivation, and lifecycle cleanup.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels.mdx
new file mode 100644
index 00000000..98f93bef
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/peers-channels.mdx
@@ -0,0 +1,116 @@
+---
+title: Connect peers and manage channels
+description: Connect Lightning peers and open, inspect, or close standard, RGB, and trusted virtual channels.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/rgb-assets.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/rgb-assets.mdx
new file mode 100644
index 00000000..34a1980a
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/rgb-assets.mdx
@@ -0,0 +1,116 @@
+---
+title: Receive and send RGB assets
+description: Query existing RGB assets, create receive invoices, and transfer assets through the RGB Lightning node.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/send-btc-utxos.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/send-btc-utxos.mdx
new file mode 100644
index 00000000..55b31ce8
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/send-btc-utxos.mdx
@@ -0,0 +1,86 @@
+---
+title: Send Bitcoin and manage UTXOs
+description: Quote and send Bitcoin, inspect history, and use native UTXO operations through the RGB Lightning account.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/sign-verify-messages.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/sign-verify-messages.mdx
new file mode 100644
index 00000000..6a91137a
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/sign-verify-messages.mdx
@@ -0,0 +1,74 @@
+---
+title: Sign and verify Lightning messages
+description: Sign domain-separated messages through VLS and verify them with full or read-only RGB Lightning accounts.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/vss-backup-recovery.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/vss-backup-recovery.mdx
new file mode 100644
index 00000000..476df5db
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/guides/vss-backup-recovery.mdx
@@ -0,0 +1,110 @@
+---
+title: Back up and recover with VSS
+description: Configure encrypted VSS snapshots, force checkpoints, recover with the original seed, and handle ownership fences safely.
+docType: how-to
+schemaType: TechArticle
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/index.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/index.mdx
new file mode 100644
index 00000000..83d2e3ff
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/index.mdx
@@ -0,0 +1,87 @@
+---
+title: RGB Lightning wallet
+description: Run a community-maintained RGB-over-Lightning wallet with LDK channels, invoices, payments, VSS, and LSP flows.
+docType: explanation
+schemaType: TechArticle
+---
+
+`@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.
+
+
diff --git a/content/docs/sdk/community-modules/wdk-rgb-lightning/usage.mdx b/content/docs/sdk/community-modules/wdk-rgb-lightning/usage.mdx
new file mode 100644
index 00000000..6994a11a
--- /dev/null
+++ b/content/docs/sdk/community-modules/wdk-rgb-lightning/usage.mdx
@@ -0,0 +1,57 @@
+---
+title: RGB Lightning wallet usage
+description: Task-focused guides for @utexo/wdk-rgb-lightning 0.1.0-beta.15.
+docType: how-to
+schemaType: TechArticle
+icon: BookOpen
+---
+
+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)
diff --git a/content/docs/sdk/community-modules/wdk-wallet-cosmos/api-reference.mdx b/content/docs/sdk/community-modules/wdk-wallet-cosmos/api-reference.mdx
index 05919dbd..d8a8cf5b 100644
--- a/content/docs/sdk/community-modules/wdk-wallet-cosmos/api-reference.mdx
+++ b/content/docs/sdk/community-modules/wdk-wallet-cosmos/api-reference.mdx
@@ -1,114 +1,282 @@
---
title: Cosmos wallet API reference
-description: API reference for the independently maintained @base58-io/wdk-wallet-cosmos module.
+description: Public API reference for @base58-io/wdk-wallet-cosmos version 1.0.0-beta.4.
docType: reference
-schemaType: TechArticle
+schemaType: APIReference
+icon: Code
---
-This page summarizes the public API exposed by `@base58-io/wdk-wallet-cosmos` based on the package README, `package.json`, generated TypeScript declarations, and source files from commit `b924dba`.
+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).
-This module is 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. Review the package and use your own judgment before using it with real funds.
+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` |
-| Repository | [base58-io/wdk-wallet-cosmos](https://github.com/base58-io/wdk-wallet-cosmos) |
-| Main entry | `index.js` |
-| Bare entry | `bare.js` |
-| Types | `types/index.d.ts` |
+| 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` |
-## Exports
+The package export map exposes the root module and a `./package` subpath for `package.json`. Internal files are not supported public entrypoints.
-| Export | Description |
-|---|---|
-| `default` | `WalletManagerCosmos` |
-| `WalletAccountCosmos` | Cosmos wallet account implementation |
-| `CosmosWalletConfig` | Wallet configuration type |
-| `ResolvedChainConfig` | Resolved chain configuration type |
-| `resolveChainConfig(config)` | Resolves chain config from chain-registry or custom fields |
-| `getAvailableChains()` | Returns chain names available through the bundled chain-registry data |
-| `isKnownChain(chainName)` | Returns whether a chain name exists in the registry data |
-
-## `CosmosWalletConfig`
+## Root exports
-| Field | Required | Description |
-|---|---:|---|
-| `chainName` | No | Chain name from chain-registry, such as `cosmoshub`, `osmosis`, or `juno`. |
-| `rpcEndpoints` | No | RPC endpoint URLs. If provided, they override registry endpoints. |
-| `retryCount` | No | Maximum retry rounds for RPC fallback. Default is `3`. |
-| `retryDelay` | No | Base retry delay in milliseconds. Default is `150`. |
-| `addressPrefix` | No | Bech32 address prefix. Defaults to `cosmos` when not resolved from registry. |
-| `nativeDenom` | No | Native token denomination. Defaults to `uatom` when not resolved from registry. |
-| `coinType` | No | BIP-44 coin type. Defaults to `118` when not resolved from registry. |
-| `gasPrice` | No | Gas price with denomination, such as `0.025uatom`. |
-| `transferMaxFee` | No | Maximum fee amount for transfer operations. |
-| `ibcChannels` | No | IBC channel map keyed by destination Bech32 prefix. |
+| 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
-import WalletManagerCosmos from '@base58-io/wdk-wallet-cosmos'
+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
-const manager = new WalletManagerCosmos(seed, config)
+| 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()
```
-| Method or property | Description |
-|---|---|
-| `constructor(seed, config)` | Creates a manager from a BIP-39 seed phrase or seed bytes. |
-| `getAccount(index = 0)` | Returns the Cosmos account at a BIP-44 index. |
-| `getAccountByPath(path)` | Returns the Cosmos account for a specific BIP-44 derivation suffix. |
-| `getFeeRates()` | Returns normal and fast fee rates from chain-registry fee tiers or configured gas price. Requires RPC configuration. |
-| `isDisposed` | Returns whether the manager has been disposed. |
-| `dispose()` | Disposes cached wallet accounts and clears manager state. |
+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`
-| Method or property | Description |
+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
diff --git a/content/docs/sdk/swidge-modules/swidge-rhinofi/configuration.mdx b/content/docs/sdk/swidge-modules/swidge-rhinofi/configuration.mdx
index e20eed7d..6c384811 100644
--- a/content/docs/sdk/swidge-modules/swidge-rhinofi/configuration.mdx
+++ b/content/docs/sdk/swidge-modules/swidge-rhinofi/configuration.mdx
@@ -6,6 +6,12 @@ schemaType: TechArticle
icon: 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.
+
+
`RhinofiProtocol` requires a Rhino.fi API key. The SDK authenticates every call, including quote and discovery calls. Create and manage API keys in the [Rhino.fi Console](https://console.rhino.fi/).
```javascript
diff --git a/content/docs/sdk/swidge-modules/swidge-rhinofi/index.mdx b/content/docs/sdk/swidge-modules/swidge-rhinofi/index.mdx
index 6ae37b62..a37b22a3 100644
--- a/content/docs/sdk/swidge-modules/swidge-rhinofi/index.mdx
+++ b/content/docs/sdk/swidge-modules/swidge-rhinofi/index.mdx
@@ -5,6 +5,12 @@ docType: explanation
schemaType: TechArticle
---
+
+Community modules are developed and maintained independently by third-party contributors.
+
+Tether and the WDK Team do not endorse or assume responsibility for their code, security, or maintenance. Use your own judgment and proceed at your own risk.
+
+
The Rhino.fi Swidge module lets WDK EVM accounts quote and execute cross-chain swaps and bridges through Rhino.fi using the shared `SwidgeProtocol` interface.
Use this module when an app needs authenticated Rhino.fi quotes, EVM source-chain execution, live token discovery, status polling, and WDK-standard fee and status shapes.
diff --git a/content/docs/sdk/swidge-modules/swidge-rhinofi/usage.mdx b/content/docs/sdk/swidge-modules/swidge-rhinofi/usage.mdx
index a68e3786..58805603 100644
--- a/content/docs/sdk/swidge-modules/swidge-rhinofi/usage.mdx
+++ b/content/docs/sdk/swidge-modules/swidge-rhinofi/usage.mdx
@@ -6,6 +6,12 @@ schemaType: TechArticle
icon: BookOpen
---
+
+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
```bash
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/api-reference.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/api-reference.mdx
new file mode 100644
index 00000000..95e02fe7
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/api-reference.mdx
@@ -0,0 +1,290 @@
+---
+title: Symbiosis Swidge API Reference
+description: API reference for @symbiosis-finance/wdk-protocol-swidge-symbiosis 1.3.0.
+docType: reference
+schemaType: APIReference
+icon: Code
+---
+
+
+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 exports
+
+```javascript
+import SymbiosisProtocol, {
+ ApiError,
+ ConfigurationError,
+ ExactOutNotSupportedError,
+ FeeLimitExceededError,
+ ReadOnlyAccountError,
+ SymbiosisError,
+ TransactionError,
+ UnsupportedChainError,
+ UnsupportedRouteError,
+ UnsupportedTokenError,
+ ValidationError
+} from '@symbiosis-finance/wdk-protocol-swidge-symbiosis'
+```
+
+The package exports `SymbiosisProtocol` as both its default export and a named export. It also re-exports `ISwidgeProtocol` from `@tetherto/wdk-wallet/protocols`.
+
+This reference covers release [`1.3.0`](https://github.com/symbiosis-finance/wdk-protocol-swidge-symbiosis/tree/v1.3.0).
+
+## `SymbiosisProtocol`
+
+`SymbiosisProtocol` extends `SwidgeProtocol`.
+
+### Constructor
+
+```typescript
+new SymbiosisProtocol(
+ account?: IWalletAccount | IWalletAccountReadOnly,
+ config?: SymbiosisProtocolConfig
+)
+```
+
+| Account | Available operations |
+|---|---|
+| Writable account with the route's required methods | Discovery, quote, status, and supported source execution |
+| Read-only account | Discovery and status; quoting uses the account's address as the request sender |
+| `undefined` | Discovery and status; quoting requires `recipient` to supply the request sender |
+
+`chain` is optional in the constructor type, but `quoteSwidge()` and `swidge()` throw `ConfigurationError` when it is absent.
+
+### Configuration type
+
+```typescript
+type SymbiosisProtocolConfig = {
+ chain?: string | number
+ apiUrl?: string
+ timeoutMs?: number
+ partnerId?: string
+ defaultSlippage?: number
+ partnerAddress?: string
+ refundAddress?: string
+ skipApproval?: boolean
+ maxNetworkFeeBps?: number | bigint
+ maxProtocolFeeBps?: number | bigint
+}
+```
+
+See [Configuration](/sdk/swidge-modules/swidge-symbiosis/configuration) for defaults, validation boundaries, and execution effects.
+
+## Methods
+
+| Method | Side effects | Description |
+|---|---|---|
+| `quoteSwidge(options)` | Provider API reads only | Returns an indicative exact-input quote. |
+| `swidge(options, config?)` | Can approve and broadcast one or more source transactions | Requests a fresh execution response and submits its source route through the wallet account. |
+| `getSwidgeStatus(id, options?)` | Provider API read | Maps Symbiosis settlement state to a WDK status. |
+| `getSupportedChains()` | Provider API reads, cached | Returns provider-listed chains with WDK chain metadata. |
+| `getSupportedTokens(options?)` | Provider API read, cached | Returns provider-listed tokens, optionally filtered to one chain. |
+
+### `quoteSwidge(options)`
+
+```typescript
+quoteSwidge(options: SwidgeOptions): Promise
+```
+
+Builds an exact-input request and calls `/v2/quote`. The method does not reserve or bind the result for `swidge()`.
+
+Package-specific errors include:
+
+- `ConfigurationError` when `chain` is missing;
+- `ValidationError` when `fromTokenAmount` is missing, is not an integer, or is not positive; a token identifier is not a string; or no account address or `recipient` supplies the request sender;
+- `ExactOutNotSupportedError` when `toTokenAmount` is present;
+- `UnsupportedChainError` or `UnsupportedTokenError` when discovery cannot resolve an identifier;
+- `ApiError` when the API returns a non-2xx response, times out, or fails before a response is received. Network failures and timeouts carry `status: 0`.
+
+The method can also propagate an error from `account.getAddress()`.
+
+### `swidge(options, config?)`
+
+```typescript
+swidge(
+ options: SwidgeOptions,
+ config?: SwidgeProtocolConfig
+): Promise
+```
+
+Requires an account with `sendTransaction()`. It calls `/v2/swap`, checks the applicable fee caps, performs the route-specific approval or source-payment steps, and returns after source broadcast.
+
+For `ton`, `tron`, and `solana` source routes, the method first probes the bound account for the transaction format the route requires (raw BoC message bodies, smart contract calls plus TRC-20 approvals, and serialized transactions respectively) and throws `UnsupportedRouteError` when the capability is missing or a `ton` route needs more than one message.
+
+The optional second argument overrides `maxNetworkFeeBps` and `maxProtocolFeeBps` for this execution.
+
+
+`swidge()` does not consume the preceding `quoteSwidge()` response or expose its fresh `/v2/swap` response for a separate confirmation. After its fee checks, it proceeds internally to the required wallet writes.
+
+
+### `getSwidgeStatus(id, options?)`
+
+```typescript
+getSwidgeStatus(
+ id: string,
+ options?: SwidgeStatusOptions
+): Promise
+```
+
+Pass the ID returned by `swidge()`:
+
+```text
+:
+```
+
+For a bare transaction hash, pass `options.fromChain` or configure the instance source `chain`. The provider does not use other status hints.
+
+The resolved source-chain ID and transaction hash are URL-encoded before the provider builds the status endpoint path.
+
+HTTP `404` is returned as `pending`, not as `ApiError`.
+
+### `getSupportedChains()`
+
+```typescript
+getSupportedChains(): Promise
+```
+
+Calls the Symbiosis chain and token endpoints and maps each in-scope chain to:
+
+| Field | Type | Description |
+|---|---|---|
+| `id` | `number` | Numeric Symbiosis chain ID |
+| `name` | `string` | Provider chain name |
+| `type` | `string` | `evm`, `utxo`, `tvm`, `tron`, or `svm` |
+| `nativeToken` | `string` | Native token symbol when present in the token catalog |
+
+Monero and Zcash are filtered out because their provider routes use third-party custodial integrations outside this module's scope.
+
+### `getSupportedTokens(options?)`
+
+```typescript
+getSupportedTokens(
+ options?: SwidgeSupportedTokensOptions
+): Promise
+```
+
+The chain filter is resolved as `options.toChain ?? options.fromChain`. `fromToken` and other route context do not narrow the result.
+
+| Field | Type | Description |
+|---|---|---|
+| `token` | `string` | Native-format address when present, otherwise the token symbol |
+| `chain` | `number` | Numeric Symbiosis chain ID |
+| `symbol` | `string` | Provider token symbol |
+| `decimals` | `number` | Base-unit precision |
+| `address` | `string \| undefined` | Token address when it is not the native asset |
+| `name` | `string \| undefined` | Provider token name when supplied |
+
+The response is a token catalog, not proof of pair liquidity. Request a quote for route availability.
+
+## Relevant `SwidgeOptions`
+
+| Field | Type | Provider behavior |
+|---|---|---|
+| `fromToken` | `string` | Required source token address, symbol, or native-token alias |
+| `toToken` | `string` | Required destination token address, symbol, or native-token alias |
+| `toChain` | `string \| number \| undefined` | Destination chain; defaults to the configured source chain |
+| `recipient` | `string \| undefined` | Destination recipient; defaults to the bound account address. Without an account, it also supplies the request sender |
+| `refundAddress` | `string \| undefined` | Per-call refund address; overrides the constructor default |
+| `slippage` | `number \| undefined` | Decimal slippage; overrides `defaultSlippage` |
+| `fromTokenAmount` | `number \| bigint` | Required exact input in source-token base units |
+| `toTokenAmount` | `number \| bigint` | Unsupported; throws `ExactOutNotSupportedError` |
+
+The module does not validate slippage ranges or address formats. It converts `fromTokenAmount` with `BigInt` and throws `ValidationError` unless the result is greater than zero.
+
+## Quote and result fields
+
+### `SwidgeQuote`
+
+| Field | Type | Source |
+|---|---|---|
+| `fromTokenAmount` | `bigint` | Requested exact input |
+| `toTokenAmount` | `bigint` | Provider-estimated output |
+| `toTokenAmountMin` | `bigint` | Provider minimum output after slippage |
+| `fees` | `SwidgeFee[]` | Mapped provider fee entries |
+| `estimatedDuration` | `number \| undefined` | Provider estimate in seconds |
+| `priceImpact` | `number \| undefined` | Provider percentage converted to a decimal |
+
+The provider does not map a quote expiry into `SwidgeQuote`.
+
+### `SwidgeResult`
+
+| Field | Public type | Provider behavior |
+|---|---|---|
+| `id` | `string` | `:` |
+| `hash` | `string \| undefined` | Source transaction hash |
+| `fees` | `SwidgeFee[]` | Fees from the fresh execution response |
+| `transactions` | `SwidgeTransaction[] \| undefined` | Zero, one, or two EVM or Tron approval hashes followed by the source hash; a later status response returns its own source, destination, or refund transaction list |
+| `fromTokenAmount` | `bigint` | Submitted exact input |
+| `toTokenAmount` | `bigint` | Fresh provider-estimated output |
+| `toTokenAmountMin` | `bigint \| undefined` | Fresh provider minimum output |
+
+## Status mapping
+
+| Symbiosis code or response | WDK status |
+|---|---|
+| `0` | `completed` |
+| `1` | `pending` |
+| `2` | `pending` |
+| `3` | `refunded` |
+| `-1` | `pending` |
+| Unknown code | `pending` |
+| HTTP `404` | `pending` with the known source transaction |
+
+When status is `refunded`, a returned settlement transaction is labeled `refund`; otherwise it is labeled `destination`.
+
+## Fee mapping and caps
+
+| Symbiosis fee rule | WDK fee type | Cap |
+|---|---|---|
+| `description` is exactly `Partner fee` | `affiliate` | None |
+| Every other fee entry | `protocol` | `maxProtocolFeeBps` |
+
+Mapped fees include `amount`, `token`, `chain`, `description`, and `included: true`.
+
+The provider emits no `network` fee entry in this release, so `maxNetworkFeeBps` does not constrain the wallet transaction's chain fee. Inherited legacy `bridge()` results expose `0n` for their network `fee`; mapped protocol fees contribute to `bridgeFee`.
+
+Fee-cap fallback comparison uses decimal-normalized values when positive USD prices are unavailable. This is approximate when the fee token differs in unit value from the input token.
+
+## Error classes
+
+Every package-specific error extends `SymbiosisError`.
+
+| Error | When thrown | Useful fields |
+|---|---|---|
+| `SymbiosisError` | Base class for package-defined errors | Standard `Error` fields |
+| `ConfigurationError` | Required source `chain` configuration is missing | — |
+| `ValidationError` | A locally checked option, sender, token identifier, or status ID is invalid | — |
+| `ExactOutNotSupportedError` | `toTokenAmount` requests exact-output execution | — |
+| `UnsupportedChainError` | A chain ID or name is not in provider discovery | `identifier` |
+| `UnsupportedTokenError` | A token is not in the selected chain's token catalog | `identifier` |
+| `ReadOnlyAccountError` | Execution lacks a writable account, or an EVM approval is required and the account does not support approvals | — |
+| `UnsupportedRouteError` | The route's source transaction cannot be executed through the bound account: a missing wallet capability for `ton`, `tron`, or `solana`, or a multi-message `ton` route | `type` |
+| `FeeLimitExceededError` | A mapped `network` or `protocol` total exceeds its configured cap | `feeType`, `bps`, `cap` |
+| `TransactionError` | Approval receipt polling detects a revert (including a failed Tron approval receipt) or reaches its 180-second timeout | `hash` |
+| `ApiError` | The REST API returns a non-2xx response other than status lookup's special `404` handling, or a request fails or times out before a response | `status`, `response`, and `cause` for failures before a response |
+
+`ApiError.status` is `0` when no HTTP response was received. Errors thrown by wallet account methods are propagated and are not necessarily instances of `SymbiosisError`.
+
+## Inherited compatibility methods
+
+`SymbiosisProtocol` inherits:
+
+- `swap()` and `quoteSwap()`;
+- `bridge()` and `quoteBridge()`.
+
+Those methods delegate to `swidge()` and `quoteSwidge()`. For the legacy bridge shape, the provider can resolve the destination token by matching the source token symbol on the destination chain.
+
+Prefer the Swidge methods when an application needs itemized fees, provider status, combined route semantics, or explicit destination-token selection.
+
+
+
+Install, discover, quote, confirm, execute, and track routes.
+
+
+Review constructor defaults, approval behavior, fee caps, caching, and runtime constraints.
+
+
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/configuration.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/configuration.mdx
new file mode 100644
index 00000000..78413fc1
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/configuration.mdx
@@ -0,0 +1,224 @@
+---
+title: Symbiosis Swidge Configuration
+description: Configure source-chain identity, slippage, refunds, approval behavior, and fee caps for the Symbiosis community provider.
+docType: reference
+schemaType: TechArticle
+icon: 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
+
+```typescript
+new SymbiosisProtocol(account?, config?)
+```
+
+```javascript
+import SymbiosisProtocol from '@symbiosis-finance/wdk-protocol-swidge-symbiosis'
+
+const symbiosis = new SymbiosisProtocol(account, {
+ chain: 'Ethereum',
+ timeoutMs: 30_000,
+ partnerId: 'my-app',
+ defaultSlippage: 0.02,
+ refundAddress: 'bc1qRefund...',
+ maxProtocolFeeBps: 100
+})
+```
+
+| Parameter | Description |
+|---|---|
+| `account` | Optional WDK wallet account. Discovery and quote-only use can run without one; execution requires the capabilities used by the returned route. |
+| `config` | Optional `SymbiosisProtocolConfig`. `chain` becomes required before quoting or execution. |
+
+## Configuration fields
+
+| Field | Type | Default | Behavior |
+|---|---|---|---|
+| `chain` | `string \| number` | None | Symbiosis chain name or numeric ID for the bound source account. Required by `quoteSwidge()` and `swidge()`. |
+| `apiUrl` | `string` | `https://api.symbiosis.finance/crosschain` | Overrides the REST API base URL. Trailing slashes are removed. |
+| `timeoutMs` | `number` | `30000` | Aborts a Symbiosis API request after this many milliseconds. Timeout failures become `ApiError` instances with `status: 0`. |
+| `partnerId` | `string` | `'wdk'` | Sends the value in the `X-Partner-Id` header on every API request. Registered partners can receive higher API rate limits; pass `''` to omit the header. |
+| `defaultSlippage` | `number` | `0.02` | Decimal slippage tolerance used when `options.slippage` is absent. `0.02` means 2%. |
+| `partnerAddress` | `string` | None | Registered Symbiosis partner EVM address sent with quote and execution requests. |
+| `refundAddress` | `string` | None | Default refund address for deposit-address routes. `options.refundAddress` overrides it. |
+| `skipApproval` | `boolean` | `false` | Suppresses the module's automatic ERC-20 or TRC-20 approval step. |
+| `maxNetworkFeeBps` | `number \| bigint` | None | Shared network-fee cap. This release maps no provider fee to `network`, so the cap does not constrain a separate network cost. |
+| `maxProtocolFeeBps` | `number \| bigint` | None | Rejects execution when fees mapped as `protocol` exceed this many basis points of the input amount. It does not constrain fees mapped as `affiliate`. |
+
+`partnerId` identifies the integrating application in an HTTP header. `partnerAddress` is a separate fee-share address included in quote and execution request bodies.
+
+## Source chain
+
+Use a numeric ID or the exact name returned by `getSupportedChains()`:
+
+```javascript
+const byName = new SymbiosisProtocol(account, {
+ chain: 'Ethereum'
+})
+
+const byId = new SymbiosisProtocol(account, {
+ chain: 1
+})
+```
+
+The configured chain must identify the bound account's source chain. The module does not derive or verify it from the wallet account.
+
+## Chain and token identifiers
+
+Chain identifiers can be numeric Symbiosis IDs or case-insensitive names from `getSupportedChains()`.
+
+Token identifiers can be:
+
+- a provider-listed contract or asset address;
+- a token symbol on the selected chain;
+- `''`, `'native'`, or the zero address for a native token.
+
+For TON and Solana assets, token discovery returns the provider's native-format address when available.
+
+Token symbols can be ambiguous. Prefer the exact address returned by `getSupportedTokens()` and confirm route availability with `quoteSwidge()`.
+
+## Slippage and amounts
+
+The per-call `slippage` option overrides `defaultSlippage`:
+
+```javascript
+const quote = await symbiosis.quoteSwidge({
+ fromToken,
+ toToken,
+ toChain,
+ recipient,
+ fromTokenAmount: 100_000_000n,
+ slippage: 0.01
+})
+```
+
+The module converts the decimal slippage value to basis points with `Math.round(slippage * 10000)`. It does not validate the range of either slippage setting.
+
+Pass `fromTokenAmount` as a positive base-unit integer. Missing values, values that cannot be converted with `BigInt`, zero, and negative amounts throw `ValidationError` before the API request.
+
+## Recipient, partner, and refund addresses
+
+For a bound account, the source sender comes from `account.getAddress()`. `recipient` defaults to that address when omitted.
+
+Without an account, `recipient` supplies both the source sender and destination recipient:
+
+```javascript
+const quoteOnly = new SymbiosisProtocol(undefined, {
+ chain: 'Ethereum'
+})
+
+const quote = await quoteOnly.quoteSwidge({
+ fromToken: 'USDT',
+ toToken: 'USDC',
+ toChain: 'Arbitrum One',
+ recipient: '0xRecipient...',
+ fromTokenAmount: 100_000_000n
+})
+```
+
+Set a refund address suitable for a deposit-address route:
+
+```javascript
+const symbiosis = new SymbiosisProtocol(bitcoinAccount, {
+ chain: 'Bitcoin',
+ refundAddress: 'bc1qRefund...'
+})
+```
+
+Override it for one request with `options.refundAddress`.
+
+
+The module forwards `recipient`, `partnerAddress`, and `refundAddress` without validating their address formats or intended chains. Validate them in the host application.
+
+
+## Approval behavior
+
+For a non-native EVM or Tron input token, the module uses the spender returned by the fresh execution response.
+
+By default it:
+
+1. calls `getAllowance(token, spender)` when available;
+2. skips approval when allowance covers the input amount;
+3. resets a non-zero insufficient allowance to zero and waits for its receipt when the account supports receipt lookup;
+4. approves the exact input amount and waits for its receipt when supported;
+5. approves without a reset when allowance lookup fails.
+
+When allowance lookup succeeds and the account returns transaction hashes, both approval hashes are included in `result.transactions` when a reset is required. This supports tokens such as USDT on Ethereum that reject a direct non-zero-to-non-zero allowance change. If allowance lookup fails while such a token already has a non-zero allowance, the fallback direct approval can still fail.
+
+For Tron token routes, the module converts the provider's EVM-style token address to Tron hex form, requires the wallet's smart-contract-call and approval capabilities, and checks each returned approval receipt before broadcasting the route transaction. A Tron account without the required capability throws `UnsupportedRouteError`; a writable EVM account that cannot approve throws `ReadOnlyAccountError`.
+
+Disable the automatic step only when the host application manages allowance:
+
+```javascript
+const symbiosis = new SymbiosisProtocol(account, {
+ chain: 'Ethereum',
+ skipApproval: true
+})
+```
+
+`skipApproval` does not verify ERC-20 or TRC-20 allowance. Insufficient allowance can cause the subsequent transaction to fail.
+
+## Fee caps
+
+Set protocol-level defaults on the instance:
+
+```javascript
+const symbiosis = new SymbiosisProtocol(account, {
+ chain: 'Ethereum',
+ maxProtocolFeeBps: 100
+})
+```
+
+Override shared fee caps for one execution:
+
+```javascript
+await symbiosis.swidge(options, {
+ maxProtocolFeeBps: 75
+})
+```
+
+The module checks the fresh `/v2/swap` response before calling a wallet write method.
+
+When both the input token and fee token have positive USD price data, the module compares USD values. Otherwise it compares decimal-normalized token amounts. That fallback is approximate when the fee token and input token have different unit values.
+
+| Symbiosis fee rule | Mapped type | Constrained by |
+|---|---|---|
+| `description` is exactly `Partner fee` | `affiliate` | Neither available cap |
+| Every other fee entry | `protocol` | `maxProtocolFeeBps` |
+
+No returned fee maps to `network`, so `maxNetworkFeeBps` remains at zero in this provider's current fee calculation. The module does not estimate the wallet transaction's chain fee.
+
+## Discovery caching
+
+The provider caches chain and token discovery promises for ten minutes per instance. A failed request is removed from the cache and can be retried by a later call.
+
+The cache duration has no public configuration field. Construct a new provider instance when the application must bypass cached discovery.
+
+Monero and Zcash are filtered from chain discovery, token discovery, and chain resolution because their routes use third-party custodial integrations outside this module's scope.
+
+## API and runtime behavior
+
+- The default entrypoint is ESM.
+- The `bare` export initializes `bare-node-runtime` globals before loading the provider.
+- The package declares no Node.js `engines` range.
+- Requests use the runtime's global `fetch`.
+- Requests use an internal `AbortController` and time out after `timeoutMs`; network failures and timeouts throw `ApiError` with `status: 0`.
+- The provider does not retry or back off automatically.
+- `apiUrl` is normalized only by removing trailing slashes.
+
+Use a trusted `https://` endpoint for `apiUrl`. Choose a timeout appropriate for the runtime and retry cautiously, especially around execution and status polling.
+
+
+
+Discover, quote, confirm, execute, and track Symbiosis routes.
+
+
+Review exported methods, option and result fields, statuses, fees, and errors.
+
+
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/bridge-from-bitcoin.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/bridge-from-bitcoin.mdx
new file mode 100644
index 00000000..de714e42
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/bridge-from-bitcoin.mdx
@@ -0,0 +1,104 @@
+---
+title: Bridge from Bitcoin with Symbiosis
+description: Execute a Symbiosis deposit-address route from a WDK Bitcoin account with a configured refund address.
+docType: how-to
+schemaType: TechArticle
+---
+
+
+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 guide covers [how the deposit-address route works](#how-the-deposit-address-route-works), [Bitcoin account setup](#set-up-a-bitcoin-source-account), [the refund address](#configure-a-refund-address), and [execution](#execute-the-route).
+
+## How the deposit-address route works
+
+A Bitcoin source route does not sign provider calldata. `swidge()` requests a fresh execution response from the provider's swap endpoint, receives a generated deposit address, and transfers the input amount to it from the bound account. Symbiosis settles the destination side after the deposit confirms.
+
+
+The deposit address is not returned for a separate confirmation step; the transfer is sent as part of `swidge()`. Confirm the amount, recipient, refund address, selected slippage, and the indicative quote with the user before calling the method.
+
+
+The package documents the swap endpoint as rate-limited to one request per second, and Bitcoin execution uses that endpoint to generate the deposit address. Serialize executions rather than issuing them concurrently.
+
+## Set up a Bitcoin source account
+
+Create a Bitcoin account with [`WalletManagerBtc`](/sdk/wallet-modules/wallet-btc/api-reference) from `@tetherto/wdk-wallet-btc` and an Electrum client:
+
+```javascript title="Create a Bitcoin account"
+import WalletManagerBtc, { ElectrumTls } from '@tetherto/wdk-wallet-btc'
+
+const seedPhrase = process.env.WDK_SEED_PHRASE
+if (!seedPhrase) throw new Error('WDK_SEED_PHRASE is required')
+
+const client = new ElectrumTls({
+ host: 'electrum.blockstream.info',
+ port: 50002
+})
+
+const wallet = new WalletManagerBtc(seedPhrase, {
+ client,
+ network: 'bitcoin',
+ transactionMaxFee: 10_000n
+})
+
+const bitcoinAccount = await wallet.getAccount(0)
+```
+
+The public TLS endpoint is suitable for development and testing. For production, use your own Fulcrum server as described in [Electrum server configuration](/sdk/wallet-modules/wallet-btc/configuration#electrum-server-configuration). Choose an application-specific `transactionMaxFee` in satoshis; Symbiosis provider fee caps do not constrain this Bitcoin network fee.
+
+## Configure a refund address
+
+Set a Bitcoin refund address on the provider so the provider can return funds if the route cannot complete:
+
+```javascript title="Provider with a refund address"
+import SymbiosisProtocol from '@symbiosis-finance/wdk-protocol-swidge-symbiosis'
+
+const symbiosis = new SymbiosisProtocol(bitcoinAccount, {
+ chain: 'Bitcoin',
+ refundAddress: 'bc1qRefund...'
+})
+```
+
+`options.refundAddress` overrides the constructor default for one request. The module forwards the value without validating its format or chain, so validate it in the host application and make sure the wallet controls it.
+
+## Execute the route
+
+Use the token symbol `BTC` as the source token and a destination recipient in the destination chain's address format:
+
+```javascript title="Bitcoin to Arbitrum USDC"
+const quote = await symbiosis.quoteSwidge({
+ fromToken: 'BTC',
+ toToken: 'USDC',
+ toChain: 'Arbitrum One',
+ recipient: '0xRecipient...',
+ fromTokenAmount: 50_000n
+})
+
+// Show the quote, recipient, and refund address to the user, then:
+
+try {
+ const result = await symbiosis.swidge({
+ fromToken: 'BTC',
+ toToken: 'USDC',
+ toChain: 'Arbitrum One',
+ recipient: '0xRecipient...',
+ fromTokenAmount: 50_000n
+ })
+
+ console.log('Operation ID:', result.id)
+ console.log('Deposit transfer hash:', result.hash)
+} finally {
+ wallet.dispose()
+}
+```
+
+`fromTokenAmount` is in satoshis: `50_000n` is 0.0005 BTC. The returned ID embeds the Bitcoin transaction hash and works with [`getSwidgeStatus()`](/sdk/swidge-modules/swidge-symbiosis/api-reference#getswidgestatusid-options) like any other route; a route the provider cannot complete resolves to `refunded` at the configured refund address. `wallet.dispose()` clears derived keys and closes the Electrum connection after the source broadcast attempt.
+
+Bridging **to** Bitcoin needs no special handling: execute from the source chain's account as usual and pass a Bitcoin `recipient`.
+
+## Next steps
+
+Poll the operation in [Track Settlement](/sdk/swidge-modules/swidge-symbiosis/guides/track-settlement), or review refund-related failure modes in [Handle Errors](/sdk/swidge-modules/swidge-symbiosis/guides/handle-errors).
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/get-started.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/get-started.mdx
new file mode 100644
index 00000000..e3de56c1
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/get-started.mdx
@@ -0,0 +1,108 @@
+---
+title: Get Started with Symbiosis Swidge
+description: Install the Symbiosis community Swidge package, bind a WDK wallet account, and discover chains and tokens.
+docType: how-to
+schemaType: TechArticle
+---
+
+
+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 guide shows how to [install the package](#install-the-package), [create a source wallet account](#create-a-source-wallet-account), [instantiate the provider](#instantiate-the-provider), and [discover chains and tokens](#discover-chains-and-tokens).
+
+## Install the package
+
+### Prerequisites
+
+- **[Node.js](https://nodejs.org/)**: version 18 or higher for a global `fetch`. The package is ESM-only and also ships a `bare` entrypoint for the Bare runtime.
+- **[npm](https://www.npmjs.com/)**: usually bundled with Node.js.
+
+Install the released package together with the WDK wallet module for the source chain you execute from. The EVM example uses the wallet version installed by the release's tests:
+
+```bash title="Install @symbiosis-finance/wdk-protocol-swidge-symbiosis"
+npm install @symbiosis-finance/wdk-protocol-swidge-symbiosis@1.3.0 @tetherto/wdk-wallet-evm@1.0.0-beta.14
+```
+
+## Create a source wallet 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 = process.env.WDK_SEED_PHRASE
+if (!seedPhrase) throw new Error('WDK_SEED_PHRASE is required')
+
+const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
+ provider: 'https://eth.drpc.org',
+ transactionMaxFee: 100_000_000_000_000n
+})
+```
+
+
+**Seed phrase:** Load the mnemonic from secure storage; never hard-code or log it. Anyone with the phrase controls the funds on derived accounts.
+
+
+`transactionMaxFee` caps the EVM wallet transaction fee in wei. Choose a limit appropriate for the source network and your application; Symbiosis provider fee caps do not constrain this chain fee.
+
+Dispose the account in a `finally` block when the flow ends so key material is cleared from memory.
+
+## Instantiate the provider
+
+WDK wallet accounts do not expose their chain, so `chain` identifies the bound account's source chain and is required before quoting or execution. Use a Symbiosis numeric ID or a case-insensitive name from [`getSupportedChains()`](/sdk/swidge-modules/swidge-symbiosis/api-reference#getsupportedchains):
+
+```javascript title="Construct SymbiosisProtocol"
+import SymbiosisProtocol from '@symbiosis-finance/wdk-protocol-swidge-symbiosis'
+
+const symbiosis = new SymbiosisProtocol(account, {
+ chain: 'Ethereum',
+ partnerId: 'my-app'
+})
+```
+
+`partnerId` is sent as an `X-Partner-Id` header on every API request; registered partners can receive higher rate limits. See [Configuration](/sdk/swidge-modules/swidge-symbiosis/configuration) for the remaining fields, including `timeoutMs`, `defaultSlippage`, `refundAddress`, and the fee caps.
+
+
+The module does not verify that the configured `chain` matches the bound account. A mismatch produces route payloads for the wrong network, so derive both from the same application setting.
+
+
+## Discover chains and tokens
+
+```javascript title="Discover provider catalogs"
+const chains = await symbiosis.getSupportedChains()
+
+const tokens = await symbiosis.getSupportedTokens({
+ fromChain: 'Ethereum'
+})
+```
+
+Discovery reads provider-maintained catalogs and caches them for ten minutes per instance. A listed pair is not proof of a live route; request a quote to confirm availability. Monero and Zcash are excluded because their provider routes use third-party custodial integrations outside this module's scope.
+
+Prefer the exact address returned by `getSupportedTokens()` over a symbol when identifying tokens: symbols can be ambiguous on a chain.
+
+## Quote without a wallet account
+
+You can run discovery and quotes before any wallet exists, for example to render prices in an onboarding flow:
+
+```javascript title="Quote-only provider"
+const quoteOnly = new SymbiosisProtocol(undefined, {
+ chain: 'Ethereum'
+})
+
+const quote = await quoteOnly.quoteSwidge({
+ fromToken: 'USDT',
+ toToken: 'USDC',
+ toChain: 'Arbitrum One',
+ recipient: '0xRecipient...',
+ fromTokenAmount: 100_000_000n
+})
+```
+
+Without an account, `recipient` supplies both the source sender and the destination recipient in the request. Bind an account when those addresses differ.
+
+## Next steps
+
+Quote and execute a route in [Quote and Execute](/sdk/swidge-modules/swidge-symbiosis/guides/quote-and-execute), or start from a Bitcoin source in [Bridge from Bitcoin](/sdk/swidge-modules/swidge-symbiosis/guides/bridge-from-bitcoin).
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/handle-errors.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/handle-errors.mdx
new file mode 100644
index 00000000..ea61de10
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/handle-errors.mdx
@@ -0,0 +1,98 @@
+---
+title: Handle Symbiosis Errors
+description: Branch on the typed Symbiosis error family, distinguish pre-write from post-write failures, and retry safely.
+docType: how-to
+schemaType: TechArticle
+---
+
+
+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 guide covers [the typed error family](#the-typed-error-family), [branching with instanceof](#branch-with-instanceof), [pre-write versus post-write failures](#pre-write-versus-post-write-failures), and [cleanup](#dispose-signing-accounts).
+
+## The typed error family
+
+Every package-specific error extends `SymbiosisError`, so one `instanceof` check separates provider errors from wallet errors. The classes most flows branch on are:
+
+| Error | Thrown when | Useful fields |
+|---|---|---|
+| `ValidationError` | A locally checked option is invalid, for example a missing or non-positive `fromTokenAmount`. | — |
+| `ExactOutNotSupportedError` | `toTokenAmount` requests exact output. | — |
+| `UnsupportedChainError`, `UnsupportedTokenError` | An identifier is not in provider discovery. | `identifier` |
+| `UnsupportedRouteError` | The bound account lacks a capability the source route requires (TON, Tron, and Solana routes are probed at execution time), or a TON route needs more than one message. | `type` |
+| `FeeLimitExceededError` | A mapped fee total exceeds its configured cap, before any wallet write. | `feeType`, `bps`, `cap` |
+| `TransactionError` | Approval receipt polling detects a revert or times out. | `hash` |
+| `ApiError` | The REST API returns a non-2xx response, times out, or fails before a response. | `status`, `response`; `cause` for failures before a response |
+
+The full list, including `ConfigurationError` and `ReadOnlyAccountError`, is in the [API reference](/sdk/swidge-modules/swidge-symbiosis/api-reference#error-classes).
+
+## Branch with instanceof
+
+```javascript title="Handle execution errors"
+import {
+ ApiError,
+ FeeLimitExceededError,
+ SymbiosisError,
+ UnsupportedRouteError,
+ ValidationError
+} from '@symbiosis-finance/wdk-protocol-swidge-symbiosis'
+
+try {
+ const result = await symbiosis.swidge(options, { maxProtocolFeeBps: 100 })
+ await persistOperation(result.id, result.hash)
+} catch (error) {
+ if (error instanceof FeeLimitExceededError) {
+ // No wallet write happened. Show the fresh fee level and let the user re-confirm.
+ console.error(`Fee ${error.bps} bps exceeds cap ${error.cap} bps`)
+ } else if (error instanceof UnsupportedRouteError) {
+ // Keep this route quote-only or bind a wallet account that supports it.
+ console.error(`Source route type not executable: ${error.type}`)
+ } else if (error instanceof ValidationError) {
+ // Fix the request options; nothing was sent.
+ } else if (error instanceof ApiError) {
+ // status is 0 for a timeout or network failure without an HTTP response.
+ console.error(`Provider API failure (status ${error.status})`)
+ } else if (error instanceof SymbiosisError) {
+ // Another package-defined error; see the API reference.
+ } else {
+ // Propagated wallet account error: RPC failures, insufficient gas, signing issues.
+ }
+}
+```
+
+`persistOperation` is your app code. Store the ID before reporting success so a crash right after broadcast does not lose the handle to the funds in flight.
+
+## Pre-write versus post-write failures
+
+Which side of the wallet write an error occurs on determines whether a retry is safe:
+
+- **Before any wallet write** — `ValidationError`, `ConfigurationError`, discovery errors, `ExactOutNotSupportedError`, `FeeLimitExceededError`, and an `ApiError` from the quote or swap request. No funds moved; calling `swidge()` again is safe.
+- **After a wallet write started** — a `TransactionError` from approval polling, or a wallet error thrown by the route broadcast. An approval or the source transaction may already be on-chain.
+
+
+Do not retry `swidge()` blindly after an uncertain failure. First check the wallet's transaction history and, when you hold a source hash, query [`getSwidgeStatus()`](/sdk/swidge-modules/swidge-symbiosis/api-reference#getswidgestatusid-options) with `':'`. A duplicate call builds a second, independent route and spends the input twice.
+
+
+API requests time out after 30 seconds by default (`timeoutMs`), and the module does not retry or back off automatically. Put retry policy for reads — quotes, discovery, status — in the application, and keep executions single-flight.
+
+## Dispose signing accounts
+
+Clear key material when the flow ends, including on the error paths:
+
+```javascript title="Dispose in finally"
+try {
+ const result = await symbiosis.swidge(options)
+ await persistOperation(result.id, result.hash)
+} finally {
+ account.dispose()
+}
+```
+
+Dispose only when no further signing is needed from that instance; status polling needs no account and works after disposal.
+
+## Next steps
+
+Return to [Quote and Execute](/sdk/swidge-modules/swidge-symbiosis/guides/quote-and-execute), or review provider-behavior boundaries in [Configuration](/sdk/swidge-modules/swidge-symbiosis/configuration).
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/quote-and-execute.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/quote-and-execute.mdx
new file mode 100644
index 00000000..25dcedd0
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/quote-and-execute.mdx
@@ -0,0 +1,109 @@
+---
+title: Quote and Execute Symbiosis Routes
+description: Quote an exact-input Symbiosis route, review it with the user, and execute it from an EVM source account.
+docType: how-to
+schemaType: TechArticle
+---
+
+
+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 guide covers [route options](#build-exact-input-route-options), [quotes](#quote-the-route), [user review](#review-before-execution), [EVM execution](#execute-an-evm-route), and [fee caps](#cap-provider-fees).
+
+## Prerequisites
+
+Complete [Get Started](/sdk/swidge-modules/swidge-symbiosis/guides/get-started): a signing account bound to a `SymbiosisProtocol` instance whose `chain` matches the account's network.
+
+## Build exact-input route options
+
+The same options object drives both the quote and the execution:
+
+```javascript title="Route options"
+const options = {
+ fromToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ toToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
+ toChain: 'Arbitrum One',
+ recipient: '0xRecipient...',
+ fromTokenAmount: 100_000_000n,
+ slippage: 0.02
+}
+```
+
+- `fromTokenAmount` is the exact input in source-token base units. `100_000_000n` is 100 USDT with 6 decimals. Missing, zero, negative, and non-integer values throw `ValidationError` before an API request.
+- `slippage` is a decimal; `0.02` means 2% and is converted to 200 basis points for the provider. When omitted, `defaultSlippage` applies.
+- `toChain` defaults to the configured source chain, which produces a same-chain swap.
+- `recipient` defaults to the bound account's address.
+
+Only exact-input routes are supported. Passing `toTokenAmount` throws `ExactOutNotSupportedError`; there is no way to request an exact destination amount.
+
+## Quote the route
+
+[`quoteSwidge()`](/sdk/swidge-modules/swidge-symbiosis/api-reference#quoteswidgeoptions) performs no wallet write and returns an indicative result:
+
+```javascript title="Quote exact input"
+const quote = await symbiosis.quoteSwidge(options)
+
+console.log('Expected output:', quote.toTokenAmount)
+console.log('Minimum output:', quote.toTokenAmountMin)
+console.log('Estimated seconds:', quote.estimatedDuration)
+console.log('Fees:', quote.fees)
+```
+
+Quoted fees are already reflected in `toTokenAmount`; do not subtract them again. The provider does not return a quote expiry, so treat the numbers as a snapshot rather than a reservation.
+
+## Review before execution
+
+Show the user the source token and amount, the destination token and chain, the recipient, the expected and minimum output, the itemized fees, and the selected slippage.
+
+
+`swidge()` does not consume the earlier quote. It requests a fresh execution response and proceeds internally to fee checks, approvals, and the source broadcast without exposing that response for a second confirmation. Fee caps limit only the mapped provider fees in that fresh response; they do not bind its output amount, spender, payload, deposit address, or wallet chain fee.
+
+
+## Execute an EVM route
+
+Call [`swidge()`](/sdk/swidge-modules/swidge-symbiosis/api-reference#swidgeoptions-config) only after the user confirms:
+
+```javascript title="Execute with a fee cap"
+const result = await symbiosis.swidge(options, {
+ maxProtocolFeeBps: 100
+})
+
+console.log('Operation ID:', result.id)
+console.log('Source transaction:', result.hash)
+console.log('Recorded transactions:', result.transactions)
+```
+
+For a non-native EVM input token the method:
+
+1. Reads the current allowance for the spender returned by the fresh execution response.
+2. Resets a non-zero insufficient allowance to zero first, as required by tokens such as USDT on Ethereum.
+3. Approves the exact input amount.
+4. Waits for each approval to mine (when the account supports receipt lookup) before broadcasting the route transaction.
+
+If the allowance lookup fails, the module falls back to a direct approval without the reset; see [Approval behavior](/sdk/swidge-modules/swidge-symbiosis/configuration#approval-behavior) for the full decision table.
+
+Approval hashes are appended to `result.transactions` with type `approval`, followed by the source transaction with type `source`. The method returns after the source broadcast; destination settlement continues asynchronously. Track it with the returned `result.id` as described in [Track Settlement](/sdk/swidge-modules/swidge-symbiosis/guides/track-settlement).
+
+Set `skipApproval: true` in the constructor config only when the host application manages allowance itself; the module then broadcasts the route transaction without checking allowance, and an insufficient allowance surfaces as an on-chain failure.
+
+## Cap provider fees
+
+`maxProtocolFeeBps` bounds the fees mapped as `protocol` in basis points of the input amount, checked against the fresh execution response before any wallet write:
+
+```javascript title="Instance-level and per-call caps"
+const symbiosis = new SymbiosisProtocol(account, {
+ chain: 'Ethereum',
+ maxProtocolFeeBps: 100
+})
+
+await symbiosis.swidge(options, { maxProtocolFeeBps: 75 })
+```
+
+A fee whose description is exactly `Partner fee` maps to `affiliate` and is not constrained by either cap. No fee maps to `network` in this release, so `maxNetworkFeeBps` does not bound the wallet transaction's own chain fee. When the cap is exceeded, `swidge()` throws `FeeLimitExceededError` before touching the wallet.
+
+## Next steps
+
+Poll the operation in [Track Settlement](/sdk/swidge-modules/swidge-symbiosis/guides/track-settlement), or branch on the typed error family in [Handle Errors](/sdk/swidge-modules/swidge-symbiosis/guides/handle-errors).
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/track-settlement.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/track-settlement.mdx
new file mode 100644
index 00000000..e7f0b487
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/guides/track-settlement.mdx
@@ -0,0 +1,86 @@
+---
+title: Track Symbiosis Settlement
+description: Persist the Symbiosis operation ID, poll cross-chain status, and recognize completion and refunds.
+docType: how-to
+schemaType: TechArticle
+---
+
+
+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 guide covers [the operation ID](#persist-the-operation-id), [polling](#poll-until-terminal), [status meanings](#status-mapping), and [refunds](#recognize-refunds).
+
+## Persist the operation ID
+
+`swidge()` returns after the source transaction is broadcast. Destination settlement continues on the provider side, identified by the returned ID:
+
+```text
+:
+```
+
+Persist the ID (or at least the source transaction hash) in durable storage before treating the operation as submitted. The ID is self-contained: a fresh `SymbiosisProtocol` instance in a new process can resolve it without any other state, so tracking survives an application restart.
+
+```javascript title="Status from a stored ID"
+const status = await symbiosis.getSwidgeStatus(storedId)
+```
+
+For a bare transaction hash, pass `options.fromChain` or rely on the instance's configured source `chain`.
+
+## Poll until terminal
+
+Poll [`getSwidgeStatus()`](/sdk/swidge-modules/swidge-symbiosis/api-reference#getswidgestatusid-options) until the status leaves `pending`, and bound the loop with a deadline:
+
+```javascript title="Poll with a deadline"
+const deadline = Date.now() + 60 * 60 * 1000
+
+for (;;) {
+ const { status, transactions } = await symbiosis.getSwidgeStatus(storedId)
+
+ if (status !== 'pending') {
+ console.log('Final status:', status)
+ for (const tx of transactions) {
+ console.log(`${tx.type} transaction on chain ${tx.chain}:`, tx.hash)
+ }
+ break
+ }
+
+ if (Date.now() > deadline) {
+ // Surface the stored ID and source hash for manual follow-up.
+ break
+ }
+
+ await new Promise(resolve => setTimeout(resolve, 10_000))
+}
+```
+
+Use `quote.estimatedDuration` (seconds) as a hint when choosing the polling deadline; cross-chain routes normally settle in minutes.
+
+
+A status lookup that returns HTTP `404` is reported as `pending`, so a newly submitted operation and a genuinely unknown ID are indistinguishable through this method. The deadline is what turns a typo or an unindexed transaction into an actionable state instead of an infinite loop.
+
+
+## Status mapping
+
+| Symbiosis state | WDK status | Meaning |
+|---|---|---|
+| `0` (success) | `completed` | Destination transaction settled. |
+| `1` (pending) | `pending` | Route is in flight. |
+| `2` (stuck) | `pending` | The provider resolves this state automatically by completing or refunding; no user action is required. |
+| `3` (reverted) | `refunded` | Funds were returned on the source side. |
+| `-1` (not found) | `pending` | The source transaction may not be indexed yet. |
+| HTTP `404` | `pending` | Operation unknown to the provider; see the callout above. |
+
+The status result also returns the provider's transaction list: the source transaction plus, once settled, a `destination` or `refund` transaction.
+
+## Recognize refunds
+
+When the status is `refunded`, the settlement transaction in the list is labeled `refund` and points at the transaction that returned the funds — on the source chain, at the refund address for deposit-address routes. Reconcile the refunded amount against the original input; provider costs already incurred are not necessarily returned in full.
+
+Treat `completed` and `refunded` as the two terminal states. Do not resubmit a route while its ID still reports `pending`; a stuck route resolves on the provider side without a new source transaction.
+
+## Next steps
+
+Branch on the typed error family in [Handle Errors](/sdk/swidge-modules/swidge-symbiosis/guides/handle-errors), or return to [Quote and Execute](/sdk/swidge-modules/swidge-symbiosis/guides/quote-and-execute).
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/index.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/index.mdx
new file mode 100644
index 00000000..38bee7d6
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/index.mdx
@@ -0,0 +1,122 @@
+---
+title: Symbiosis Swidge Overview
+description: Use the Symbiosis community Swidge module for same-chain and cross-chain asset routes.
+docType: explanation
+schemaType: TechArticle
+---
+
+
+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 [`@symbiosis-finance/wdk-protocol-swidge-symbiosis@1.3.0`](https://www.npmjs.com/package/@symbiosis-finance/wdk-protocol-swidge-symbiosis/v/1.3.0) when your wallet needs a WDK `SwidgeProtocol` provider for routes served by Symbiosis. The module uses the public Symbiosis REST API for discovery, quotes, execution payloads, and cross-chain status.
+
+The released source is tagged [`v1.3.0`](https://github.com/symbiosis-finance/wdk-protocol-swidge-symbiosis/tree/v1.3.0) and maintained by [Symbiosis](https://symbiosis.finance/).
+
+## When to use it
+
+Use this module when your application needs:
+
+- same-chain swaps, cross-chain bridges, or combined swap-and-bridge routes;
+- runtime chain and token discovery;
+- exact-input quotes;
+- EVM, Bitcoin, TON, Tron, or Solana source execution through a WDK wallet account with the route-required capabilities;
+- cross-chain settlement status mapped to WDK status values.
+
+TON, Tron, and Solana source routes execute when the bound wallet account supports the transaction format the route requires. The module probes the account at execution time; on wallet versions without the capability those routes stay quote-only and `swidge()` throws `UnsupportedRouteError`.
+
+## Responsibility model
+
+| Area | Owner |
+|---|---|
+| Wallet keys, source address, approval, signing, and transaction broadcast | WDK wallet account |
+| Chain and token catalogs, route payloads, deposit addresses, and settlement status | Symbiosis API |
+| Input validation, quote review, user confirmation, fee policy, retries, and status polling | Host application |
+
+## Discovery is not a route guarantee
+
+`getSupportedChains()` and `getSupportedTokens(options?)` read provider-maintained catalogs. `getSupportedTokens()` filters the token catalog by `toChain` when present, otherwise by `fromChain`. It does not prove that a specific token pair currently has liquidity.
+
+Call `quoteSwidge()` for the requested pair before presenting a route. Treat the returned quote as indicative because execution obtains a fresh response.
+
+## Quote and execution model
+
+`quoteSwidge()` calls the Symbiosis quote endpoint and does not write to the wallet.
+
+`swidge()` calls the Symbiosis swap endpoint again. The execution amounts, fees, spender, transaction payload, or Bitcoin deposit address can differ from the earlier quote. The method checks configured fee caps on this fresh response and then proceeds to the route-specific wallet writes.
+
+
+`swidge()` does not expose the fresh execution response for a separate confirmation step. Show the indicative quote, recipient, destination chain, refund address, and selected slippage before calling it. Configure the applicable fee caps, and do not treat the earlier quote as reserved or bound to execution.
+
+
+For a non-native EVM input token, `swidge()` can:
+
+1. Read the current allowance.
+2. Reset a non-zero insufficient allowance to zero and, when the account supports receipt lookup, wait for that approval receipt.
+3. Approve the spender returned by Symbiosis for the input amount and, when supported, wait for that receipt.
+4. Broadcast the route transaction.
+
+The method returns after the source transaction is broadcast. Use `getSwidgeStatus(result.id)` to track destination settlement or a refund.
+
+## Source execution support
+
+| Source route type | Execution behavior |
+|---|---|
+| `evm` | Optionally approves the input ERC-20, then sends the API-provided calldata transaction. |
+| `btc` | Transfers the input amount to the generated deposit address. Configure a suitable refund address. |
+| `ton` | Sends the route's message with its raw BoC payload when the account supports raw cell bodies and the route is a single message; otherwise `swidge()` throws `UnsupportedRouteError`. |
+| `tron` | Approves the input TRC-20 when needed and sends the router contract call when the account supports smart contract calls and approvals; otherwise `swidge()` throws `UnsupportedRouteError`. |
+| `solana` | Signs and broadcasts the API-provided serialized transaction when the account supports serialized transactions; otherwise `swidge()` throws `UnsupportedRouteError`. |
+
+Destination support is provider-controlled. Use runtime discovery and a successful quote instead of maintaining a static route list.
+
+## Integrator limitations
+
+- Only exact-input routes are supported. Passing `toTokenAmount` throws `ExactOutNotSupportedError`.
+- TON source routes are executed only when the provider returns a single transfer message. The TON wallet account reads a fresh sequence number per send without waiting for inclusion, so a multi-message route could execute partially; such routes throw `UnsupportedRouteError`.
+- `fromTokenAmount` must convert to a positive integer `bigint`; invalid, zero, and negative values throw `ValidationError` before an API request.
+- The module does not validate slippage ranges or the formats of recipient, refund, and partner addresses. Validate those application inputs before calling the provider.
+- Discovery responses are cached for ten minutes per protocol instance. The cache duration is not configurable.
+- Monero and Zcash are excluded from discovery and chain resolution because their provider routes use third-party custodial integrations outside this module's scope.
+- A status lookup returning HTTP `404` is mapped to `pending`. A newly submitted operation and a genuinely unknown ID are therefore indistinguishable through this method.
+- API requests time out after `timeoutMs` (30 seconds by default). The module does not retry or back off automatically.
+- The package documents `/v2/swap` as rate-limited to one request per second. Bitcoin execution also uses that endpoint to generate a deposit address.
+- If allowance lookup fails, the module falls back to sending an approval without a reset. That direct approval can still fail for a token with an existing non-zero allowance, so ensure allowance reads work or manage the reset in the application.
+- If the wallet does not expose transaction-receipt lookup, the module cannot wait for approval confirmation before submitting the route transaction.
+- A fee whose description is exactly `Partner fee` maps to `affiliate`; every other fee maps to `protocol`. No fee maps to `network`, so `maxNetworkFeeBps` does not constrain a separately reported network cost and `maxProtocolFeeBps` does not constrain the affiliate fee.
+- Quote-only construction without an account uses `recipient` as both the source sender and destination recipient.
+- The package exposes ESM and Bare entrypoints but does not declare a Node.js `engines` range.
+
+## Next steps
+
+
+
+Install the package, bind a source wallet account, and discover provider catalogs step by step.
+
+
+Review an indicative quote, cap mapped provider fees, and execute an EVM route.
+
+
+Configure a refund address and execute a Bitcoin deposit-address route.
+
+
+Persist the operation ID, poll status, and recognize completion or refunds.
+
+
+Branch on typed errors and avoid unsafe retries after a wallet write.
+
+
+Configure source-chain identity, slippage, refund handling, approval behavior, and fee caps.
+
+
+Review the exported class, methods, options, result shapes, statuses, and typed errors.
+
+
+
+---
+
+## Need Help?
+
+
diff --git a/content/docs/sdk/swidge-modules/swidge-symbiosis/usage.mdx b/content/docs/sdk/swidge-modules/swidge-symbiosis/usage.mdx
new file mode 100644
index 00000000..fc7158d9
--- /dev/null
+++ b/content/docs/sdk/swidge-modules/swidge-symbiosis/usage.mdx
@@ -0,0 +1,258 @@
+---
+title: Symbiosis Swidge Usage
+description: Install and use the released Symbiosis community Swidge provider with WDK wallet accounts.
+docType: how-to
+schemaType: TechArticle
+icon: BookOpen
+---
+
+
+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 released Symbiosis package is `1.3.0`. The EVM example uses the WDK wallet version installed by that release's tests:
+
+```bash
+npm install @symbiosis-finance/wdk-protocol-swidge-symbiosis@1.3.0 @tetherto/wdk-wallet-evm@1.0.0-beta.14
+```
+
+The provider package includes `@tetherto/wdk-wallet` as a runtime dependency. Install the matching WDK wallet module separately for the source chain you intend to execute from.
+
+A runnable end-to-end example that quotes, executes, and tracks a route is available at [`examples/swidge.js`](https://github.com/symbiosis-finance/wdk-protocol-swidge-symbiosis/blob/v1.3.0/examples/swidge.js) in the source repository.
+
+## Create the provider
+
+Configure `chain` as the Symbiosis ID or name for the bound source account.
+
+```javascript
+import SymbiosisProtocol from '@symbiosis-finance/wdk-protocol-swidge-symbiosis'
+import { WalletAccountEvm } from '@tetherto/wdk-wallet-evm'
+
+const account = new WalletAccountEvm(seedPhrase, "0'/0/0", {
+ provider: 'https://eth.drpc.org'
+})
+
+const symbiosis = new SymbiosisProtocol(account, {
+ chain: 'Ethereum',
+ timeoutMs: 30_000,
+ partnerId: 'my-app',
+ maxProtocolFeeBps: 100
+})
+```
+
+Keep the account alive through quote review, execution, and source broadcast. Dispose it in a `finally` block when the flow ends:
+
+```javascript
+try {
+ // Discover, quote, and execute while the account is active.
+} finally {
+ account.dispose()
+}
+```
+
+## Discover chains and tokens
+
+```javascript
+const chains = await symbiosis.getSupportedChains()
+
+const ethereumTokens = await symbiosis.getSupportedTokens({
+ fromChain: 'Ethereum'
+})
+
+const arbitrumTokens = await symbiosis.getSupportedTokens({
+ fromChain: 'Ethereum',
+ toChain: 'Arbitrum One'
+})
+```
+
+When both filters are present, `toChain` takes precedence. The method returns known tokens on the selected chain; it does not check whether a particular source and destination pair has a live route.
+
+Use the returned chain IDs, names, and token identifiers to build selectors, then request a quote to test the requested pair. Monero and Zcash do not appear because this release excludes their third-party custodial routes from the module.
+
+## Quote an exact-input route
+
+```javascript
+const options = {
+ fromToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ toToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
+ toChain: 'Arbitrum One',
+ recipient: '0xRecipient...',
+ fromTokenAmount: 100_000_000n,
+ slippage: 0.02
+}
+
+const quote = await symbiosis.quoteSwidge(options)
+
+console.log('Expected output:', quote.toTokenAmount)
+console.log('Minimum output:', quote.toTokenAmountMin)
+console.log('Estimated seconds:', quote.estimatedDuration)
+console.log('Fees:', quote.fees)
+```
+
+`quoteSwidge()` performs no wallet write. It calls `/v2/quote` and returns an indicative result.
+
+Only exact-input operations are supported. Pass `fromTokenAmount`; passing `toTokenAmount` throws `ExactOutNotSupportedError`.
+
+## Review before execution
+
+Before calling `swidge()`, show the user:
+
+- source token and amount;
+- destination token and chain;
+- recipient and refund address, when applicable;
+- expected and minimum output from the indicative quote;
+- itemized quote fees;
+- the selected slippage tolerance.
+
+
+`swidge()` calls `/v2/swap` and then proceeds internally to fee checks, approval, and source broadcast. It does not expose that fresh response for a second application-level confirmation. Its amounts, fees, spender, transaction payload, or deposit address can differ from the preceding quote.
+
+
+The provider rejects a missing, non-integer, zero, or negative `fromTokenAmount` with `ValidationError`. The host application must still validate its allowed slippage range and confirm that each user-supplied address belongs to the intended chain.
+
+## Execute an EVM route
+
+Call `swidge()` only after the user confirms the indicative quote and route inputs:
+
+```javascript
+const result = await symbiosis.swidge(options, {
+ maxProtocolFeeBps: 100
+})
+
+console.log('Operation ID:', result.id)
+console.log('Source transaction:', result.hash)
+console.log('Recorded transactions:', result.transactions)
+```
+
+The method uses this order:
+
+1. Resolve the source and destination chains and tokens.
+2. Request a fresh response from `/v2/swap`.
+3. Check applicable fee caps before a wallet write.
+4. For a non-native EVM token, read allowance and reset a non-zero insufficient allowance to zero.
+5. Approve the returned spender for the input amount.
+6. Wait for each approval receipt when the account supports receipt lookup.
+7. Broadcast the API-provided route transaction.
+8. Return the source hash and operation ID without waiting for destination settlement.
+
+If allowance lookup fails, the module falls back to approval without a reset; that direct approval can still fail for a token with an existing non-zero allowance. When a reset is required and the account returns transaction hashes, both approval hashes are recorded in `result.transactions`. If the account cannot read receipts, it proceeds without waiting for approval confirmation.
+
+Set `skipApproval: true` only when the host application has already verified and managed allowance:
+
+```javascript
+const symbiosis = new SymbiosisProtocol(account, {
+ chain: 'Ethereum',
+ skipApproval: true
+})
+```
+
+`maxProtocolFeeBps` constrains fees mapped as `protocol`; it does not constrain a fee whose description is exactly `Partner fee`, which maps as `affiliate`. This release maps no fee entry as `network`, so `maxNetworkFeeBps` does not constrain the wallet transaction's chain fee.
+
+## Execute a Bitcoin source route
+
+For Bitcoin, configure a refund address before requesting execution:
+
+```javascript
+const symbiosis = new SymbiosisProtocol(bitcoinAccount, {
+ chain: 'Bitcoin',
+ refundAddress: 'bc1qRefund...'
+})
+
+const result = await symbiosis.swidge({
+ fromToken: 'BTC',
+ toToken: 'USDC',
+ toChain: 'Arbitrum One',
+ recipient: '0xRecipient...',
+ fromTokenAmount: 50_000n
+})
+```
+
+`swidge()` requests a deposit address and sends the input amount to it without returning the deposit address for a separate confirmation. Confirm the refund address, recipient, amount, selected slippage, and indicative quote before calling the method.
+
+## Execute TON, Tron, and Solana source routes
+
+TON, Tron, and Solana source routes execute in `1.3.0` when the bound wallet account supports the transaction format the route requires: raw BoC message bodies for TON (single-message routes only), smart contract calls plus TRC-20 approvals for Tron, and base64-serialized transactions for Solana. The module probes the account at execution time and throws `UnsupportedRouteError` when the capability is missing, so older wallet versions keep the previous quote-only behavior. Tron approval receipts are checked for on-chain failure before the swap is sent.
+
+## Track settlement
+
+The returned ID has the form `':'`:
+
+```javascript
+const status = await symbiosis.getSwidgeStatus(result.id)
+
+console.log('Status:', status.status)
+console.log('Transactions:', status.transactions)
+```
+
+Poll until the operation reaches the state your application handles as terminal. Symbiosis status code `2` is reported as `pending` because the provider resolves that state without a separate user action.
+
+The module also maps an HTTP `404` to `pending`. A newly submitted operation and a genuinely unknown ID produce the same result, so enforce a polling deadline and retain the source transaction hash.
+
+## Quote without a wallet account
+
+You can construct the provider without an account for a quote:
+
+```javascript
+const symbiosis = new SymbiosisProtocol(undefined, {
+ chain: 'Ethereum'
+})
+
+const quote = await symbiosis.quoteSwidge({
+ fromToken: 'USDT',
+ toToken: 'USDC',
+ toChain: 'Arbitrum One',
+ recipient: '0xRecipient...',
+ fromTokenAmount: 100_000_000n
+})
+```
+
+Without an account, the module sends `recipient` to Symbiosis as both the source sender and destination recipient. Bind an account when those addresses differ or use different address formats.
+
+## Handle errors
+
+```javascript
+import {
+ ApiError,
+ ExactOutNotSupportedError,
+ FeeLimitExceededError,
+ ReadOnlyAccountError,
+ SymbiosisError,
+ UnsupportedRouteError
+} from '@symbiosis-finance/wdk-protocol-swidge-symbiosis'
+
+try {
+ await symbiosis.swidge(options, { maxProtocolFeeBps: 100 })
+} catch (error) {
+ if (error instanceof FeeLimitExceededError) {
+ // Stop before wallet execution and review the fresh mapped fees.
+ } else if (error instanceof ExactOutNotSupportedError) {
+ // Rebuild the request with fromTokenAmount.
+ } else if (error instanceof ReadOnlyAccountError) {
+ // Bind an account that supports the required write capabilities.
+ } else if (error instanceof UnsupportedRouteError) {
+ // Keep the route quote-only or choose an executable source chain.
+ } else if (error instanceof ApiError) {
+ // Handle a non-2xx response, timeout, or network failure.
+ // status is 0 when no HTTP response was received.
+ } else if (error instanceof SymbiosisError) {
+ // Handle another package-specific error.
+ } else {
+ // Handle errors propagated by the wallet account.
+ }
+}
+```
+
+API requests time out after 30 seconds by default; set `timeoutMs` on the provider to change that limit. The package does not retry automatically. Avoid retrying `swidge()` blindly after an uncertain API or wallet failure; first inspect wallet history and retained transaction state.
+
+
+
+Review constructor fields, per-call fee caps, identifiers, caching, and runtime behavior.
+
+
+Review exact methods, result fields, statuses, fee mapping, and typed errors.
+
+
diff --git a/content/docs/sdk/wallet-modules/index.mdx b/content/docs/sdk/wallet-modules/index.mdx
index ef306d0d..9dc839ec 100644
--- a/content/docs/sdk/wallet-modules/index.mdx
+++ b/content/docs/sdk/wallet-modules/index.mdx
@@ -61,9 +61,9 @@ Spark Mainnet
-## Classic Wallet Modules
+## Wallet Modules
-Standard wallet implementations that use native blockchain tokens for transaction fees:
+Wallet implementations for supported chains and asset systems:
| Module | Blockchain | Status | Documentation |
|--------|------------|--------|---------------|
@@ -73,7 +73,10 @@ Standard wallet implementations that use native blockchain tokens for transactio
| [`@tetherto/wdk-wallet-spark`](https://github.com/tetherto/wdk-wallet-spark) | Spark | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-spark) |
| [`@tetherto/wdk-wallet-tron`](https://github.com/tetherto/wdk-wallet-tron) | TRON | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-tron) |
| [`@tetherto/wdk-wallet-solana`](https://github.com/tetherto/wdk-wallet-solana) | Solana | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-solana) |
-| [`@arkade-os/wdk`](https://github.com/arkade-os/arkade-wdk) | Arkade | ✅ Ready | [README](https://github.com/arkade-os/arkade-wdk#readme) |
+| [`@tetherto/wdk-wallet-aptos`](https://github.com/tetherto/wdk-wallet-aptos) | Aptos | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-aptos) |
+| [`@utexo/wdk-wallet-rgb`](https://www.npmjs.com/package/@utexo/wdk-wallet-rgb) | Bitcoin (RGB) | ✅ Ready | [Documentation](/sdk/community-modules/wdk-wallet-rgb/) |
+| [`@utexo/wdk-rgb-lightning`](https://www.npmjs.com/package/@utexo/wdk-rgb-lightning) | Bitcoin (RGB and Lightning) | ✅ Ready | [Documentation](/sdk/community-modules/wdk-rgb-lightning/) |
+| [`@base58-io/wdk-wallet-cosmos`](https://www.npmjs.com/package/@base58-io/wdk-wallet-cosmos) | Cosmos | ✅ Ready | [Documentation](/sdk/community-modules/wdk-wallet-cosmos/) |
## Account Abstraction Wallet Modules
@@ -81,27 +84,13 @@ Wallet implementations that support [Account Abstraction](/resources/concepts#ac
| Module | Blockchain | Status | Documentation |
|--------|------------|--------|---------------|
-| [`@tetherto/wdk-wallet-evm-erc4337`](https://github.com/tetherto/wdk-wallet-evm-erc-4337) | EVM | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-evm-erc-4337) |
+| [`@tetherto/wdk-wallet-evm-erc-4337`](https://github.com/tetherto/wdk-wallet-evm-erc-4337) | EVM | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-evm-erc-4337) |
| [`@tetherto/wdk-wallet-evm-7702-gasless`](https://github.com/tetherto/wdk-wallet-evm-7702-gasless) | EVM | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-evm-7702-gasless) |
| [`@tetherto/wdk-wallet-ton-gasless`](https://github.com/tetherto/wdk-wallet-ton-gasless) | TON | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-ton-gasless) |
| [`@tetherto/wdk-wallet-tron-gasfree`](https://github.com/tetherto/wdk-wallet-tron-gasfree) | TRON | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-tron-gasfree) |
| [`@tetherto/wdk-wallet-solana-gasless`](https://github.com/tetherto/wdk-wallet-solana-gasless) | Solana | ✅ Ready | [Documentation](/sdk/wallet-modules/wallet-solana-gasless) |
| `@tetherto/wdk-wallet-solana-jupiterz` | Solana | In progress | - |
-## Community Wallet Modules
-
-Wallet modules developed by the community. See the [Community Modules](/sdk/community-modules/) page for more details.
-
-
-Community modules are developed and maintained independently. Use your own judgment and proceed at your own risk.
-
-
-| Module | Blockchain | Description | Repository |
-|--------|------------|-------------|------------|
-| [`@utexo/wdk-wallet-rgb`](https://www.npmjs.com/package/@utexo/wdk-wallet-rgb) | Bitcoin (RGB) | RGB protocol wallet integration for Bitcoin-based smart contracts | [Docs](/sdk/community-modules/wdk-wallet-rgb/) |
-| [`@arkade-os/wdk`](https://www.npmjs.com/package/@arkade-os/wdk) | Arkade | Bitcoin wallet module built on the Arkade SDK | [README](https://github.com/arkade-os/arkade-wdk#readme) |
-| [`@base58-io/wdk-wallet-cosmos`](https://www.npmjs.com/package/@base58-io/wdk-wallet-cosmos) | Cosmos | Wallet module for Cosmos-compatible blockchains | [Docs](/sdk/community-modules/wdk-wallet-cosmos/) |
-
## Next Steps
To get started with WDK modules, follow these steps:
diff --git a/content/docs/sdk/wallet-modules/wallet-aptos/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-aptos/api-reference.mdx
new file mode 100644
index 00000000..c66dbe38
--- /dev/null
+++ b/content/docs/sdk/wallet-modules/wallet-aptos/api-reference.mdx
@@ -0,0 +1,185 @@
+---
+title: Wallet Aptos API Reference
+description: API reference for @tetherto/wdk-wallet-aptos.
+docType: reference
+schemaType: APIReference
+icon: Code
+---
+
+## Exports
+
+```javascript
+import WalletManagerAptos, {
+ WalletAccountAptos,
+ WalletAccountReadOnlyAptos
+} from '@tetherto/wdk-wallet-aptos'
+```
+
+## WalletManagerAptos
+
+Creates and manages seed-derived Aptos accounts.
+
+```typescript
+new WalletManagerAptos(
+ seed: string | Uint8Array,
+ config?: AptosWalletConfig
+)
+```
+
+### Methods
+
+| Method | Description | Returns |
+|--------|-------------|---------|
+| `getAccount(index?)` | Returns the account at `m/44'/637'/index'/0'/0'`. | `Promise` |
+| `getAccountByPath(path)` | Returns an account at a relative hardened path. | `Promise` |
+| `getFeeRates()` | Returns Aptos fee rates in octas per gas unit. | `Promise` |
+| `dispose()` | Clears managed account secret material. | `void` |
+
+## WalletAccountAptos
+
+Writable Aptos account with signing and transaction submission support.
+
+```typescript
+new WalletAccountAptos(
+ seed: string | Uint8Array,
+ path: string,
+ config?: AptosWalletConfig
+)
+```
+
+### Properties
+
+| Property | Description |
+|----------|-------------|
+| `index` | Index parsed from the derivation path. |
+| `path` | Full derivation path, including the `m/44'/637'` prefix. |
+| `keyPair` | Public/private key byte-array views. Treat them as read-only. The private key is unavailable after `dispose()`. |
+
+### Methods
+
+| Method | Description | Returns |
+|--------|-------------|---------|
+| `getAddress()` | Returns the Aptos account address. | `Promise` |
+| `getBalance()` | Returns the native APT balance in octas. | `Promise` |
+| `getTokenBalance(tokenAddress)` | Returns a fungible asset balance by metadata address. | `Promise` |
+| `quoteSendTransaction(tx)` | Estimates fee for a native APT transfer. | `Promise<{ fee: bigint }>` |
+| `sendTransaction(tx)` | Signs and submits a native APT transfer. | `Promise` |
+| `signTransaction(tx)` | Simulates and signs a native APT transfer without broadcasting. Requires a provider. | `Promise` |
+| `quoteTransfer(options)` | Estimates fee for a fungible asset transfer. | `Promise<{ fee: bigint }>` |
+| `transfer(options)` | Signs and submits a fungible asset transfer. | `Promise` |
+| `getTransactionReceipt(hash)` | Looks up a pending or committed Aptos transaction. | `Promise<{ type: string; hash: string; success?: boolean; vm_status?: string } \| null>` |
+| `sign(message)` | Signs a message with the account key. | `Promise` |
+| `verify(message, signature)` | Verifies a message signature. | `Promise` |
+| `toReadOnlyAccount()` | Returns a read-only account for the same address. | `Promise` |
+| `dispose()` | Clears private key material from memory. | `void` |
+
+## WalletAccountReadOnlyAptos
+
+Read-only account for address-based reads and verification.
+
+```typescript
+new WalletAccountReadOnlyAptos(
+ address: string,
+ config?: AptosWalletConfig,
+ publicKey?: Uint8Array
+)
+```
+
+An address-only instance supports balance reads and receipt lookup. `quoteSendTransaction()`, `quoteTransfer()`, and `verify()` require the matching public key because an Aptos address cannot be reversed into an Ed25519 public key. `toReadOnlyAccount()` supplies that key automatically.
+
+| Method | Description | Returns |
+|--------|-------------|---------|
+| `getAddress()` | Returns the normalized Aptos address. | `Promise` |
+| `getBalance()` | Returns the native APT balance in octas. | `Promise` |
+| `getTokenBalance(tokenAddress)` | Returns a fungible asset balance by metadata address. | `Promise` |
+| `quoteSendTransaction(tx)` | Estimates fee for a native APT transfer. | `Promise<{ fee: bigint }>` |
+| `quoteTransfer(options)` | Estimates fee for a fungible asset transfer. | `Promise<{ fee: bigint }>` |
+| `getTransactionReceipt(hash)` | Looks up a pending or committed Aptos transaction. | `Promise<{ type: string; hash: string; success?: boolean; vm_status?: string } \| null>` |
+| `verify(message, signature)` | Verifies a message signature. Requires the matching public key. | `Promise` |
+
+## Config Type
+
+```typescript
+type AptosWalletConfig = {
+ provider?: string | string[]
+ chainId?: number
+ retries?: number
+ txnExpirationSecs?: number
+ transferMaxFee?: number | bigint
+}
+```
+
+| Field | Default and behavior |
+|---|---|
+| `provider` | No default. An array enables endpoint failover. |
+| `chainId` | Fetched from ledger info on first use when omitted. A supplied value must match the provider network. |
+| `retries` | `3` for a provider array. |
+| `txnExpirationSecs` | `60`. |
+| `transferMaxFee` | No default. Applies only to fungible asset `transfer()` and rejects fees at or above the cap. |
+
+## Transaction Types
+
+```typescript
+type AptosTransaction = {
+ to: string
+ value: number | bigint
+}
+
+type TransferOptions = {
+ token: string
+ recipient: string
+ amount: number | bigint
+}
+```
+
+`token` is an Aptos fungible asset metadata address.
+
+## Result Types
+
+```typescript
+type TransactionResult = {
+ hash: string
+ fee: bigint
+}
+
+type TransferResult = {
+ hash: string
+ fee: bigint
+}
+```
+
+`getTransactionReceipt()` has three observable states. The package root does not export an `AptosTransactionReceipt` type; the released declaration uses `type: string`. The following is the portable declared shape:
+
+```typescript
+type AptosReceiptShape = {
+ type: string
+ hash: string
+ success?: boolean
+ vm_status?: string
+}
+```
+
+- `null`: the fullnode does not know the hash.
+- An observed `type` of `pending_transaction`: accepted into the mempool; `success` and `vm_status` are absent.
+- An observed `type` of `user_transaction`: committed; inspect `success` and `vm_status` to determine execution outcome.
+
+## Signed Transaction
+
+`signTransaction(tx)` returns a JSON-form signed transaction accepted by the Aptos REST API. It includes sender, sequence number, gas fields, payload, and Ed25519 signature. Before signing, the module uses the configured provider to simulate the transaction and obtain sequence, gas, and chain data. A failed simulation prevents signing.
+
+
+`signTransaction()` does not broadcast, but it is not an offline operation. It signs native APT transfers only. Use `transfer()` for fungible asset transfers.
+
+
+
+`transferMaxFee` does not protect native `sendTransaction()` or `signTransaction()` calls. Quote native transfers and enforce an application-level limit when required.
+
+
+
+
+Create accounts, read balances, transfer funds, and sign messages.
+
+
+Provider, network, fee, and derivation path options.
+
+
diff --git a/content/docs/sdk/wallet-modules/wallet-aptos/configuration.mdx b/content/docs/sdk/wallet-modules/wallet-aptos/configuration.mdx
new file mode 100644
index 00000000..13dc2fbf
--- /dev/null
+++ b/content/docs/sdk/wallet-modules/wallet-aptos/configuration.mdx
@@ -0,0 +1,129 @@
+---
+title: Wallet Aptos Configuration
+description: Configuration options for @tetherto/wdk-wallet-aptos.
+docType: reference
+schemaType: TechArticle
+icon: Settings
+---
+
+Configure the Aptos wallet module with an Aptos fullnode REST endpoint. A provider is required for balance reads, fee quotes, transaction submission, and receipt polling.
+
+```javascript
+import WalletManagerAptos from '@tetherto/wdk-wallet-aptos'
+
+const wallet = new WalletManagerAptos(seedPhrase, {
+ provider: [
+ 'https://fullnode.mainnet.aptoslabs.com/v1',
+ process.env.APTOS_FAILOVER_FULLNODE_URL
+ ].filter(Boolean),
+ retries: 3,
+ txnExpirationSecs: 60,
+ transferMaxFee: 100000n
+})
+```
+
+## Wallet Configuration
+
+| Option | Type | Description |
+|--------|------|-------------|
+| `provider` | `string \| string[]` | Aptos fullnode REST API URL, or an ordered list for failover. Required for chain operations. |
+| `chainId` | `number` | Optional chain ID. If omitted, the module fetches it from ledger info on first use. |
+| `retries` | `number` | Failover attempts when `provider` is an array. Defaults to `3`. |
+| `txnExpirationSecs` | `number` | Transaction expiration window measured from the current time. Defaults to `60`. |
+| `transferMaxFee` | `number \| bigint` | Optional maximum estimated fee in octas for fungible asset `transfer()` calls. |
+
+An empty provider array behaves like no provider. Balance reads, quotes, receipt lookup, transaction signing, and transaction submission then throw a provider-required error.
+
+## Account Configuration
+
+You can construct accounts directly when you need a specific derivation path.
+
+```javascript
+import { WalletAccountAptos } from '@tetherto/wdk-wallet-aptos'
+
+const account = new WalletAccountAptos(seedPhrase, "0'/0'/0'", {
+ provider: 'https://fullnode.mainnet.aptoslabs.com/v1',
+ transferMaxFee: 100000n
+})
+```
+
+An address and provider are enough for balance and receipt reads:
+
+```javascript
+import { WalletAccountReadOnlyAptos } from '@tetherto/wdk-wallet-aptos'
+
+const readOnlyAccount = new WalletAccountReadOnlyAptos('0x...', {
+ provider: 'https://fullnode.mainnet.aptoslabs.com/v1'
+})
+```
+
+Fee quotes simulate a transaction and message verification uses Ed25519, so those operations also require the matching 32-byte public key:
+
+```javascript
+const readOnlyWithPublicKey = new WalletAccountReadOnlyAptos(
+ '0x...',
+ { provider: 'https://fullnode.mainnet.aptoslabs.com/v1' },
+ publicKey
+)
+```
+
+The constructor rejects a public key that does not derive the supplied address. Prefer `account.toReadOnlyAccount()` when you already have a writable account; it carries the matching public key forward.
+
+## Network Selection
+
+Network selection is controlled by the fullnode URL and the transaction chain ID. If you provide `chainId`, it must match the configured fullnode. The module does not compare a supplied chain ID with ledger info before signing.
+
+| Network | Provider URL |
+|---------|--------------|
+| Mainnet | `https://fullnode.mainnet.aptoslabs.com/v1` |
+| Testnet | `https://fullnode.testnet.aptoslabs.com/v1` |
+
+## Derivation Paths
+
+The module uses SLIP-0010 Ed25519 derivation. `getAccount(index)` derives:
+
+```text
+m/44'/637'/index'/0'/0'
+```
+
+Use `getAccountByPath(path)` to provide a relative path after `m/44'/637'/`.
+
+```javascript
+const account = await wallet.getAccountByPath("5'/0'/0'")
+```
+
+
+Every Aptos path segment must be hardened. A path segment without an apostrophe is invalid for this module's Ed25519 derivation.
+
+
+The relative path must contain exactly three hardened indexes without leading zeros, such as `"5'/0'/0'"`.
+
+## Fee Limit
+
+`transferMaxFee` caps the simulated fee for fungible asset `transfer()` calls. The transfer is rejected when the estimated fee is equal to or greater than the cap.
+
+```javascript
+const wallet = new WalletManagerAptos(seedPhrase, {
+ provider: 'https://fullnode.mainnet.aptoslabs.com/v1',
+ transferMaxFee: 100000n
+})
+```
+
+The cap does not apply to native APT `sendTransaction()` or `signTransaction()`. Use `quoteSendTransaction()` and enforce a separate application limit for native transfers when needed.
+
+## Security Notes
+
+- Keep seed phrases and seed bytes outside logs and client-visible error reporting.
+- Use trusted fullnode endpoints for production wallets.
+- Keep a supplied `chainId` consistent with every configured fullnode.
+- Set a fee cap for user-facing fungible asset transfers and check native APT quotes separately.
+- Call `dispose()` on accounts and managers when secret material is no longer needed.
+
+
+
+Create accounts, read balances, send transactions, and transfer fungible assets.
+
+
+Detailed class, method, config, and type reference.
+
+
diff --git a/content/docs/sdk/wallet-modules/wallet-aptos/index.mdx b/content/docs/sdk/wallet-modules/wallet-aptos/index.mdx
new file mode 100644
index 00000000..e65856c7
--- /dev/null
+++ b/content/docs/sdk/wallet-modules/wallet-aptos/index.mdx
@@ -0,0 +1,69 @@
+---
+title: Wallet Aptos Overview
+description: Overview of the @tetherto/wdk-wallet-aptos module.
+docType: explanation
+schemaType: TechArticle
+---
+
+The Aptos wallet module manages SLIP-0010 Ed25519 accounts for the Aptos blockchain through the shared WDK wallet interfaces.
+
+Use this module when an app needs Aptos account derivation, APT balances, fungible asset balances, native APT transfers, fungible asset transfers, fee quotes, message signing, and read-only account support.
+
+## Features
+
+- **BIP-39 seed support**: Accepts a mnemonic phrase or seed bytes.
+- **SLIP-0010 Ed25519 derivation**: Uses Aptos coin type `637` and hardened path segments.
+- **Aptos addresses**: Derives 32-byte Aptos addresses from the public key.
+- **Native APT support**: Sends native APT through `0x1::aptos_account::transfer`.
+- **Fungible asset support**: Reads and transfers Aptos fungible assets by metadata address.
+- **Fee estimation**: Simulates transactions to estimate gas before signing and submitting.
+- **Message signing**: Signs and verifies messages with Ed25519 keys.
+- **Read-only accounts**: Address-only accounts read balances and receipts. Accounts created with the matching public key can also quote fees and verify signatures.
+- **Bare runtime compatibility**: Uses the Aptos fullnode REST API over `fetch` instead of the Aptos SDK at runtime.
+
+## Supported Networks
+
+| Network | Chain ID | Fullnode example |
+|---------|----------|------------------|
+| Aptos Mainnet | `1` | `https://fullnode.mainnet.aptoslabs.com/v1` |
+| Aptos Testnet | `2` | `https://fullnode.testnet.aptoslabs.com/v1` |
+
+## Aptos-Specific Behavior
+
+| Area | Behavior |
+|------|----------|
+| Default derivation path | `m/44'/637'/account'/0'/0'` |
+| `getAccount(index)` mapping | Uses `index` as the `account` segment. |
+| Token model | Uses Aptos fungible asset metadata addresses, not coin type tags. |
+| APT units | APT balances and fees are returned in octas. |
+| Token transfers | `transfer()` builds and submits `0x1::primary_fungible_store::transfer`. |
+| Signing without broadcast | `signTransaction()` simulates and signs a native APT transfer without submitting it. It still requires a provider. |
+| Fungible asset fee cap | `transferMaxFee` applies to `transfer()` only and rejects an estimated fee at or above the cap. |
+
+
+All Aptos derivation path segments are hardened because the module uses Ed25519 with SLIP-0010 derivation.
+
+
+
+Native `sendTransaction()` and `signTransaction()` do not apply `transferMaxFee`. Quote native transfers and enforce an application-level limit before sending or signing when your product requires one.
+
+
+## Next Steps
+
+
+
+Configure Aptos fullnode access, derivation paths, and fee limits.
+
+
+Install the package, create accounts, read balances, send APT, transfer fungible assets, and sign messages.
+
+
+Review manager, account, read-only account, config, transaction, and result types.
+
+
+
+---
+
+## Need Help?
+
+
diff --git a/content/docs/sdk/wallet-modules/wallet-aptos/usage.mdx b/content/docs/sdk/wallet-modules/wallet-aptos/usage.mdx
new file mode 100644
index 00000000..95af9a9e
--- /dev/null
+++ b/content/docs/sdk/wallet-modules/wallet-aptos/usage.mdx
@@ -0,0 +1,199 @@
+---
+title: Wallet Aptos Usage
+description: Install and use @tetherto/wdk-wallet-aptos for Aptos accounts, balances, transfers, and signing.
+docType: how-to
+schemaType: TechArticle
+icon: BookOpen
+---
+
+## Install
+
+```bash
+npm install @tetherto/wdk-wallet-aptos
+```
+
+## Create a Wallet Manager
+
+```javascript
+import WalletManagerAptos from '@tetherto/wdk-wallet-aptos'
+
+const wallet = new WalletManagerAptos(seedPhrase, {
+ provider: 'https://fullnode.mainnet.aptoslabs.com/v1',
+ transferMaxFee: 100000n
+})
+
+const account = await wallet.getAccount(0)
+const address = await account.getAddress()
+```
+
+## Manage Accounts
+
+```javascript
+const first = await wallet.getAccount(0)
+const second = await wallet.getAccount(1)
+
+const custom = await wallet.getAccountByPath("5'/0'/0'")
+
+console.log(await first.getAddress())
+console.log(await second.getAddress())
+console.log(await custom.getAddress())
+```
+
+`getAccount(index)` maps to `m/44'/637'/index'/0'/0'`.
+
+## Read Balances
+
+```javascript
+const aptBalance = await account.getBalance()
+console.log('APT balance in octas:', aptBalance)
+
+const usdtMetadataAddress =
+ '0x357b0b74bc833e95a115ad22604854d6b0fca151cecd94111770e5d6ffc9dc2b'
+
+const usdtBalance = await account.getTokenBalance(usdtMetadataAddress)
+console.log('USDT balance:', usdtBalance)
+```
+
+Read-only accounts support the same balance reads without a seed phrase.
+
+```javascript
+import { WalletAccountReadOnlyAptos } from '@tetherto/wdk-wallet-aptos'
+
+const readOnlyAccount = new WalletAccountReadOnlyAptos('0x...', {
+ provider: 'https://fullnode.mainnet.aptoslabs.com/v1'
+})
+
+const balance = await readOnlyAccount.getBalance()
+```
+
+An address-only account can read balances and receipts. Fee quotes and message verification also need the matching Ed25519 public key. Use `account.toReadOnlyAccount()` when possible; the returned account includes it.
+
+## Send Native APT
+
+```javascript
+const nativeFeeLimit = 100000n
+
+const quote = await account.quoteSendTransaction({
+ to: '0x...',
+ value: 100000000n
+})
+
+console.log('Estimated fee in octas:', quote.fee)
+
+if (quote.fee >= nativeFeeLimit) {
+ throw new Error('Native APT fee is at or above the application limit')
+}
+
+const result = await account.sendTransaction({
+ to: '0x...',
+ value: 100000000n
+})
+
+console.log('Transaction hash:', result.hash)
+console.log('Fee in octas:', result.fee)
+```
+
+`sendTransaction()` submits a native APT transfer through `0x1::aptos_account::transfer`.
+
+## Transfer Fungible Assets
+
+Use the fungible asset metadata address as `token`.
+
+```javascript
+const quote = await account.quoteTransfer({
+ token: usdtMetadataAddress,
+ recipient: '0x...',
+ amount: 1000000n
+})
+
+console.log('Estimated fee in octas:', quote.fee)
+
+const result = await account.transfer({
+ token: usdtMetadataAddress,
+ recipient: '0x...',
+ amount: 1000000n
+})
+
+console.log('Transfer hash:', result.hash)
+console.log('Fee in octas:', result.fee)
+```
+
+`transfer()` submits `0x1::primary_fungible_store::transfer` and can auto-create the recipient primary store.
+
+When configured, `transferMaxFee` applies to this fungible asset flow only. `transfer()` rejects an estimated fee at or above the cap.
+
+## Sign and Verify Messages
+
+```javascript
+const message = 'Hello, Aptos'
+const signature = await account.sign(message)
+
+const readOnly = await account.toReadOnlyAccount()
+const valid = await readOnly.verify(message, signature)
+
+console.log('Signature valid:', valid)
+```
+
+## Sign Native APT Transfers Without Broadcasting
+
+`signTransaction()` simulates and signs a native APT transfer without broadcasting it. It still calls the configured fullnode for account sequence, gas, chain, and simulation data.
+
+```javascript
+const signed = await account.signTransaction({
+ to: '0x...',
+ value: 100000000n
+})
+
+console.log('Signed Aptos transaction:', signed)
+```
+
+
+This is not an offline operation. A failed simulation prevents signing. Fungible asset transfers are built and submitted through `transfer()`.
+
+
+`transferMaxFee` does not apply to native APT signing or sending. Quote the transfer and enforce your own native-fee limit before signing or submitting:
+
+```javascript
+const nativeFeeLimit = 100000n
+
+const quote = await account.quoteSendTransaction({
+ to: '0x...',
+ value: 100000000n
+})
+
+if (quote.fee >= nativeFeeLimit) {
+ throw new Error('Native APT fee is at or above the application limit')
+}
+```
+
+## Check Transaction Status
+
+```javascript
+const receipt = await account.getTransactionReceipt(result.hash)
+
+if (!receipt) {
+ console.log('Transaction is not known to this fullnode')
+} else if (receipt.type === 'pending_transaction') {
+ console.log('Transaction is pending')
+} else if (!receipt.success) {
+ console.error('Transaction failed:', receipt.vm_status)
+}
+```
+
+A non-null receipt is not necessarily final. Wait for `type === 'user_transaction'`, then inspect `success`.
+
+## Dispose Secret Material
+
+```javascript
+account.dispose()
+wallet.dispose()
+```
+
+
+
+Provider, fee, network, and derivation path configuration.
+
+
+Detailed method and type reference.
+
+
diff --git a/content/docs/sdk/wallet-modules/wallet-btc/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-btc/api-reference.mdx
index c5b98935..72901fdf 100644
--- a/content/docs/sdk/wallet-modules/wallet-btc/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-btc/api-reference.mdx
@@ -127,9 +127,9 @@ new WalletAccountBtc(seed, path, config)
|--------|-------------|---------|
| `getAddress()` | Returns the account's Bitcoin address | `Promise` |
| `getBalance()` | Returns the total account balance in satoshis, including unconfirmed funds when present | `Promise` |
-| `sendTransaction(options, timeoutMs?)` | Sends a Bitcoin transaction and optionally polls until spent inputs disappear from unspent outputs | `Promise<{hash: string, fee: bigint}>` |
+| `sendTransaction(tx, timeoutMs?)` | Signs and sends transaction options, or broadcasts signed raw transaction hex, then optionally polls spent inputs | `Promise<{hash: string, fee: bigint}>` |
| `signTransaction(options)` | Signs a Bitcoin transaction without broadcasting it | `Promise` |
-| `quoteSendTransaction(options)` | Estimates the fee for a transaction | `Promise<{fee: bigint}>` |
+| `quoteSendTransaction(tx)` | Estimates an unsigned transaction fee or calculates the fee of signed raw transaction hex | `Promise<{fee: bigint}>` |
| `getTransfers(options?)` | Returns the account's transfer history | `Promise` |
| `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise` |
| `getMaxSpendable()` | Returns the maximum spendable amount | `Promise` |
@@ -160,10 +160,10 @@ console.log('Balance:', balance, 'satoshis')
```
##### `sendTransaction(options, timeoutMs?)`
-Sends a Bitcoin transaction to a single recipient and optionally polls after broadcast until spent inputs disappear from the unspent-output set.
+Accepts either transaction options or signed raw Bitcoin transaction hex. With transaction options, the wallet builds, signs, and broadcasts a single-recipient transaction. With signed hex, it broadcasts the exact supplied transaction without rebuilding or signing it. Both paths can poll after broadcast until spent inputs disappear from the unspent-output set.
**Parameters:**
-- `options` (BtcTransaction): Transaction options
+- `options` (`BtcTransaction | string`): Transaction options or signed raw transaction hex
- `to` (string): Recipient's Bitcoin address
- `value` (number | bigint): Amount in satoshis
- `feeRate` (number | bigint, optional): Fee rate in sat/vB. If provided, overrides the fee rate estimated from the blockchain.
@@ -174,7 +174,7 @@ Sends a Bitcoin transaction to a single recipient and optionally polls after bro
- `hash`: Transaction hash
- `fee`: Transaction fee in satoshis
-**Throws:** Error if the estimated transaction fee exceeds `transactionMaxFee` when configured.
+**Throws:** Error if the transaction fee exceeds `transactionMaxFee` when configured. Invalid raw transaction data, unavailable previous transactions, and broadcast rejection errors propagate from the configured client.
**Example:**
```javascript
@@ -212,10 +212,10 @@ console.log('Signed transaction:', signedTransaction)
```
##### `quoteSendTransaction(options)`
-Estimates the fee for a transaction without broadcasting it.
+Estimates the fee for transaction options or calculates the fee encoded by signed raw transaction hex, without broadcasting it.
**Parameters:**
-- `options` (BtcTransaction): Same as sendTransaction options
+- `options` (`BtcTransaction | string`): Transaction options or signed raw transaction hex
- `to` (string): Recipient's Bitcoin address
- `value` (number | bigint): Amount in satoshis
- `feeRate` (number | bigint, optional): Fee rate in sat/vB. If provided, overrides the fee rate estimated from the blockchain.
@@ -233,6 +233,8 @@ const quote = await account.quoteSendTransaction({
console.log('Estimated fee:', quote.fee, 'satoshis')
```
+For signed hex, the wallet parses the transaction and fetches every referenced previous transaction through the configured client to calculate input value minus output value. This path requires network access, does not validate signatures before broadcast, does not broadcast, and does not apply `transactionMaxFee`.
+
##### `getTransfers(options?)`
Returns the account's transfer history with detailed transaction information.
diff --git a/content/docs/sdk/wallet-modules/wallet-btc/configuration.mdx b/content/docs/sdk/wallet-modules/wallet-btc/configuration.mdx
index 42add2d4..be14821b 100644
--- a/content/docs/sdk/wallet-modules/wallet-btc/configuration.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-btc/configuration.mdx
@@ -234,7 +234,7 @@ const config = {
### Transaction Max Fee
-The `transactionMaxFee` option sets the maximum fee, in satoshis, for BTC `sendTransaction()` and `signTransaction()` operations. Use it to stop a native BTC send or signed transaction from being created when the estimated fee is higher than your app allows.
+The `transactionMaxFee` option sets the maximum fee, in satoshis, for BTC `sendTransaction()` and `signTransaction()` operations. It blocks building or signing a transaction above the cap and also blocks `sendTransaction(signedHex)` from broadcasting externally supplied signed hex when its calculated fee is above the cap.
**Type:** `number | bigint` (optional)
diff --git a/content/docs/sdk/wallet-modules/wallet-btc/guides/get-started.mdx b/content/docs/sdk/wallet-modules/wallet-btc/guides/get-started.mdx
index 3aaf87d4..cbb112ff 100644
--- a/content/docs/sdk/wallet-modules/wallet-btc/guides/get-started.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-btc/guides/get-started.mdx
@@ -9,7 +9,7 @@ This guide explains how to [install the package](#1-install-the-package), [creat
### Prerequisites
-* **[Node.js](https://nodejs.org/)**: version 18 or higher.
+* **[Node.js](https://nodejs.org/)**: version 20.19.0 or higher. Since Wallet BTC beta.11, `@bitcoinerlab/descriptors` 3.1.7 sets this minimum; the requirement remains unchanged in beta.12.
* **[npm](https://www.npmjs.com/)**: usually comes with Node.js.
```bash title="Install @tetherto/wdk-wallet-btc"
diff --git a/content/docs/sdk/wallet-modules/wallet-btc/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-btc/guides/send-transactions.mdx
index 6f134564..406a9a88 100644
--- a/content/docs/sdk/wallet-modules/wallet-btc/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-btc/guides/send-transactions.mdx
@@ -3,7 +3,7 @@ title: Send Transactions
description: Send BTC and estimate transaction fees.
---
-This guide explains how to [send BTC](#send-btc), [extend post-broadcast polling](#extend-post-broadcast-polling), [sign without broadcasting](#sign-without-broadcasting), [estimate fees before sending](#estimate-fees), [cap transaction fees](#cap-transaction-fees), [use a custom fee rate](#send-with-custom-fee-rate), and [target a specific confirmation time](#send-with-confirmation-target).
+This guide explains how to [send BTC](#send-btc), [extend post-broadcast polling](#extend-post-broadcast-polling), [sign without broadcasting](#sign-without-broadcasting), [quote and broadcast signed hex](#quote-and-broadcast-signed-hex), [estimate fees before sending](#estimate-fees), [cap transaction fees](#cap-transaction-fees), [use a custom fee rate](#send-with-custom-fee-rate), and [target a specific confirmation time](#send-with-confirmation-target).
## Send BTC
@@ -60,6 +60,26 @@ console.log('Signed transaction:', signedTransaction)
`signTransaction()` returns the signed transaction hex. Use `sendTransaction()` when WDK should sign, broadcast, and return the transaction hash.
+## Quote and Broadcast Signed Hex
+
+A writable Bitcoin account can quote and broadcast a previously signed raw transaction. The wallet broadcasts the supplied hex without rebuilding or signing it again.
+
+```javascript title="Quote And Broadcast Signed BTC"
+const signedTransaction = await account.signTransaction({
+ to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
+ value: 100000n,
+ feeRate: 10n
+})
+
+const quote = await account.quoteSendTransaction(signedTransaction)
+console.log('Signed transaction fee:', quote.fee, 'satoshis')
+
+const result = await account.sendTransaction(signedTransaction)
+console.log('Transaction hash:', result.hash)
+```
+
+Signed-hex quoting parses the transaction and fetches its referenced previous transactions through the configured client. It requires network access and does not broadcast. Broadcasting applies `transactionMaxFee` when configured, but signature validity is ultimately checked by the Bitcoin network rather than pre-validated by this method.
+
## Estimate Fees
You can estimate the fee for a transaction without broadcasting it using [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-btc/api-reference#quotesendtransactionoptions):
diff --git a/content/docs/sdk/wallet-modules/wallet-btc/index.mdx b/content/docs/sdk/wallet-modules/wallet-btc/index.mdx
index 06876bf5..afa2fd08 100644
--- a/content/docs/sdk/wallet-modules/wallet-btc/index.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-btc/index.mdx
@@ -27,8 +27,8 @@ Use [`getAccountByPath`](/sdk/wallet-modules/wallet-btc/api-reference#getaccount
- **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase
- **Address Types Support**: Generate Native SegWit (P2WPKH) addresses by default, with Legacy (P2PKH) support via configuration
- **UTXO Management**: Track and manage unspent transaction outputs
-- **Offline Transaction Signing**: Sign Bitcoin transactions with `signTransaction()` without broadcasting them
-- **Transaction Management**: Create, sign, and broadcast Bitcoin transactions (single recipient per transaction)
+- **Non-broadcasting Transaction Signing**: Build and sign Bitcoin transactions with `signTransaction()` without broadcasting them. The method still uses the configured client to fetch UTXOs and fee data.
+- **Transaction Management**: Create, sign, and broadcast single-recipient Bitcoin transactions, or separately quote and broadcast signed raw transaction hex
- **Fee Estimation**: Dynamic fee calculation via mempool.space API
- **Provider Failover**: Configure ordered Electrum, WebSocket, Blockbook, or custom client fallbacks
- **TypeScript Support**: Full TypeScript definitions included
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/api-reference.mdx
index 2e5994bc..472bd871 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/api-reference.mdx
@@ -92,7 +92,7 @@ Treat the arrays returned through `keyPair` as read-only. Call `dispose()` when
| `verify(message, signature)` | `message: string`, `signature: string` | `Promise\` | Verifies a plain message signature. |
| `verifyTypedData(typedData, signature)` | `TypedData`, `signature: string` | `Promise\` | Verifies EIP-712 typed data. |
| `approve(options)` | `ApproveOptions` | `Promise\` | Sends an ERC-20 approval as a UserOperation. |
-| `quoteSendTransaction(tx, config?)` | `EvmTransaction \| EvmTransaction[]`, partial fee-mode config | `Promise\\>` | Returns `{ fee }`; owned accounts cache paymaster-token quotes for up to 2 minutes. |
+| `quoteSendTransaction(tx, config?)` | `EvmTransaction \| EvmTransaction[]`, partial fee-mode config | `Promise\\>` | Returns `{ fee }`; owned accounts cache paymaster-token quotes for up to 2 minutes and validate the nonce before reuse. |
| `sendTransaction(tx, config?)` | `EvmTransaction \| EvmTransaction[]`, partial fee-mode config | `Promise\` | Returns a UserOperation hash and fee. |
| `quoteTransfer(options, config?)` | `EvmTransferOptions`, partial fee-mode config | `Promise\\>` | Quotes an ERC-20 transfer. |
| `transfer(options, config?)` | `EvmTransferOptions`, partial fee-mode config | `Promise\` | Sends an ERC-20 transfer as a UserOperation. |
@@ -106,9 +106,10 @@ Treat the arrays returned through `keyPair` as read-only. Call `dispose()` when
- Sponsored mode returns `fee: 0n` from quote methods.
- Paymaster-token mode returns fees in the configured paymaster token's base units.
-- Owned account quotes are cached for up to 2 minutes for the same serialized transaction.
+- Owned account paymaster-token quotes are cached for up to 2 minutes for the same serialized transaction. Before reusing a cached UserOperation, the account reads the current EntryPoint nonce and re-quotes if it has moved.
+- The cache key contains the transaction but not the per-call fee-mode configuration. Use the same fee-mode and paymaster settings for a quote and its matching `sendTransaction()` or `transfer()` call.
- `sendTransaction()` signs the UserOperation typed data before submitting it to the bundler.
-- If the account is already delegated to `delegationAddress`, the send path does not include a new EIP-7702 authorization.
+- If the account is already delegated to `delegationAddress`, the send path does not include a new EIP-7702 authorization. A send that needs a fresh authorization rebuilds the UserOperation instead of reusing the cached one.
### Transfer Fee Cap
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/configuration.mdx b/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/configuration.mdx
index a9fd91dc..24b14882 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/configuration.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/configuration.mdx
@@ -164,3 +164,7 @@ const result = await account.sendTransaction({
```
When an override is provided, the merged config is validated before the operation runs.
+
+
+The two-minute quote cache is keyed by the transaction, not by this override. Pass the same fee-mode and paymaster settings to a quote and its matching send or transfer. If those settings must change, do not execute the same transaction from that account until its earlier cached quote has expired.
+
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/guides/send-transactions.mdx
index 3f16cefc..eee5d4cb 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-7702-gasless/guides/send-transactions.mdx
@@ -62,7 +62,11 @@ For paymaster-token mode, the fee is returned in the paymaster token's base unit
## Quote Reuse
-Owned accounts cache a recently quoted transaction for up to 2 minutes. If `sendTransaction()` receives the same transaction during that window, the account can reuse the built UserOperation. If the account must include a fresh EIP-7702 authorization, it rebuilds the UserOperation before sending.
+Owned accounts cache a recently quoted paymaster-token transaction for up to 2 minutes. If `sendTransaction()` receives the same transaction during that window, the account first reads the current EntryPoint nonce. It reuses the built UserOperation only when the nonce still matches; if the nonce moved, it quotes again. A send that needs a fresh EIP-7702 authorization also rebuilds the UserOperation before submission.
+
+
+Cache identity does not include per-call fee-mode configuration. Use the same fee-mode and paymaster settings for `quoteSendTransaction()` and the matching `sendTransaction()`. Do not quote with one paymaster token or mode and send the same transaction with another while the quote is cached.
+
## Override Fee Mode for One Send
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/api-reference.mdx
index 781ae33d..a8caf6eb 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/api-reference.mdx
@@ -42,7 +42,11 @@ new WalletManagerEvmErc4337(seed, config)
- `chainId` (number): The blockchain's ID (e.g., 1 for Ethereum mainnet)
- `provider` (string | Eip1193Provider | Array\): RPC endpoint URL, EIP-1193 provider instance, or ordered failover list
- `bundlerUrl` (string): The URL of the bundler service
- - `safeModulesVersion` (string): The Safe modules version (e.g., `'0.3.0'`)
+ - `safeModulesVersion` (string): Must be `'0.3.0'`, the only Safe modules version supported by beta.14
+
+**Optional common config fields:**
+ - `parallel` (boolean): Put each send, sign, or transfer in a fresh random 192-bit nonce-key lane
+ - `nonceKey` (number | bigint | string): Reuse a raw uint192 key or a deterministic named lane; takes precedence over `parallel`
**Gas payment mode** (one of the following):
@@ -267,9 +271,10 @@ const account = new WalletAccountEvmErc4337(seedPhrase, "0'/0/0", {
| `getAddress()` | Returns the Safe account's address | `Promise\` | - |
| `sign(message)` | Signs a message using the account's private key | `Promise\` | - |
| `verify(message, signature)` | Verifies a message signature | `Promise\` | - |
-| `sendTransaction(tx, config?)` | Sends a transaction via UserOperation | `Promise\<{hash: string, fee: bigint}\>` | If fee exceeds max |
-| `quoteSendTransaction(tx, config?)` | Estimates the fee for a UserOperation | `Promise\<{fee: bigint}\>` | - |
-| `transfer(options, config?, txOverrides?)` | Transfers ERC20 tokens via UserOperation | `Promise\<{hash: string, fee: bigint}\>` | If fee exceeds max |
+| `signTransaction(tx, config?)` | Builds and signs one UserOperation without submitting it | `Promise\` | If a non-sponsored fee exceeds max |
+| `sendTransaction(tx, config?)` | Builds and sends transactions, or submits a signed UserOperation | `Promise\<{hash: string, fee: bigint}\>` | If a newly built operation exceeds max |
+| `quoteSendTransaction(tx, config?)` | Estimates the fee for transactions or a signed UserOperation | `Promise\<{fee: bigint}\>` | - |
+| `transfer(options, config?, txOverrides?)` | Transfers ERC20 tokens via UserOperation | `Promise\<{hash: string, fee: bigint}\>` | If fee meets or exceeds max |
| `quoteTransfer(options, config?, txOverrides?)` | Estimates the fee for an ERC20 transfer | `Promise\<{fee: bigint}\>` | - |
| `approve(options, txOverrides?)` | Approves a spender to spend ERC20 tokens | `Promise\<{hash: string, fee: bigint}\>` | - |
| `getBalance()` | Returns the native token balance (in wei) | `Promise\` | - |
@@ -325,21 +330,45 @@ const isValid = await account.verify(message, signature)
console.log('Signature valid:', isValid)
```
+##### `signTransaction(tx, config?)`
+Builds and signs one ERC-4337 v0.7 UserOperation without submitting it to the bundler.
+
+**Parameters:**
+- `tx` (EvmErc4337Transaction): One transaction. Batch arrays are not accepted by this method.
+- `config` (optional): Per-call configuration override (see [Config Override](#config-override)).
+
+**Returns:** `Promise\` - A signed UserOperation that can be quoted or submitted later
+
+**Throws:** Error if a non-sponsored operation exceeds `transactionMaxFee`, or a raw `nonceKey` is outside `0..2^192-1`
+
+```javascript title="Sign a UserOperation"
+const signedUserOperation = await account.signTransaction({
+ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
+ value: 1000000000000000n
+})
+```
+
+
+The returned operation contains the selected nonce lane and fee-mode configuration used while building it. Submit it promptly through the same account and do not mutate it.
+
+
##### `sendTransaction(tx, config?)`
Sends a transaction via UserOperation through the bundler.
**Parameters:**
-- `tx` (EvmErc4337Transaction | EvmErc4337Transaction[]): Transaction object or array for batch transactions
+- `tx` (EvmErc4337Transaction | EvmErc4337Transaction[] | UserOperationV7): Transaction object, array for batch transactions, or a signed UserOperation
- `to` (string): Recipient address
- `value` (number | bigint): Amount in wei
- `data` (string, optional): Transaction data in hex format
- `callGasLimit`, `verificationGasLimit`, `preVerificationGas` (number | bigint, optional): Per-call overrides for the UserOperation gas limits
- `maxFeePerGas`, `maxPriorityFeePerGas` (number | bigint, optional): Per-call overrides for the EIP-1559 fee pair; set both together. In a batch, only the first transaction's gas overrides apply (see [EvmErc4337Transaction](#evmerc4337transaction))
-- `config` (optional): Per-call configuration override. Accepts a partial version of the gas payment mode fields (see [Config Override](#config-override)).
+- `config` (optional): Per-call configuration override. Beta.14 runtime also reads `parallel` and `nonceKey`, subject to the [published typing limitation](#config-override).
**Returns:** `Promise\<{hash: string, fee: bigint}\>` - UserOperation hash and fee
-**Throws:** Error if fee exceeds `transactionMaxFee`
+**Throws:** Error if a non-sponsored, newly built operation exceeds `transactionMaxFee`, or a raw `nonceKey` is outside `0..2^192-1`
+
+When `tx` is a signed `UserOperationV7`, WDK forwards that exact operation to the bundler. It does not rebuild or re-sign it, and it does not apply the current `transactionMaxFee` again. Only submit an operation produced by the same account with the intended fee-mode configuration, and submit it before its nonce becomes stale.
**Example:**
```javascript
@@ -375,19 +404,27 @@ const fastResult = await account.sendTransaction({
})
```
-The account reserves a local nonce for each pending `sendTransaction()` or `transfer()` from the same account instance. Pre-acceptance failures release the reserved nonce; ambiguous transport failures keep it reserved to avoid nonce reuse.
+Beta.14 does not reserve sequential nonces locally. With no lane option, sends use the default key-0 lane and must not overlap. `parallel: true` creates a new random lane; `nonceKey` selects a reusable named or raw lane and takes precedence. Operations sharing a lane remain sequential, so wait for inclusion before reusing it. Distinct lanes are unordered; batch dependent transactions into one UserOperation.
##### `quoteSendTransaction(tx, config?)`
Estimates the fee for a UserOperation without sending it.
-Quoted UserOperations are cached internally for up to 2 minutes. When a later `sendTransaction()` matches the quoted transaction, the account performs a lightweight on-chain nonce check before reuse and re-quotes if the nonce has moved.
+Default-lane, non-sponsored UserOperations built while quoting transaction inputs can be cached internally for up to 2 minutes. When a later default-lane `sendTransaction()` or `signTransaction()` matches, the account performs a lightweight on-chain nonce check before reuse and rebuilds if the nonce has moved. Sponsored quotes do not cache a built operation. A supplied signed UserOperation is evaluated directly rather than rebuilt.
+
+This quote method does not resolve or reserve a nonce lane. A later send or sign using `parallel` or `nonceKey` rebuilds in that lane instead of reusing the quote.
+
+
+The cache key contains the transaction but not the per-call fee-mode configuration. Use the same fee-mode and paymaster settings for a quote and its matching send, sign, or transfer. Do not quote under one mode, token, paymaster, or sponsorship policy and execute the same transaction under another while the quote is cached.
+
**Parameters:**
-- `tx` (EvmErc4337Transaction | EvmErc4337Transaction[]): Transaction object or array (same as sendTransaction)
+- `tx` (EvmErc4337Transaction | EvmErc4337Transaction[] | UserOperationV7): Transaction object, array, or signed UserOperation
- `config` (optional): Per-call configuration override (see [Config Override](#config-override))
**Returns:** `Promise\<{fee: bigint}\>` - Fee estimate
+For a signed UserOperation, sponsored mode returns `0n`. Other modes return a 20% buffered native-gas ceiling in wei. In paymaster-token mode, this signed-operation quote is not a token-denominated paymaster charge.
+
**Example:**
```javascript
const quote = await account.quoteSendTransaction({
@@ -411,8 +448,9 @@ Transfers ERC20 tokens via UserOperation.
**Returns:** `Promise\<{hash: string, fee: bigint}\>` - UserOperation hash and fee
**Throws:**
-- Error if fee exceeds `transferMaxFee`
+- Error if fee meets or exceeds `transferMaxFee`
- Error if insufficient token balance
+- Error if a raw `nonceKey` is outside `0..2^192-1`
**Example:**
```javascript
@@ -426,13 +464,15 @@ const result = await account.transfer({
maxFeePerGas: 30000000000n,
maxPriorityFeePerGas: 2000000000n
})
-console.log('Transfer hash:', result.hash)
+console.log('Transfer UserOperation hash:', result.hash)
console.log('Transfer fee:', result.fee)
```
##### `quoteTransfer(options, config?, txOverrides?)`
Estimates the fee for an ERC20 token transfer.
+This method does not resolve or reserve a nonce lane. A later transfer using `parallel` or `nonceKey` rebuilds in that lane.
+
**Parameters:**
- `options` (TransferOptions): Transfer options (same as transfer)
- `config` (optional): Per-call configuration override (see [Config Override](#config-override))
@@ -1007,7 +1047,9 @@ interface EvmErc4337WalletCommonConfig {
chainId: number; // Blockchain ID
provider: string | Eip1193Provider | Array; // RPC provider or failover list
bundlerUrl: string; // Bundler service URL
- safeModulesVersion: string; // Safe modules version (e.g., '0.3.0')
+ safeModulesVersion: string; // Must be '0.3.0' in beta.14
+ parallel?: boolean; // Fresh random nonce-key lane per operation
+ nonceKey?: number | bigint | string; // Reusable raw or named uint192 lane key
}
// Mode 1: Paymaster Token
@@ -1046,7 +1088,7 @@ type EvmErc4337WalletConfig = EvmErc4337WalletCommonConfig &
### Config Override
-The `config` parameter on `sendTransaction`, `signTransaction`, `quoteSendTransaction`, `transfer`, and `quoteTransfer` allows per-call overrides of gas payment settings:
+The published beta.14 `config` parameter type on `sendTransaction`, `signTransaction`, `quoteSendTransaction`, `transfer`, and `quoteTransfer` allows per-call overrides of gas payment settings:
```typescript
type ConfigOverride = Partial<
@@ -1057,8 +1099,8 @@ type ConfigOverride = Partial<
```
**Available override fields:**
-- `isSponsored` (boolean): Switch to sponsorship mode
-- `useNativeCoins` (boolean): Switch to native coin mode
+- `isSponsored` (boolean): Enable or disable sponsorship mode
+- `useNativeCoins` (boolean): Enable or disable native-coin mode
- `paymasterUrl` (string): Override paymaster URL
- `paymasterAddress` (string): Override paymaster contract
- `paymasterToken` (\{address: string\}): Override paymaster token
@@ -1066,6 +1108,12 @@ type ConfigOverride = Partial<
- `transactionMaxFee` (number | bigint): Override maximum fee for `sendTransaction()` and `signTransaction()`
- `transferMaxFee` (number | bigint): Override maximum fee
+Overrides are shallow-merged with the account configuration, then validated. When switching modes, explicitly clear an inherited opposing flag: sponsorship requires `isSponsored: true` and `useNativeCoins: false`; native-coin mode requires `isSponsored: false` and `useNativeCoins: true`; paymaster-token mode requires both flags to be `false`. The merged configuration must also contain the URL, address, token, or policy fields required by the selected mode.
+
+At runtime, beta.14 additionally honors `parallel` and `nonceKey` per call for `signTransaction()`, `sendTransaction()`, and `transfer()`. Those two fields are missing from the published per-call declarations, so object literals containing them fail TypeScript excess-property checks even though JavaScript calls work. Construction-level `EvmErc4337WalletCommonConfig` includes both fields and is the typed path in this release. `quoteSendTransaction()` and `quoteTransfer()` do not select a lane.
+
+The quote cache does not include fee-mode override fields in its key. Pass the same fee-mode and paymaster settings to a quote and its matching send, sign, or transfer. Lane sends, signs, and transfers bypass cached UserOperations and rebuild in the selected lane.
+
### EvmErc4337Transaction
The transaction shape accepted by `sendTransaction`, `quoteSendTransaction`, and `signTransaction`. Beyond the call fields (`to`, `value`, `data`), it accepts optional gas overrides that are applied to the resulting UserOperation.
@@ -1085,6 +1133,16 @@ interface EvmErc4337Transaction {
The gas-override fields are optional. When omitted, the gas limits fall back to AbstractionKit's estimation and the fee pair (`maxFeePerGas` / `maxPriorityFeePerGas`) falls back to the bundler-fetched gas price. Setting either fee field disables the bundler-fetched fee fallback for both, so set them together. In a batched call (`tx` passed as an array), only the gas overrides on the first transaction are honored — a UserOperation carries a single set of gas fields regardless of how many calls it batches.
+### UserOperationV7
+
+`UserOperationV7` is the ERC-4337 v0.7 operation type re-exported by `@tetherto/wdk-wallet-evm-erc-4337` from AbstractionKit.
+
+```typescript
+import type { UserOperationV7 } from '@tetherto/wdk-wallet-evm-erc-4337'
+```
+
+`signTransaction()` returns this type with its `signature` populated. Passing that signed value to `quoteSendTransaction()` estimates its submitted cost; passing it to `sendTransaction()` submits the same operation without rebuilding it.
+
### EvmErc4337GasOverrides
The helper methods `transfer()`, `quoteTransfer()`, and `approve()` accept these same UserOperation gas and fee override fields as a separate `txOverrides` argument.
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/configuration.mdx b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/configuration.mdx
index f606885a..40d6241f 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/configuration.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/configuration.mdx
@@ -17,7 +17,7 @@ const config = {
// Required parameters
chainId: 1,
provider: 'https://rpc.mevblocker.io/fast',
- safeModulesVersion: '0.3.0', // '0.2.0' and '0.3.0' are valid
+ safeModulesVersion: '0.3.0',
bundlerUrl: `https://api.pimlico.io/v1/ethereum/rpc?apikey=${PIMLICO_API_KEY}`,
paymasterUrl: `https://api.pimlico.io/v2/ethereum/rpc?apikey=${PIMLICO_API_KEY}`,
paymasterAddress: '0x777777777777AeC03fd955926DbF81597e66834C',
@@ -47,7 +47,7 @@ const account = new WalletAccountEvmErc4337(
// Read-only account (fee caps for sending are not needed)
const readOnlyAccount = new WalletAccountReadOnlyEvmErc4337(
- '0x...', // Smart contract wallet address
+ '0x...', // Owner EOA address; the module predicts the Safe address
{
chainId: 1,
provider: 'https://rpc.mevblocker.io/fast',
@@ -213,9 +213,37 @@ const config = {
}
```
+### Parallel Nonce Lanes
+
+ERC-4337 supports two-dimensional nonces: a 192-bit key selects a lane, and each lane has its own sequence. By default this module uses key `0`, so do not fire another default-lane operation until the previous one has been included.
+
+Use these optional common configuration fields when the account needs independent lanes:
+
+- `parallel` (`boolean`, default `false`): Give each send, sign, or transfer operation a fresh random lane at sequence `0`. Ordering across those lanes is not guaranteed. Each successfully included UserOperation in a fresh lane creates or advances a distinct EntryPoint nonce slot; signing alone and unincluded submissions do not advance on-chain nonce state.
+- `nonceKey` (`number | bigint | string`): Reuse an explicit lane. A string is hashed into a deterministic named lane. A number or `bigint` is used as the raw uint192 key and must be in `0..2^192-1`; use `bigint` for raw keys above `Number.MAX_SAFE_INTEGER`.
+
+`nonceKey` takes precedence over `parallel`; when neither is set, the module uses the default key-0 lane.
+
+Before enabling either option, configure the account without a lane, submit one default-lane operation, and wait for its receipt so the Safe is deployed. Confirm that the configured bundler accepts nonzero nonce keys, then create the account with the lane setting.
+
+```javascript title="Configure A Named Lane After Safe Deployment"
+const config = {
+ // ...required network and gas-payment fields
+ nonceKey: 'scheduled-payments'
+}
+
+const wallet = new WalletManagerEvmErc4337(seedPhrase, config)
+```
+
+Reusing one named lane remains sequential: wait for inclusion before submitting its next operation, or batch dependent calls into one UserOperation.
+
+
+The beta.14 runtime also honors `parallel` and `nonceKey` in the per-call config for `signTransaction()`, `sendTransaction()`, and `transfer()`, but the published per-call TypeScript declarations omit both fields. Construction-level configuration is typed; JavaScript per-call usage works at runtime.
+
+
### Safe Modules Version
-The `safeModulesVersion` option specifies the Safe modules version for smart contract wallet implementation. **Required** for smart account initialization.
+The `safeModulesVersion` option specifies the Safe modules version for smart contract wallet implementation. Beta.14 supports only `0.3.0`; other values, including `0.2.0`, throw `ConfigurationError`. **Required** for smart account initialization.
**Type:** `string`
**Required:** Yes
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/handle-errors.mdx b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/handle-errors.mdx
index 3eb895ea..cc0e6fe6 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/handle-errors.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/handle-errors.mdx
@@ -3,7 +3,7 @@ title: Handle Errors
description: Handle errors, manage fees, and dispose of sensitive data.
---
-This guide explains how to [handle transaction errors](#transaction-errors), [handle transfer errors](#transfer-errors), and follow [best practices](#best-practices) for fee management and memory cleanup.
+This guide explains how to [handle transaction errors](#transaction-errors), [handle transfer errors](#transfer-errors), [diagnose nonce-lane failures](#nonce-lane-failures), and follow [best practices](#best-practices) for fee management and memory cleanup.
## Transaction Errors
@@ -15,7 +15,7 @@ try {
to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
value: 1000000000000000n
})
- console.log('Transaction hash:', result.hash)
+ console.log('UserOperation hash:', result.hash)
} catch (error) {
if (error.message.includes('not enough funds')) {
console.error('Insufficient paymaster token balance')
@@ -27,7 +27,7 @@ try {
## Transfer Errors
-Token transfers via [`account.transfer()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) can fail due to insufficient balance or exceeding the maximum fee limit:
+Token transfers via [`account.transfer()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference) can fail due to insufficient balance or a fee at or above the maximum limit:
```javascript title="Handle Transfer Errors"
try {
@@ -36,10 +36,10 @@ try {
recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
amount: 1000000
})
- console.log('Transfer hash:', result.hash)
+ console.log('Transfer UserOperation hash:', result.hash)
} catch (error) {
if (error.message.includes('Exceeded maximum fee')) {
- console.error('Transfer cancelled: fee exceeds the configured limit')
+ console.error('Transfer cancelled: fee meets or exceeds the configured limit')
} else if (error.message.includes('not enough funds')) {
console.error('Insufficient paymaster token balance')
} else {
@@ -48,6 +48,16 @@ try {
}
```
+## Nonce-Lane Failures
+
+Lane configuration and bundler behavior can fail in several ways:
+
+- A raw `nonceKey` below `0` or above `2^192 - 1` throws `nonceKey must be within the uint192 range (0 to 2^192 - 1).`
+- Parallel sends from an undeployed Safe or a bundler that does not support nonzero keys can be rejected by the RPC or bundler. Deploy the account with one completed operation first and verify bundler support.
+- Two operations submitted concurrently in the same named or default lane can select the same sequence. Both calls can return a UserOperation hash even though one never receives a receipt.
+
+Treat a returned hash as submission evidence, not inclusion. Poll `getUserOperationReceipt(hash)` with an application timeout. If one same-lane hash never resolves, inspect the bundler response and on-chain lane sequence before retrying; use a fresh lane for independent work or batch dependent calls rather than blindly resubmitting the same operation.
+
## Best Practices
### Fee Management
@@ -70,7 +80,7 @@ try {
to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
value: 1000000000000000n
})
- console.log('Transaction hash:', result.hash)
+ console.log('UserOperation hash:', result.hash)
} finally {
account.dispose()
wallet.dispose()
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/send-transactions.mdx
index 063ae29e..88233ba7 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/send-transactions.mdx
@@ -3,7 +3,7 @@ title: Send Transactions
description: Send gasless transactions and estimate fees.
---
-This guide explains how to [send a gasless transaction](#send-a-gasless-transaction), [estimate fees](#estimate-fees), [cap transaction fees](#cap-transaction-fees), [reuse a recent quote](#reuse-a-recent-quote), and [use a custom paymaster token](#send-with-custom-paymaster-token).
+This guide explains how to [send a gasless transaction](#send-a-gasless-transaction), [estimate fees](#estimate-fees), [sign, quote, and submit a UserOperation](#sign-quote-and-submit-a-useroperation), [cap transaction fees](#cap-transaction-fees), [reuse a recent quote](#reuse-a-recent-quote), [use parallel nonce lanes](#use-parallel-nonce-lanes), and [use a custom paymaster token](#send-with-custom-paymaster-token).
## Send a Gasless Transaction
@@ -14,7 +14,7 @@ const result = await account.sendTransaction({
to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
value: 1000000000000000n // 0.001 ETH in wei
})
-console.log('Transaction hash:', result.hash)
+console.log('UserOperation hash:', result.hash)
console.log('Fee paid in paymaster token:', result.fee)
```
@@ -34,9 +34,33 @@ const quote = await account.quoteSendTransaction({
console.log('Estimated fee:', quote.fee)
```
+## Sign, Quote, and Submit a UserOperation
+
+Use `signTransaction()` when a separate review or relay step needs a signed ERC-4337 v0.7 UserOperation. The method accepts one transaction, not a batch.
+
+```javascript title="Sign, Quote, and Submit"
+const tx = {
+ to: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
+ value: 1000000000000000n
+}
+
+const signedUserOperation = await account.signTransaction(tx)
+const quote = await account.quoteSendTransaction(signedUserOperation)
+console.log('Signed operation fee ceiling:', quote.fee)
+
+const result = await account.sendTransaction(signedUserOperation)
+console.log('UserOperation hash:', result.hash)
+```
+
+
+Only submit an operation produced by the same account with the intended fee-mode configuration. WDK preserves the signed nonce and configuration, does not rebuild or re-sign the operation, and does not reapply `transactionMaxFee` during signed submission. Do not mutate the operation, and submit it before its nonce becomes stale.
+
+
+For signed UserOperations, sponsored mode quotes `0n`. Other modes quote a 20% buffered native-gas ceiling in wei. In paymaster-token mode, that signed-operation quote is not a token-denominated paymaster charge.
+
## Cap Transaction Fees
-Set `transactionMaxFee` to reject non-sponsored `sendTransaction()` and `signTransaction()` operations when the estimated UserOperation fee is above your cap. Use `transferMaxFee` separately for token transfers.
+Set `transactionMaxFee` to reject non-sponsored operations newly built by `sendTransaction()` or `signTransaction()` when the estimated UserOperation fee is above your cap. A signed UserOperation passed to `sendTransaction()` is not checked against the cap again. Use `transferMaxFee` separately for token transfers.
```javascript title="Cap sendTransaction fees"
const result = await account.sendTransaction({
@@ -53,7 +77,13 @@ const result = await account.sendTransaction({
## Reuse a Recent Quote
-Quotes are cached for up to 2 minutes. If you call `sendTransaction()` with the same transaction during that window, the account checks the current on-chain nonce before reusing the cached UserOperation. If the nonce has moved, it builds a fresh quote before sending.
+Default-lane, non-sponsored UserOperations built while quoting can be cached for up to 2 minutes. If you call `sendTransaction()` with the same transaction during that window, the account checks the current on-chain nonce before reusing a cached UserOperation. If the nonce has moved, it rebuilds before sending. Sponsored quotes do not cache a built UserOperation.
+
+Quote methods do not resolve or reserve a nonce lane. A later send or sign using `parallel` or `nonceKey` rebuilds in that selected lane instead of reusing the quoted UserOperation.
+
+
+Cache identity does not include per-call fee-mode configuration. Use the same fee-mode and paymaster settings for `quoteSendTransaction()` and the matching `sendTransaction()` or `signTransaction()`. Do not quote with one mode, paymaster token, or sponsorship policy and execute the same transaction with another while the quote is cached.
+
```javascript title="Quote Then Send"
const tx = {
@@ -65,20 +95,44 @@ const quote = await account.quoteSendTransaction(tx)
console.log('Estimated fee:', quote.fee)
const result = await account.sendTransaction(tx)
-console.log('Transaction hash:', result.hash)
+console.log('UserOperation hash:', result.hash)
```
-## Send Rapid Transactions
+## Use Parallel Nonce Lanes
-When you call `sendTransaction()` or `transfer()` rapidly from the same account instance, the wallet reserves local nonces so concurrent UserOperations use sequential nonces. If submission fails before bundler acceptance, the reserved nonce is released. If the failure is ambiguous, such as a transport error after submission, the wallet keeps the nonce reserved to avoid accidental reuse.
+Beta.14 no longer reserves sequential nonces locally. By default, operations use ERC-4337 nonce key `0`; two default-lane sends started before the first is included can select the same sequence and collide.
-
-The wallet reads the on-chain nonce before reserving locally. If that read does not complete within 30 seconds, the operation fails instead of guessing the next nonce.
+Before using nonzero lanes, deploy the Safe with one operation and wait for its UserOperation receipt. Your bundler must support parallel nonce keys, and its per-sender mempool limits still apply.
+
+Use `parallel: true` to give each independent operation a fresh random lane:
+
+```javascript title="Send Independent Operations Concurrently"
+const [first, second] = await Promise.all([
+ account.sendTransaction({ to: recipientA, value: 0n }, { parallel: true }),
+ account.sendTransaction({ to: recipientB, value: 0n }, { parallel: true })
+])
+
+console.log(first.hash, second.hash) // UserOperation hashes
+```
+
+Use distinct named lanes for stable independent work streams. Strings are hashed as labels; they are not parsed as numeric keys.
+
+```javascript title="Use Named Nonce Lanes"
+const payroll = await account.sendTransaction(txA, { nonceKey: 'payroll' })
+const refunds = await account.sendTransaction(txB, { nonceKey: 'refunds' })
+```
+
+`nonceKey` takes precedence over `parallel`. A raw numeric key must fit uint192; use a `bigint` above `Number.MAX_SAFE_INTEGER`.
+
+
+One lane is still sequential. Two concurrent sends using the same named or default lane can return UserOperation hashes even though one never receives a receipt. Wait for inclusion before reusing a lane. If calls depend on one another, batch them with `sendTransaction([tx1, tx2])` so they execute in order under one nonce. Different lanes are independent and can be included in either order.
+`signTransaction()` preserves the lane chosen by the same configuration rules. In beta.14, per-call `parallel` and `nonceKey` work at runtime but are missing from the published per-call TypeScript declarations; construction-level lane configuration is typed.
+
## Send with Custom Paymaster Token
-You can override the default paymaster token for a specific transaction by passing a config object to [`account.sendTransaction()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference):
+You can override the default paymaster token for a specific transaction by passing a config object to [`account.sendTransaction()`](/sdk/wallet-modules/wallet-evm-erc-4337/api-reference). This example assumes the account is already in paymaster-token mode:
```javascript title="Custom Paymaster Token"
const result = await account.sendTransaction({
@@ -91,6 +145,8 @@ const result = await account.sendTransaction({
})
```
+When switching from sponsored or native-coin mode, also set `isSponsored: false` and `useNativeCoins: false`, and provide any paymaster fields absent from the account configuration.
+
## Next Steps
Learn how to [transfer ERC-20 tokens](/sdk/wallet-modules/wallet-evm-erc-4337/guides/transfer-tokens).
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/transfer-tokens.mdx b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/transfer-tokens.mdx
index 5a8ef189..2f7aebb6 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/transfer-tokens.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/guides/transfer-tokens.mdx
@@ -3,7 +3,7 @@ title: Transfer Tokens
description: Transfer ERC-20 tokens with gasless transactions.
---
-This guide explains how to [transfer ERC-20 tokens](#transfer-erc-20-tokens), [estimate transfer fees](#estimate-transfer-fees), [set a maximum fee limit](#transfer-with-maximum-fee-limit), and [apply UserOperation gas overrides](#transfer-with-useroperation-gas-overrides).
+This guide explains how to [transfer ERC-20 tokens](#transfer-erc-20-tokens), [estimate transfer fees](#estimate-transfer-fees), [set a maximum fee limit](#transfer-with-maximum-fee-limit), [select a nonce lane](#transfer-in-a-nonce-lane), and [apply UserOperation gas overrides](#transfer-with-useroperation-gas-overrides).
## Transfer ERC-20 Tokens
@@ -15,7 +15,7 @@ const result = await account.transfer({
recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
amount: 1000000 // 1 USDT (6 decimals)
})
-console.log('Transfer hash:', result.hash)
+console.log('Transfer UserOperation hash:', result.hash)
console.log('Transfer fee:', result.fee)
```
@@ -47,9 +47,25 @@ const result = await account.transfer({
```
-If the estimated fee exceeds `transferMaxFee`, the transfer is cancelled with an "Exceeded maximum fee" error.
+If the estimated fee meets or exceeds `transferMaxFee`, the transfer is cancelled with an "Exceeded maximum fee" error.
+## Transfer in a Nonce Lane
+
+`transfer()` follows the same beta.14 nonce-lane rules as sends. Use a stable named lane for an independent transfer stream, and wait for inclusion before reusing that lane:
+
+```javascript title="Transfer In A Named Lane"
+const result = await account.transfer({
+ token: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
+ recipient: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
+ amount: 1000000
+}, {
+ nonceKey: 'token-payouts'
+})
+```
+
+`quoteTransfer()` estimates the transfer but does not select or reserve a lane. A lane transfer rebuilds its UserOperation. The beta.14 runtime accepts lane fields per call, while the published per-call TypeScript declaration omits them; see [Use Parallel Nonce Lanes](/sdk/wallet-modules/wallet-evm-erc-4337/guides/send-transactions#use-parallel-nonce-lanes).
+
## Transfer with UserOperation Gas Overrides
Use the third argument to pass UserOperation gas or EIP-1559 fee overrides through `transfer()` or `quoteTransfer()`. Set `maxFeePerGas` and `maxPriorityFeePerGas` together.
diff --git a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/index.mdx b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/index.mdx
index 3136210a..c3aee06f 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/index.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm-erc-4337/index.mdx
@@ -14,6 +14,8 @@ Use the ERC-4337 wallet module when your app needs EVM smart accounts, UserOpera
- **Multi-Account Management**: Create and manage multiple account abstraction wallets from a single seed phrase
- **ERC-4337 Support**: Full implementation of ERC-4337 account abstraction standard
- **UserOperation Management**: Create and send UserOperations through bundlers
+- **Separated Submission Flow**: Sign one UserOperation, review or quote it, then submit the same operation
+- **Parallel Nonce Lanes**: Opt into independent ERC-4337 nonce keys for concurrent UserOperations
- **Message Signing**: Sign and verify messages using EVM cryptography
- **ERC20 Support**: Query native token and ERC20 token balances using smart contract interactions
- **TypeScript Support**: Full TypeScript definitions included
diff --git a/content/docs/sdk/wallet-modules/wallet-evm/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-evm/api-reference.mdx
index a3e47806..1d1ca5d4 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm/api-reference.mdx
@@ -281,8 +281,8 @@ const signerAccount = new WalletAccountEvm(
| `signTransaction(tx)` | Signs an EVM transaction without broadcasting it | `Promise` | If transaction signing fails |
| `verify(message, signature)` | Verifies a message signature | `Promise` | - |
| `verifyTypedData(typedData, signature)` | Verifies a typed data signature (EIP-712) | `Promise` | - |
-| `sendTransaction(tx)` | Sends an EVM transaction | `Promise<{hash: string, fee: bigint}>` | If no provider |
-| `quoteSendTransaction(tx)` | Estimates the fee for an EVM transaction | `Promise<{fee: bigint}>` | If no provider |
+| `sendTransaction(tx)` | Sends an EVM transaction object | `Promise<{hash: string, fee: bigint}>` | If no provider |
+| `quoteSendTransaction(tx)` | Estimates the fee for an EVM transaction object or serialized transaction | `Promise<{fee: bigint}>` | If no provider |
| `transfer(options)` | Transfers ERC20 tokens to another address | `Promise<{hash: string, fee: bigint}>` | If no provider or fee exceeds max |
| `quoteTransfer(options)` | Estimates the fee for an ERC20 transfer | `Promise<{fee: bigint}>` | If no provider |
| `getBalance()` | Returns the native token balance (in wei) | `Promise` | If no provider |
@@ -443,8 +443,12 @@ console.log('Typed data signature valid:', isValid) // true
#### `sendTransaction(tx)`
Sends an EVM transaction and returns the result with hash and fee.
+
+In `1.0.0-beta.16`, the TypeScript declaration also accepts a serialized transaction string, but the send path does not broadcast those supplied bytes. It repopulates a transaction from the value passed to the method and can therefore broadcast a different transaction. Pass an `EvmTransaction` object here. Submit signed raw transactions through a separate relay or provider until this runtime mismatch is resolved.
+
+
**Parameters:**
-- `tx` (EvmTransaction): The transaction object
+- `tx` (EvmTransaction | string): The declared input type. Use the `EvmTransaction` object form for sending in `1.0.0-beta.16`.
- `to` (string | null, optional): Recipient address; omit or pass `null` for contract creation
- `value` (number | bigint): Amount in wei
- `data` (string, optional): Transaction data in hex format
@@ -489,7 +493,9 @@ console.log('Transaction fee:', result.fee, 'wei')
Estimates the fee for an EVM transaction without sending it.
**Parameters:**
-- `tx` (EvmTransaction): The transaction object (same format as sendTransaction)
+- `tx` (EvmTransaction | string): A transaction object or serialized transaction string
+
+For a serialized transaction, the method parses the transaction fields for gas estimation but uses the provider's current fee data to calculate the quote. The result is a current network estimate and does not necessarily reproduce the fee settings embedded in the serialized transaction.
**Returns:** `Promise<{fee: bigint}>` - Fee estimate in wei
diff --git a/content/docs/sdk/wallet-modules/wallet-evm/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-evm/guides/send-transactions.mdx
index 7c8307ab..4cac24a6 100644
--- a/content/docs/sdk/wallet-modules/wallet-evm/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-evm/guides/send-transactions.mdx
@@ -73,7 +73,7 @@ console.log('Signed transaction:', signedTransaction)
```
-`signTransaction()` returns the signed transaction payload and does not broadcast it. Use `sendTransaction()` when WDK should sign, broadcast, and return the transaction hash.
+`signTransaction()` returns the signed transaction payload and does not broadcast it. In `1.0.0-beta.16`, do not pass that serialized payload back to `sendTransaction()`: the send path does not broadcast the supplied bytes and can populate a different transaction. Submit signed raw transactions through a separate relay or provider. Use `sendTransaction()` with an `EvmTransaction` object when WDK should populate, sign, broadcast, and return the transaction hash.
## Estimate Transaction Fees
diff --git a/content/docs/sdk/wallet-modules/wallet-solana-gasless/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-solana-gasless/api-reference.mdx
index 2b187d49..3a60edc1 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana-gasless/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana-gasless/api-reference.mdx
@@ -7,7 +7,7 @@ icon: Code
---
-This page documents the published `@tetherto/wdk-wallet-solana-gasless@1.0.0-beta.1` type declarations.
+This page documents the published `@tetherto/wdk-wallet-solana-gasless@1.0.0-beta.2` type declarations and runtime behavior.
## Imports
@@ -131,9 +131,9 @@ new WalletAccountSolanaGasless(
| `getTokenBalance(tokenAddress)` | Returns one SPL token balance in base units. | `Promise` |
| `getTokenBalances(tokenAddresses)` | Returns multiple SPL token balances in base units. | `Promise>` |
| `getPaymasterTokenBalance()` | Returns the configured paymaster token balance in base units. | `Promise` |
-| `quoteSendTransaction(tx, config?)` | Quotes the paymaster fee for a native send or transaction message. | `Promise>` |
+| `quoteSendTransaction(tx, config?)` | Quotes an unsigned send, or decodes the embedded payment fee from a fully signed transaction. | `Promise>` |
| `signTransaction(tx, config?)` | Returns a fully signed paymaster-funded transaction without broadcasting it. | `Promise` |
-| `sendTransaction(tx, config?)` | Sends a paymaster-funded native transfer or transaction message. | `Promise` |
+| `sendTransaction(tx, config?)` | Sends an unsigned paymaster-funded transaction, or directly broadcasts a fully signed transaction through Solana RPC. | `Promise` |
| `quoteTransfer(options, config?)` | Quotes the paymaster fee for an SPL transfer. | `Promise>` |
| `transfer(options, config?)` | Transfers SPL tokens through the configured paymaster. | `Promise` |
| `getTransactionReceipt(hash)` | Reads a Solana transaction receipt by signature. | `Promise` |
@@ -165,7 +165,7 @@ signTransaction(
): Promise
```
-Signs a paymaster-funded transaction without broadcasting it. The module adds paymaster payment instructions, signs with the account owner, asks the paymaster to sign, and returns the fully signed transaction.
+Signs a paymaster-funded transaction without broadcasting it. The module adds the paymaster payment instruction, checks the quoted payment against `transactionMaxFee`, signs with the account owner, asks the paymaster to sign, and returns the fully signed transaction.
The method throws when the quoted paymaster fee is greater than `transactionMaxFee`.
@@ -173,12 +173,12 @@ The method throws when the quoted paymaster fee is greater than `transactionMaxF
```typescript
sendTransaction(
- tx: SolanaTransaction,
+ tx: SolanaTransaction | FullySignedTransaction,
config?: SolanaGaslessWalletPaymasterConfigOverrides
): Promise
```
-Sends a paymaster-funded native transfer or prebuilt transaction message.
+For an unsigned input, sends a paymaster-funded native transfer or prebuilt transaction message. For a `FullySignedTransaction` returned by `signTransaction()`, the module does not contact the paymaster: it decodes the embedded payment fee, applies `transactionMaxFee`, base64-encodes the signed wire transaction, and sends it through the configured Solana RPC with `encoding: 'base64'`.
```javascript title="Send native SOL"
const result = await account.sendTransaction({
@@ -192,7 +192,7 @@ console.log(result.hash)
console.log(result.fee)
```
-The method throws when the quoted paymaster fee is greater than `transactionMaxFee`.
+The method throws when the payment fee is greater than `transactionMaxFee`; a fee equal to the cap is allowed. A signed transaction retains its existing blockhash or durable nonce lifetime, payment instruction, and signatures. The method does not refresh or re-sign it.
#### transfer
@@ -221,12 +221,12 @@ The method throws when the quoted paymaster fee is greater than `transferMaxFee`
```typescript
quoteSendTransaction(
- tx: SolanaTransaction,
+ tx: SolanaTransaction | FullySignedTransaction,
config?: SolanaGaslessWalletPaymasterConfigOverrides
): Promise>
```
-Quotes the paymaster fee for `sendTransaction()` or `signTransaction()` inputs. Quote methods return estimates and do not enforce `transactionMaxFee`.
+For an unsigned input, requests a paymaster fee quote for `sendTransaction()` or `signTransaction()` inputs. For a `FullySignedTransaction`, it does not call the paymaster or broadcast: it locates the signed SPL token payment to the configured paymaster token account and decodes its `u64` amount. Both forms return a `bigint` fee in the configured paymaster token's base units and do not enforce `transactionMaxFee`.
#### quoteTransfer
@@ -346,7 +346,7 @@ Pass overrides as the second argument to quote, sign, send, or transfer methods.
## ConfigurationError
-The beta.1 TypeScript declarations include `ConfigurationError`, and the runtime uses that error name for missing required paymaster fields. The beta.1 JavaScript root entrypoint does not re-export the class, so JavaScript code should not import `ConfigurationError` from `@tetherto/wdk-wallet-solana-gasless`.
+The beta.2 TypeScript declarations include `ConfigurationError`, and the runtime uses that error name for missing required paymaster fields. The JavaScript root entrypoint does not re-export the runtime class, so JavaScript code should not import `ConfigurationError` from `@tetherto/wdk-wallet-solana-gasless`.
```typescript
class ConfigurationError extends Error {
diff --git a/content/docs/sdk/wallet-modules/wallet-solana-gasless/configuration.mdx b/content/docs/sdk/wallet-modules/wallet-solana-gasless/configuration.mdx
index c310c8ce..382e6fbd 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana-gasless/configuration.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana-gasless/configuration.mdx
@@ -160,7 +160,7 @@ const result = await account.transfer({
### transactionMaxFee
-Maximum allowed paymaster fee for `sendTransaction()` and `signTransaction()` calls, in the configured paymaster token's base units. These methods throw when the quoted fee is greater than this cap.
+Maximum allowed paymaster fee for `sendTransaction()` and `signTransaction()` calls, in the configured paymaster token's base units. For unsigned inputs, the module obtains the payment instruction and checks this cap before either the account or paymaster signs. For a `FullySignedTransaction` passed to `sendTransaction()`, it decodes the embedded payment amount and checks this cap before broadcasting through Solana RPC. A fee equal to the cap is allowed; only a greater fee is rejected.
**Type:** `number | bigint`
@@ -194,7 +194,9 @@ const quote = await account.quoteTransfer({
})
```
-In `1.0.0-beta.1`, overrides support `paymasterToken`, `transferMaxFee`, and `transactionMaxFee`. Use `transferMaxFee` for `transfer()` fee protection and `transactionMaxFee` for `sendTransaction()` or `signTransaction()` fee protection. `quoteTransfer()` and `quoteSendTransaction()` return estimates without enforcing either cap.
+In `1.0.0-beta.2`, overrides support `paymasterToken`, `transferMaxFee`, and `transactionMaxFee`. Use `transferMaxFee` for `transfer()` fee protection and `transactionMaxFee` for `sendTransaction()` or `signTransaction()` fee protection. `quoteTransfer()` and `quoteSendTransaction()` return estimates without enforcing either cap.
+
+For an already signed transaction, its payment token and amount are fixed in the signed message. `quoteSendTransaction(signedTransaction)` decodes that embedded payment and does not use the per-call override. On `sendTransaction(signedTransaction, { transactionMaxFee })`, only the fee cap is relevant before direct RPC broadcast; an override cannot replace the signed payment token.
## Security Considerations
diff --git a/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/get-started.mdx b/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/get-started.mdx
index 2f014b81..09fe8d2f 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/get-started.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/get-started.mdx
@@ -9,8 +9,8 @@ This guide creates a gasless Solana account, reads its address, and checks the c
## Install
-```bash title="Install @tetherto/wdk-wallet-solana-gasless beta.1"
-npm install @tetherto/wdk-wallet-solana-gasless@1.0.0-beta.1
+```bash title="Install @tetherto/wdk-wallet-solana-gasless beta.2"
+npm install @tetherto/wdk-wallet-solana-gasless@1.0.0-beta.2
```
## Create a Wallet
diff --git a/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/handle-errors.mdx b/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/handle-errors.mdx
index f4d2d654..4844568e 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/handle-errors.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/handle-errors.mdx
@@ -24,7 +24,7 @@ try {
Required paymaster fields are `paymasterUrl`, `paymasterAddress`, and `paymasterToken`.
-In `1.0.0-beta.1`, the JavaScript root entrypoint does not re-export the `ConfigurationError` class. Check `error.name` or `error.message` instead of importing the class from the package root.
+In `1.0.0-beta.2`, the published TypeScript declarations name `ConfigurationError`, but the JavaScript root entrypoint does not re-export the runtime class. Check `error.name` or `error.message` instead of importing the class from the package root.
## Handle Transaction Fee Caps
@@ -100,6 +100,31 @@ try {
Use ordered `paymasterUrl` arrays and `retries` when you need endpoint failover.
+## Handle Signed-Transaction Broadcasts
+
+`sendTransaction(signedTransaction)` bypasses the paymaster and sends the existing signed wire transaction directly through the configured Solana RPC. It still decodes the embedded payment fee and enforces `transactionMaxFee` before broadcast. Keep the original signed payload so that a timeout can be investigated without creating a second transaction:
+
+```javascript title="Broadcast one signed transaction safely"
+const signedTransaction = await account.signTransaction(transactionMessage, {
+ transactionMaxFee: 500000n
+})
+
+try {
+ const result = await account.sendTransaction(signedTransaction, {
+ transactionMaxFee: 500000n
+ })
+ console.log('Transaction submitted:', result.hash)
+} catch (error) {
+ // Do not create and sign a replacement transaction until you have checked
+ // the original transaction's status and lifetime.
+ console.error('Signed transaction was not broadcast:', error.message)
+}
+```
+
+
+The package does not refresh a signed transaction's blockhash, durable nonce, payment instruction, or signatures. Broadcast it while its existing transaction lifetime is valid, and avoid concurrent retries of the same signed payload. If the RPC outcome is uncertain, determine whether the existing transaction reached the network before deciding whether to rebuild and re-sign.
+
+
## Dispose of Sensitive Data
Call `dispose()` on owned accounts and wallet managers when private keys are no longer needed:
diff --git a/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/send-transactions.mdx
index 77e547b0..1f8f6359 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana-gasless/guides/send-transactions.mdx
@@ -5,7 +5,7 @@ docType: how-to
schemaType: TechArticle
---
-This guide explains how to send native SOL, sign without broadcasting, quote paymaster fees, and use prebuilt transaction messages.
+This guide explains how to send native SOL, sign without broadcasting, quote paymaster fees, send an already signed transaction, and use prebuilt transaction messages.
Use `BigInt` values for token and lamport amounts to avoid precision loss.
@@ -53,7 +53,7 @@ const result = await account.sendTransaction({
})
```
-`quoteSendTransaction()` returns the estimated fee without enforcing `transactionMaxFee`.
+`quoteSendTransaction()` returns the estimated fee without enforcing `transactionMaxFee`. `sendTransaction()` and `signTransaction()` reject only when the fee is greater than the cap, so a fee equal to the cap is allowed.
## Sign Without Broadcasting
@@ -70,7 +70,36 @@ const signedTransaction = await account.signTransaction({
console.log('Signed transaction:', signedTransaction)
```
-The module signs with the owner account, asks the paymaster to sign, and returns a fully signed Solana transaction. It does not broadcast from `signTransaction()`.
+The module adds the payment instruction, checks `transactionMaxFee`, signs with the owner account, asks the paymaster to sign, and returns a `FullySignedTransaction`. It does not broadcast from `signTransaction()`.
+
+## Quote and Send a Signed Transaction
+
+In `1.0.0-beta.2`, owned accounts accept the `FullySignedTransaction` returned by `signTransaction()` in both `quoteSendTransaction()` and `sendTransaction()`:
+
+```javascript title="Inspect then broadcast a signed transaction"
+const signedTransaction = await account.signTransaction({
+ to: 'Recipient1111111111111111111111111111111',
+ value: 1000000n
+}, {
+ transactionMaxFee: 500000n
+})
+
+const { fee } = await account.quoteSendTransaction(signedTransaction)
+console.log('Embedded paymaster fee:', fee)
+
+const result = await account.sendTransaction(signedTransaction, {
+ transactionMaxFee: 500000n
+})
+console.log('Transaction hash:', result.hash)
+```
+
+For a signed input, `quoteSendTransaction()` does not request a new paymaster quote or broadcast anything. It decodes the payment amount from the signed message's SPL token payment instruction; the result is a `bigint` in the configured paymaster token's base units.
+
+`sendTransaction(signedTransaction)` decodes that same fee, enforces `transactionMaxFee`, base64-encodes the fully signed wire transaction, and submits it through the configured Solana RPC with `encoding: 'base64'`. It does not call the paymaster's `signAndSendTransaction()` again.
+
+
+A signed transaction is immutable. This flow does not obtain a fresh blockhash, durable nonce, payment instruction, or paymaster signature. Submit the exact signed payload while its existing lifetime is valid, and check its original signature before retrying after an uncertain RPC result.
+
## Use a TransactionMessage
@@ -84,7 +113,7 @@ const result = await account.sendTransaction(transactionMessage)
console.log('Transaction hash:', result.hash)
```
-If the message already includes a recent blockhash or durable nonce lifetime, the module preserves it. If it does not, the module fetches the latest blockhash.
+If the message already includes a recent blockhash or durable nonce lifetime, the module preserves it. If it does not, the module adds a blockhash lifetime before it requests payment instructions and signatures. A signed result keeps that lifetime; `sendTransaction(signedTransaction)` does not refresh it.
If a prebuilt message sets `feePayer`, it must equal `paymasterAddress`. The module sets the fee payer to the paymaster address before asking the paymaster for fee instructions.
@@ -121,6 +150,8 @@ const result = await account.sendTransaction({
})
```
+For an already signed transaction, the payment token is already embedded in the signed message. Do not use `paymasterToken` as an override to try to change it; only `transactionMaxFee` is applied before direct broadcast.
+
## Next Steps
To transfer SPL tokens instead of native SOL, see [Transfer SPL Tokens](/sdk/wallet-modules/wallet-solana-gasless/guides/transfer-tokens).
diff --git a/content/docs/sdk/wallet-modules/wallet-solana-gasless/index.mdx b/content/docs/sdk/wallet-modules/wallet-solana-gasless/index.mdx
index 67a247dc..ce6153c4 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana-gasless/index.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana-gasless/index.mdx
@@ -8,7 +8,7 @@ schemaType: TechArticle
The `@tetherto/wdk-wallet-solana-gasless` module manages Solana accounts that send transactions through a Kora-compatible paymaster. It wraps the standard Solana wallet module and adds paymaster-funded native SOL sends, SPL token transfers, fee quotes, message signing, and read-only account support.
-These pages document the published `@tetherto/wdk-wallet-solana-gasless@1.0.0-beta.1` package. Test your Solana RPC and Kora-compatible paymaster configuration on the target network before production use.
+These pages document the published `@tetherto/wdk-wallet-solana-gasless@1.0.0-beta.2` package. Test your Solana RPC and Kora-compatible paymaster configuration on the target network before production use.
@@ -18,9 +18,10 @@ This module requires a Solana RPC endpoint and a Kora-compatible paymaster endpo
## Features
- **Gasless Solana Transactions**: Quote, sign, and send native SOL transfers through a paymaster.
+- **Signed Transaction Handoff**: Inspect a fully signed transaction, quote its embedded payment fee, then send it through the configured Solana RPC without contacting the paymaster again.
- **SPL Token Transfers**: Transfer SPL tokens and create the recipient associated token account when needed.
- **Paymaster Fee Quotes**: Estimate paymaster token fees before sending.
-- **Per-Operation Overrides**: Override the paymaster token for one quote, sign, send, or transfer call; use `transactionMaxFee` for send/sign caps and `transferMaxFee` for transfer caps.
+- **Per-Operation Overrides**: Override the paymaster token for unsigned quote, sign, send, or transfer calls; use `transactionMaxFee` for send/sign caps and `transferMaxFee` for transfer caps.
- **SLIP-0010 Derivation Paths**: Use the same Solana derivation path behavior as `@tetherto/wdk-wallet-solana`.
- **Read-Only Accounts**: Check balances, quote fees, read receipts, and verify signatures for an address without a private key.
- **Message Signing**: Sign and verify messages with Ed25519 account keys.
diff --git a/content/docs/sdk/wallet-modules/wallet-solana/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-solana/api-reference.mdx
index da49681b..8549722d 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana/api-reference.mdx
@@ -129,8 +129,8 @@ In v1.0.0-beta.9 the constructor was made public. The static factory method `Wal
| `sign(message)` | Signs a message using the account's private key | `Promise` |
| `signTransaction(tx)` | Signs a Solana transaction without broadcasting it | `Promise` |
| `verify(message, signature)` | Verifies a message signature | `Promise` |
-| `sendTransaction(tx)` | Sends a Solana transaction | `Promise<{hash: string, fee: bigint}>` |
-| `quoteSendTransaction(tx)` | Estimates the fee for a transaction | `Promise<{fee: bigint}>` |
+| `sendTransaction(tx)` | Builds and sends a transaction, or broadcasts a fully signed transaction | `Promise<{hash: string, fee: bigint}>` |
+| `quoteSendTransaction(tx)` | Estimates the fee for a transaction or fully signed transaction | `Promise<{fee: bigint}>` |
| `transfer(options)` | Transfers SPL tokens to another address | `Promise<{hash: string, fee: bigint}>` |
| `quoteTransfer(options)` | Estimates the fee for an SPL token transfer | `Promise<{fee: bigint}>` |
| `getBalance()` | Returns the native SOL balance (in lamports) | `Promise` |
@@ -205,12 +205,14 @@ console.log('Signature valid:', isValid)
Sends a Solana transaction.
**Parameters:**
-- `tx` (SolanaTransaction): A simple transfer object or a prebuilt `TransactionMessage`
+- `tx` (SolanaTransaction | FullySignedTransaction): A simple transfer object, prebuilt `TransactionMessage`, or fully signed transaction
- `to` (string): Recipient's Solana address (base58-encoded)
- `value` (number | bigint): Amount in lamports
When `tx` is a `TransactionMessage`, WDK preserves an existing recent blockhash or durable nonce lifetime. If no lifetime is present, WDK fetches the latest blockhash before quoting or sending. If you set an explicit `feePayer`, it must match the wallet address.
+When `tx` is a `FullySignedTransaction`, WDK quotes it again, enforces `transactionMaxFee`, and broadcasts its exact wire bytes. It does not refresh the recent blockhash or durable nonce and does not re-sign the transaction.
+
**Returns:** `Promise<{hash: string, fee: bigint}>` - Object containing transaction hash and fee (in lamports)
**Throws:** Error if wallet is not connected to a provider or if the estimated fee is greater than `transactionMaxFee`
@@ -228,7 +230,7 @@ console.log('Transaction fee:', result.fee, 'lamports')
Estimates the fee for a Solana transaction.
**Parameters:**
-- `tx` (SolanaTransaction): The transaction object (same as `sendTransaction`)
+- `tx` (SolanaTransaction | FullySignedTransaction): A transaction input or fully signed transaction (same forms as `sendTransaction`)
**Returns:** `Promise<{fee: bigint}>` - Object containing fee estimate (in lamports)
@@ -515,6 +517,16 @@ interface SolanaWalletConfig {
}
```
+### FullySignedTransaction
+
+`signTransaction()` returns the `FullySignedTransaction` type defined by `@solana/transactions`. The WDK package does not re-export this type; import it from the Solana package when an explicit annotation is needed.
+
+```typescript
+import type { FullySignedTransaction } from '@solana/transactions'
+```
+
+The signed value contains the compiled message bytes and all required signatures. Its transaction lifetime is sealed at signing time.
+
### TransferOptions
```typescript
diff --git a/content/docs/sdk/wallet-modules/wallet-solana/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-solana/guides/send-transactions.mdx
index 98592994..dd227f61 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana/guides/send-transactions.mdx
@@ -5,7 +5,7 @@ docType: how-to
schemaType: TechArticle
---
-This guide explains how to [send native SOL](#send-native-sol), [sign a transaction without broadcasting](#sign-a-transaction-without-broadcasting), [estimate transaction fees](#estimate-transaction-fees), [cap native transaction fees](#cap-native-transaction-fees), [quote or send a TransactionMessage](#quote-or-send-a-transactionmessage), [use dynamic fee rates](#use-dynamic-fee-rates), and [run a complete SOL transfer flow](#complete-example).
+This guide explains how to [send native SOL](#send-native-sol), [sign a transaction without broadcasting](#sign-a-transaction-without-broadcasting), [quote and send a signed transaction](#quote-and-send-a-signed-transaction), [estimate transaction fees](#estimate-transaction-fees), [cap native transaction fees](#cap-native-transaction-fees), [quote or send a TransactionMessage](#quote-or-send-a-transactionmessage), [use dynamic fee rates](#use-dynamic-fee-rates), and [run a complete SOL transfer flow](#complete-example).
**BigInt Usage:** Always use `BigInt` (the `n` suffix) for monetary values to avoid precision loss with large numbers.
@@ -40,6 +40,27 @@ const signedTransaction = await account.signTransaction({
console.log('Signed transaction:', signedTransaction)
```
+## Quote and Send a Signed Transaction
+
+Pass the `FullySignedTransaction` returned by `signTransaction()` to the quote and send methods when review and submission are separate steps.
+
+```javascript title="Quote and Send Signed Bytes"
+const signedTransaction = await account.signTransaction({
+ to: '11111111111111111111111111111112',
+ value: 1000000000n
+})
+
+const quote = await account.quoteSendTransaction(signedTransaction)
+console.log('Estimated fee:', quote.fee, 'lamports')
+
+const result = await account.sendTransaction(signedTransaction)
+console.log('Transaction signature:', result.hash)
+```
+
+
+Signing seals the recent blockhash or durable nonce into the message. WDK broadcasts the signed bytes unchanged and does not refresh the transaction lifetime or re-sign it. Submit the transaction before that lifetime becomes invalid. `sendTransaction()` quotes it again and enforces `transactionMaxFee`.
+
+
## Estimate Transaction Fees
Use [`account.quoteSendTransaction()`](/sdk/wallet-modules/wallet-solana/api-reference#quotesendtransactiontx) to get a fee estimate before sending.
diff --git a/content/docs/sdk/wallet-modules/wallet-solana/index.mdx b/content/docs/sdk/wallet-modules/wallet-solana/index.mdx
index 7f909faa..c721af37 100644
--- a/content/docs/sdk/wallet-modules/wallet-solana/index.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-solana/index.mdx
@@ -28,6 +28,7 @@ Use [`getAccountByPath`](/sdk/wallet-modules/wallet-solana/api-reference#getacco
- **Solana Address Support**: Generate and manage Solana public keys and addresses
- **Message Signing**: Sign and verify messages using Ed25519 cryptography
- **Transaction Management**: Sign, send, and quote Solana transactions
+- **Signed Transaction Relay**: Quote and broadcast the exact `FullySignedTransaction` returned by `signTransaction()`
- **SPL Token Support**: Query native SOL plus single or batch SPL token balances
- **TypeScript Support**: Full TypeScript definitions included
- **Memory Safety**: Secure private key management with memory-safe implementation
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/api-reference.mdx
index f9f6e043..53141d3f 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/api-reference.mdx
@@ -28,13 +28,13 @@ new WalletManagerTonGasless(seed, config)
- `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes
- `config` (TonGaslessWalletConfig): Configuration object
- `tonClient` (object | TonClient | array): TON client configuration, instance, or an array of configurations or instances for failover
- - `url` (string): TON Center API URL (e.g., 'https://toncenter.com/api/v3')
+ - `url` (string): TON Center v2 JSON-RPC URL (e.g., 'https://toncenter.com/api/v2/jsonRPC')
- `secretKey` (string, optional): API key for TON Center
- `tonApiClient` (object | TonApiClient | array): TON API client configuration, instance, or an array of configurations or instances for failover
- - `url` (string): TON API URL (e.g., 'https://tonapi.io/v2')
+ - `url` (string): TON API base URL (e.g., 'https://tonapi.io')
- `secretKey` (string, optional): API key for TON API
- `paymasterToken` (object): Paymaster token configuration
- - `address` (string): Paymaster Jetton master contract address
+ - `address` (string): Supported paymaster Jetton master contract address
- `retries` (number, optional): Failover retries used when `tonClient` and `tonApiClient` are arrays (default: 3)
- `transferMaxFee` (number | bigint, optional): Maximum fee for gasless transfer operations
- `transactionMaxFee` (number | bigint, optional): Shared wallet config option; native `sendTransaction()`, `quoteSendTransaction()`, and `signTransaction()` are unsupported on this module
@@ -43,11 +43,11 @@ new WalletManagerTonGasless(seed, config)
```javascript
const wallet = new WalletManagerTonGasless(seedPhrase, {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key'
},
tonApiClient: {
- url: 'https://tonapi.io/v2',
+ url: 'https://tonapi.io',
secretKey: 'your-tonapi-key'
},
paymasterToken: {
@@ -64,8 +64,8 @@ const wallet = new WalletManagerTonGasless(seedPhrase, {
|--------|-------------|---------|
| `getAccount(index)` | Returns a gasless wallet account at the specified index | `Promise\` |
| `getAccountByPath(path)` | Returns a gasless wallet account at the specified BIP-44 derivation path | `Promise\` |
-| `getFeeRates()` | Returns current fee rates for transactions | `Promise\<{normal: number, fast: number}\>` |
-| `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` |
+| `getFeeRates()` | Returns fee rates from the mainnet TON API configuration | `Promise\<{normal: bigint, fast: bigint}\>` |
+| `dispose()` | Disposes cached accounts and signers; the manager seed remains in memory | `void` |
##### `getAccount(index)`
Returns a gasless wallet account at the specified index. Index `n` derives the account at `m/44'/607'/n'`.
@@ -96,9 +96,9 @@ const account = await wallet.getAccountByPath("1'")
```
##### `getFeeRates()`
-Returns current fee rates for transactions based on blockchain config.
+Returns fee rates from the mainnet TON API configuration. Through `1.0.0-beta.8`, this method always requests `https://tonapi.io/v2`, does not follow the configured client network, and returns the same calculated value for both fields.
-**Returns:** `Promise\<{normal: number, fast: number}\>` - Object containing fee rates
+**Returns:** `Promise\<{normal: bigint, fast: bigint}\>` - Object containing fee rates
**Example:**
```javascript
@@ -108,7 +108,7 @@ console.log('Fast fee rate:', feeRates.fast)
```
##### `dispose()`
-Disposes all wallet accounts, clearing private keys from memory.
+Disposes cached wallet accounts and signers, clearing their derived private keys. In the current beta, this method does not zero or unset the wallet manager's seed bytes.
**Example:**
```javascript
@@ -140,8 +140,8 @@ new WalletAccountTonGasless(seed, path, config)
| `sendTransaction(tx)` | Not supported on gasless; always rejects | `Promise\` |
| `quoteSendTransaction(tx)` | Not supported on gasless; always rejects | `Promise\\>` |
| `verify(message, signature)` | Verifies a message signature | `Promise\` |
-| `transfer(options, config?)` | Transfers tokens using gasless transactions | `Promise\<{hash: string, fee: number}\>` |
-| `quoteTransfer(options, config?)` | Estimates the fee for a token transfer | `Promise\<{fee: number}\>` |
+| `transfer(options, config?)` | Transfers tokens using gasless transactions | `Promise\<{hash: string, fee: bigint}\>` |
+| `quoteTransfer(options, config?)` | Estimates the fee for a token transfer | `Promise\<{fee: bigint}\>` |
| `getBalance()` | Returns the native TON balance (in nanotons) | `Promise\` |
| `getTokenBalance(tokenAddress)` | Returns the balance of a specific token | `Promise\` |
| `getPaymasterTokenBalance()` | Returns the balance of the paymaster token | `Promise\` |
@@ -174,7 +174,7 @@ console.log('Signature:', signature)
```
##### `signTransaction(tx)`
-Not supported on the gasless module. This method always throws. The gasless module only supports paymaster-funded Jetton transfers through [`transfer()`](#transferoptions-config). Use the standard `@tetherto/wdk-wallet-ton` module for offline transaction signing.
+Not supported on the gasless module. This method always throws. The gasless module only supports paymaster-funded Jetton transfers through [`transfer()`](#transferoptions-config). Use the standard `@tetherto/wdk-wallet-ton` module to build signed native transaction bodies.
**Parameters:**
- `tx` (TonTransaction): The transaction
@@ -202,7 +202,7 @@ await account.sendTransaction({ to: 'EQ...', value: 1000000000n })
```
##### `quoteSendTransaction(tx)`
-Not supported on the gasless module. This method is inherited for wallet-interface compatibility and always rejects at runtime. Use [`quoteTransfer()`](#quotetransferoptions) to estimate gasless Jetton transfer fees.
+Not supported on the gasless module. This method is inherited for wallet-interface compatibility and always rejects at runtime. Use [`quoteTransfer()`](#quotetransferoptions-config) to estimate gasless Jetton transfer fees.
**Parameters:**
- `tx` (TonTransaction): The transaction
@@ -237,13 +237,13 @@ Transfers a Jetton using a gasless transaction, paying the fee with the configur
- `options` (TransferOptions): Transfer options
- `token` (string): Token contract address
- `recipient` (string): Recipient TON address
- - `amount` (number): Amount in token's base units
-- `config` (object, optional): Override configuration
- - `paymasterToken` (object, optional): Override default paymaster token
+ - `amount` (number | bigint): Amount in token's base units
+- `config` (object, optional): Per-call configuration. When supplied, it replaces the wallet-level transfer configuration for this call.
+ - `paymasterToken` (object, required when `config` is supplied): Paymaster token for this transfer
- `address` (string): Paymaster token address
- `transferMaxFee` (number | bigint, optional): Override maximum fee. Transfers throw only when the estimated fee is greater than this cap, so an equal estimate is allowed.
-**Returns:** `Promise\<{hash: string, fee: number}\>` - Transfer result
+**Returns:** `Promise\<{hash: string, fee: bigint}\>` - The signed transfer body hash as lowercase hex and the fee in paymaster Jetton base units
**Example:**
```javascript
@@ -257,18 +257,21 @@ const result = await account.transfer({
})
```
-##### `quoteTransfer(options)`
+##### `quoteTransfer(options, config?)`
Estimates the fee for a Jetton (TON token) transfer.
**Parameters:**
- `options` (TransferOptions): Transfer options
- `token` (string): Token contract address
- `recipient` (string): Recipient TON address
- - `amount` (number): Amount in token's base units
-- `config` (object, optional): Override configuration
- - `paymasterToken` (object, optional): Override default paymaster token
+ - `amount` (number | bigint): Amount in token's base units
+- `config` (object, optional): Per-call configuration. When supplied, it replaces the wallet-level quote configuration for this call.
+ - `paymasterToken` (object, required when `config` is supplied): Paymaster token for this quote
+ - `address` (string): Paymaster token address
-**Returns:** `Promise\<{fee: number}\>` - Object containing fee estimate (in paymaster token base units)
+**Returns:** `Promise\<{fee: bigint}\>` - Object containing fee estimate (in paymaster token base units)
+
+`quoteTransfer()` returns a fee estimate only. It does not submit a transfer or return a transaction hash.
**Example:**
```javascript
@@ -362,8 +365,8 @@ new WalletAccountReadOnlyTonGasless(publicKey, config)
```
**Parameters:**
-- `publicKey` (string | Uint8Array): The account's public key
-- `config` (TonGaslessWalletConfig): Configuration object
+- `publicKey` (string | Uint8Array): The account's public key. String values must be hex encoded.
+- `config` (object): Client, retry, and paymaster configuration. `transferMaxFee` and `transactionMaxFee` are not accepted by the read-only constructor.
#### Methods
@@ -374,7 +377,7 @@ new WalletAccountReadOnlyTonGasless(publicKey, config)
| `getTokenBalance(tokenAddress)` | Returns the balance of a specific token | `Promise\` |
| `getPaymasterTokenBalance()` | Returns the balance of the paymaster token | `Promise\` |
| `quoteSendTransaction(tx)` | Not supported on gasless; always rejects | `Promise\\>` |
-| `quoteTransfer(options, config?)` | Estimates the fee for a token transfer | `Promise\<{fee: number}\>` |
+| `quoteTransfer(options, config?)` | Estimates the fee for a token transfer | `Promise\<{fee: bigint}\>` |
| `verify(message, signature)` | Verifies a message signature | `Promise\` |
| `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise\` |
@@ -446,11 +449,14 @@ Estimates the fee for a token transfer.
- `options` (TransferOptions): Transfer options
- `token` (string): Token contract address
- `recipient` (string): Recipient TON address
- - `amount` (number): Amount in token's base units
-- `config` (object, optional): Override configuration
- - `paymasterToken` (object, optional): Override default paymaster token
+ - `amount` (number | bigint): Amount in token's base units
+- `config` (object, optional): Per-call configuration. When supplied, it replaces the account's wallet-level quote configuration for this call.
+ - `paymasterToken` (object, required when `config` is supplied): Paymaster token for this quote
+ - `address` (string): Paymaster token address
+
+**Returns:** `Promise\<{fee: bigint}\>` - Object containing fee estimate (in paymaster token base units)
-**Returns:** `Promise\<{fee: number}\>` - Object containing fee estimate (in paymaster token base units)
+`quoteTransfer()` returns a fee estimate only. It does not submit a transfer or return a transaction hash.
**Example:**
```javascript
@@ -480,8 +486,10 @@ console.log('Signature valid:', isValid)
##### `getTransactionReceipt(hash)`
Returns a transaction's receipt.
+Through `1.0.0-beta.8`, the inherited initial receipt lookup always queries mainnet TON Center v3. Do not rely on this method for testnet receipts; see [Network Selection](/sdk/wallet-modules/wallet-ton-gasless/configuration#network-selection).
+
**Parameters:**
-- `hash` (string): The transaction's hash
+- `hash` (string): The signed transfer body hash returned by `transfer()`
**Returns:** `Promise\` - The receipt, or null if the transaction has not been included in a block yet
@@ -501,14 +509,14 @@ if (receipt) {
```typescript
type TonClientConfig = {
- /** TON Center API URL @example 'https://toncenter.com/api/v3' */
+ /** TON Center v2 JSON-RPC URL @example 'https://toncenter.com/api/v2/jsonRPC' */
url: string;
/** Optional API key for TON Center */
secretKey?: string;
};
type TonApiClientConfig = {
- /** TON API URL @example 'https://tonapi.io/v2' */
+ /** TON API base URL @example 'https://tonapi.io' */
url: string;
/** Optional API key for TON API */
secretKey?: string;
@@ -575,7 +583,7 @@ interface TransferOptions {
/**
* Amount in token's base units
*/
- amount: number;
+ amount: number | bigint;
}
```
@@ -584,14 +592,15 @@ interface TransferOptions {
```typescript
interface TransferResult {
/**
- * Transaction hash
+ * Signed transfer body hash as a lowercase hex string; pass it to getTransactionReceipt()
+ * @example '7f83b1657ff1fc53b92dc18148a1d65dfa13501404a55e63ddfde593f4f5f9d8'
*/
hash: string;
/**
* Fee paid in paymaster token units
*/
- fee: number;
+ fee: bigint;
}
```
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/configuration.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/configuration.mdx
index 27273c4c..4758e179 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/configuration.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/configuration.mdx
@@ -14,11 +14,11 @@ import WalletManagerTonGasless from '@tetherto/wdk-wallet-ton-gasless'
const config = {
// Required parameters
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
},
tonApiClient: {
- url: 'https://tonapi.io/v2',
+ url: 'https://tonapi.io',
secretKey: 'your-ton-api-key' // Optional
},
paymasterToken: {
@@ -33,6 +33,10 @@ const config = {
const wallet = new WalletManagerTonGasless(seedPhrase, config)
```
+
+`tonClient.url` must be a TON Center v2 JSON-RPC endpoint because the module passes it to `@ton/ton`'s `TonClient`. Set `tonApiClient.url` to the TON API base URL without `/v2`; the generated TON API client appends `/v2/gasless/...` to the base URL.
+
+
## Account Configuration
```javascript
@@ -41,11 +45,11 @@ import { WalletAccountTonGasless } from '@tetherto/wdk-wallet-ton-gasless'
const accountConfig = {
// Required parameters
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
},
tonApiClient: {
- url: 'https://tonapi.io/v2',
+ url: 'https://tonapi.io',
secretKey: 'your-ton-api-key' // Optional
},
paymasterToken: {
@@ -64,14 +68,14 @@ const account = new WalletAccountTonGasless(seedPhrase, "0'", accountConfig)
### tonClient
-The `tonClient` option configures the TON Center API client for blockchain interactions. You can pass a single configuration object, a `TonClient` instance, or an array of configurations or instances to enable failover.
+The `tonClient` option configures the TON Center v2 JSON-RPC client for blockchain interactions. You can pass a single configuration object, a `TonClient` instance, or an array of configurations or instances to enable failover.
**Type:**
```typescript
type TonClientConfig = {
/**
- * TON Center API endpoint URL
- * @example 'https://toncenter.com/api/v3'
+ * TON Center v2 JSON-RPC endpoint URL
+ * @example 'https://toncenter.com/api/v2/jsonRPC'
*/
url: string;
@@ -88,18 +92,18 @@ type TonClient = TonClientConfig | TonClientInstance | Array
+Do not include `/v2` in `tonApiClient.url`. Use `https://tonapi.io`, not `https://tonapi.io/v2`; otherwise the generated client requests `/v2/v2/gasless/...`.
+
+
### paymasterToken
-The `paymasterToken` option specifies the Jetton used to pay gasless transfer fees instead of native TON.
+The `paymasterToken` option specifies the Jetton used to pay gasless transfer fees instead of native TON. Its address must match a `gas_jettons[].master_id` value returned by the configured TON API service's raw `/v2/gasless/config` endpoint. The generated `@ton-api/client` exposes the same values as `gasJettons[].masterId`.
**Type:**
```typescript
@@ -129,6 +137,10 @@ type PaymasterToken = {
**Required:** Yes
+
+`paymasterToken` must be an object with an `address` field, not a raw address string.
+
+
**Example:**
```javascript
const config = {
@@ -150,12 +162,12 @@ The `retries` option sets the number of additional failover attempts after the i
```javascript
const config = {
tonClient: [
- { url: 'https://toncenter.com/api/v3' },
- { url: 'https://backup.toncenter.com/api/v3', secretKey: 'your-api-key' }
+ { url: 'https://toncenter.com/api/v2/jsonRPC' },
+ { url: 'https://your-secondary-toncenter.example/api/v2/jsonRPC', secretKey: 'your-secondary-api-key' } // Replace with a real independent provider
],
tonApiClient: [
- { url: 'https://tonapi.io/v2' },
- { url: 'https://backup.tonapi.io/v2' }
+ { url: 'https://tonapi.io' },
+ { url: 'https://your-secondary-tonapi.example' } // Replace with a real independent provider
],
retries: 3
}
@@ -192,14 +204,14 @@ Here's a complete configuration example with required clients, failover, and gas
const config = {
// TON Client (Required) - array enables failover
tonClient: [
- { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' },
- { url: 'https://backup.toncenter.com/api/v3' }
+ { url: 'https://toncenter.com/api/v2/jsonRPC', secretKey: 'your-api-key' },
+ { url: 'https://your-secondary-toncenter.example/api/v2/jsonRPC' } // Replace with a real independent provider
],
// TON API Client (Required) - array enables failover
tonApiClient: [
- { url: 'https://tonapi.io/v2', secretKey: 'your-ton-api-key' },
- { url: 'https://backup.tonapi.io/v2' }
+ { url: 'https://tonapi.io', secretKey: 'your-ton-api-key' },
+ { url: 'https://your-secondary-tonapi.example' } // Replace with a real independent provider
],
// Paymaster Token (Required)
@@ -215,6 +227,19 @@ const config = {
}
```
+## Network Selection
+
+Use TON Center and TON API endpoints for the same network:
+
+- Mainnet: `https://toncenter.com/api/v2/jsonRPC` and `https://tonapi.io`
+- Testnet: `https://testnet.toncenter.com/api/v2/jsonRPC` and `https://testnet.tonapi.io`
+
+Do not mix mainnet and testnet clients in one wallet configuration or failover list.
+
+
+Through `@tetherto/wdk-wallet-ton-gasless` `1.0.0-beta.8`, the inherited `getTransactionReceipt()` implementation starts its lookup against a hard-coded mainnet TON Center v3 endpoint. `getFeeRates()` likewise always reads mainnet TON API configuration. Do not rely on these methods for testnet-specific receipts or fee rates.
+
+
The default derivation path changed in `1.0.0-beta.5`. Accounts now derive at `m/44'/607'/{index}'` to align with `@tetherto/wdk-wallet-ton`. Wallets created with `1.0.0-beta.4` or earlier derived `getAccount(index)` at `m/44'/607'/0'/0/{index}`, so the same seed produces different addresses after upgrading. Migrate existing accounts using [`getAccountByPath()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getaccountbypathpath) with the old path if you need to keep prior addresses.
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/check-balances.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/check-balances.mdx
index b906bf4b..5a244413 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/check-balances.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/check-balances.mdx
@@ -36,7 +36,7 @@ console.log('Paymaster Jetton balance:', paymasterBalance)
```
-The paymaster token balance determines how many gasless transfers you can execute. Ensure the paymaster has sufficient token balance before initiating gasless transfers.
+The account pays gasless fees from its balance of the configured paymaster Jetton. If that is also the Jetton being transferred, the same balance must cover both the transfer amount and the final fee.
## Read-Only Account Balances
@@ -48,11 +48,11 @@ import { WalletAccountReadOnlyTonGasless } from '@tetherto/wdk-wallet-ton-gasles
const readOnlyAccount = new WalletAccountReadOnlyTonGasless(publicKey, {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
},
tonApiClient: {
- url: 'https://tonapi.io/v2',
+ url: 'https://tonapi.io',
secretKey: 'your-ton-api-key' // Optional
},
paymasterToken: {
@@ -73,4 +73,4 @@ console.log('Paymaster token balance:', paymasterBalance)
## Next Steps
-With balance checks in place, learn how to [send TON](/sdk/wallet-modules/wallet-ton-gasless/guides/send-transactions).
+With balance checks in place, learn how to [transfer Jetton tokens gaslessly](/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens).
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/get-started.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/get-started.mdx
index 0473db1c..efbf7d27 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/get-started.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/get-started.mdx
@@ -28,15 +28,16 @@ import WalletManagerTonGasless, {
WalletAccountReadOnlyTonGasless
} from '@tetherto/wdk-wallet-ton-gasless'
-const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
+const seedPhrase = process.env.WDK_SEED_PHRASE
+if (!seedPhrase) throw new Error('WDK_SEED_PHRASE is required')
const wallet = new WalletManagerTonGasless(seedPhrase, {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
},
tonApiClient: {
- url: 'https://tonapi.io/v2',
+ url: 'https://tonapi.io',
secretKey: 'your-ton-api-key' // Optional
},
paymasterToken: {
@@ -47,7 +48,7 @@ const wallet = new WalletManagerTonGasless(seedPhrase, {
```
-**Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds.
+**Secure the Seed Phrase:** Load seed phrases from secure storage; never hardcode or log them. This server-side example uses an environment variable. If the seed phrase is lost, the user will permanently lose access to their funds.
## 3. Get Your First Account
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/handle-errors.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/handle-errors.mdx
index fdbdbeaa..add5c68f 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/handle-errors.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/handle-errors.mdx
@@ -1,11 +1,11 @@
---
title: Handle Errors
-description: Handle errors, manage fees, and dispose of sensitive data in gasless TON wallets.
+description: Handle errors, manage fees, and clean up derived account keys in gasless TON wallets.
docType: how-to
schemaType: TechArticle
---
-This guide covers how to [handle gasless transfer errors](#handle-gasless-transfer-errors) and [handle unsupported method errors](#handle-unsupported-method-errors), plus [best practices](#best-practices) for fee management and memory cleanup.
+This guide covers how to [handle gasless transfer errors](#handle-gasless-transfer-errors) and [handle unsupported method errors](#handle-unsupported-method-errors), plus [best practices](#best-practices) for fee management and derived key cleanup.
## Handle Gasless Transfer Errors
@@ -18,7 +18,7 @@ try {
recipient: 'EQ...',
amount: 1000000000
})
- console.log('Transfer hash:', result.hash)
+ console.log('Signed transfer body hash:', result.hash)
} catch (error) {
if (error.message.includes('insufficient jetton balance')) {
console.error('Please add more Jetton tokens to your wallet')
@@ -26,7 +26,7 @@ try {
console.error('Please add more paymaster tokens for gas fees')
} else if (error.message.includes('invalid address')) {
console.error('The recipient address is invalid')
- } else if (error.message.toLowerCase().includes('fee')) {
+ } else if (error.message === 'The transfer operation exceeds the transfer max fee.') {
console.error('The transfer fee exceeds your configured maximum')
} else {
console.error('Transfer failed:', error.message)
@@ -54,7 +54,7 @@ try {
### Manage Fee Limits
-Set `transferMaxFee` when creating the wallet to prevent gasless transfers from exceeding a maximum cost. Fee caps reject estimates greater than the configured limit, so an estimate equal to the cap is allowed. Native `sendTransaction()`, `quoteSendTransaction()`, and `signTransaction()` are unsupported on this module, so `transactionMaxFee` does not cap gasless transfers. You can retrieve current network rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getfeerates):
+Set `transferMaxFee` when creating the wallet to prevent gasless transfers from exceeding a maximum cost. Fee caps reject estimates greater than the configured limit, so an estimate equal to the cap is allowed. Native `sendTransaction()`, `quoteSendTransaction()`, and `signTransaction()` are unsupported on this module, so `transactionMaxFee` does not cap gasless transfers. You can retrieve mainnet TON API rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getfeerates). Through `1.0.0-beta.8`, this method does not follow configured testnet clients and returns the same calculated value for `normal` and `fast`:
```javascript title="Fee Management"
const feeRates = await wallet.getFeeRates()
@@ -62,9 +62,9 @@ console.log('Normal fee rate:', feeRates.normal, 'nanotons')
console.log('Fast fee rate:', feeRates.fast, 'nanotons')
```
-### Dispose of Sensitive Data
+### Dispose Derived Account Keys
-Call [`dispose()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#dispose-1) on accounts and wallet managers to clear private keys and sensitive data from memory when they are no longer needed:
+Call [`dispose()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#dispose-1) on accounts and wallet managers to clear cached accounts' derived private keys when they are no longer needed:
```javascript title="Memory Cleanup"
account.dispose()
@@ -73,5 +73,5 @@ wallet.dispose()
```
-Always call [`dispose()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#dispose-1) in a `finally` block or cleanup handler to ensure sensitive data is cleared even if an error occurs.
+Call [`dispose()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#dispose-1) in a `finally` block or cleanup handler so derived account keys are cleared even if an error occurs. In the current beta, `wallet.dispose()` does not zero or unset `wallet.seed`; manage the seed lifecycle separately and release all manager references when finished.
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/send-transactions.mdx
index fca1d4e0..1671376d 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/send-transactions.mdx
@@ -1,11 +1,11 @@
---
-title: Send Transactions
+title: Native Sends Unsupported
description: Why native TON sends are unsupported on the gasless module and what to use instead.
docType: how-to
schemaType: TechArticle
---
-This guide explains why [native TON sends are not supported](#native-ton-sends-are-not-supported) on the gasless module and how to [check current fee rates](#check-current-fee-rates).
+This guide explains why [native TON sends are not supported](#native-ton-sends-are-not-supported) on the gasless module and how to [check mainnet fee rates](#check-mainnet-fee-rates).
## Native TON Sends Are Not Supported
@@ -23,9 +23,9 @@ await account.sendTransaction({
To send native TON, use the standard `@tetherto/wdk-wallet-ton` module. For gasless Jetton transfers, see [Transfer Jetton Tokens](/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens).
-## Check Current Fee Rates
+## Check Mainnet Fee Rates
-You can retrieve current fee rates from the wallet manager using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getfeerates):
+You can retrieve mainnet TON API fee rates from the wallet manager using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getfeerates). Through `1.0.0-beta.8`, this method does not follow configured testnet clients and returns the same calculated value for `normal` and `fast`:
```javascript title="Get Fee Rates"
const feeRates = await wallet.getFeeRates()
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/sign-verify-messages.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/sign-verify-messages.mdx
index a7c406cc..4af049b4 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/sign-verify-messages.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/sign-verify-messages.mdx
@@ -33,11 +33,11 @@ import { WalletAccountReadOnlyTonGasless } from '@tetherto/wdk-wallet-ton-gasles
const readOnlyAccount = new WalletAccountReadOnlyTonGasless(publicKey, {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key'
},
tonApiClient: {
- url: 'https://tonapi.io/v2',
+ url: 'https://tonapi.io',
secretKey: 'your-ton-api-key'
},
paymasterToken: {
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens.mdx
index 32d976f1..231a7a0c 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/guides/transfer-tokens.mdx
@@ -5,7 +5,7 @@ docType: how-to
schemaType: TechArticle
---
-This guide explains how to [transfer Jetton tokens gaslessly](#transfer-tokens-gasless), [override paymaster configuration](#override-paymaster-configuration), [estimate transfer fees](#estimate-transfer-fees), and [validate inputs before executing](#transfer-with-validation).
+This guide explains how to [transfer Jetton tokens gaslessly](#transfer-tokens-gasless), [override paymaster configuration](#override-paymaster-configuration), [estimate transfer fees](#estimate-transfer-fees), and [run preflight checks](#preflight-transfer-checks).
## Transfer Tokens (Gasless)
@@ -17,7 +17,7 @@ const result = await account.transfer({
recipient: 'EQ...', // Recipient's TON address
amount: 1000000000 // Amount in Jetton's base units
})
-console.log('Transfer hash:', result.hash)
+console.log('Signed transfer body hash:', result.hash)
console.log('Transfer fee:', result.fee, 'paymaster token units')
```
@@ -36,7 +36,7 @@ const result = await account.transfer({
},
transferMaxFee: 2000000000 // Override maximum allowed fee
})
-console.log('Transfer hash:', result.hash)
+console.log('Signed transfer body hash:', result.hash)
console.log('Transfer fee:', result.fee, 'paymaster token units')
```
@@ -44,7 +44,7 @@ console.log('Transfer fee:', result.fee, 'paymaster token units')
## Estimate Transfer Fees
-You can get a fee estimate before executing the transfer using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#quotetransferoptions):
+You can get a fee estimate before executing the transfer using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#quotetransferoptions-config):
```javascript title="Quote Gasless Transfer"
const quote = await account.quoteTransfer({
@@ -55,46 +55,70 @@ const quote = await account.quoteTransfer({
console.log('Transfer fee estimate:', quote.fee, 'paymaster token units')
```
-## Transfer with Validation
+## Preflight Transfer Checks
-Validate balances before transferring:
+Inspect balances and fees before transferring:
1. Use [`account.getTokenBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#gettokenbalancetokenaddress) to check Jetton balance.
-2. Use [`account.getPaymasterTokenBalance()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#getpaymastertokenbalance) to verify sufficient paymaster funds.
-3. Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#quotetransferoptions) to estimate fees.
-4. Execute the transfer with [`account.transfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#transferoptions-config):
+2. Use [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#quotetransferoptions-config) with the intended paymaster to estimate the fee.
+3. Query that paymaster Jetton explicitly with `getTokenBalance(paymasterJettonAddress)`. `getPaymasterTokenBalance()` always reads the wallet-level paymaster, so do not use it to preflight a per-call override.
+4. If the transferred Jetton is also the paymaster Jetton, require one balance to cover the transfer amount plus the fee. Compare parsed TON addresses because different string encodings can identify the same Jetton master.
+5. Reject a quote above your application's fee policy, then execute [`account.transfer()`](/sdk/wallet-modules/wallet-ton-gasless/api-reference#transferoptions-config) with `transferMaxFee` set to that quote. The method obtains a fresh estimate and aborts before relay if it has risen. This explicit policy check matters because a per-call configuration replaces, rather than merges with, the wallet-level transfer configuration:
-```javascript title="Validated Gasless Transfer"
-async function transferWithValidation(account, jettonAddress, recipient, amount) {
+This example imports `Address` from `@ton/ton` for canonical address comparison. Add `@ton/ton` as a direct dependency in your application before using it.
+
+```javascript title="Gasless Transfer with Preflight Checks"
+import { Address } from '@ton/ton'
+
+async function transferWithChecks(account, jettonAddress, paymasterJettonAddress, recipient, amount, maxFee) {
if (typeof jettonAddress !== 'string' || jettonAddress.length === 0) {
throw new Error('Invalid Jetton address format')
}
+ if (typeof paymasterJettonAddress !== 'string' || paymasterJettonAddress.length === 0) {
+ throw new Error('Invalid paymaster Jetton address format')
+ }
+
if (typeof recipient !== 'string' || recipient.length === 0) {
throw new Error('Invalid recipient address format')
}
- const balance = await account.getTokenBalance(jettonAddress)
- if (balance < amount) {
- throw new Error('Insufficient Jetton balance')
+ const amountBaseUnits = BigInt(amount)
+ const maxFeeBaseUnits = BigInt(maxFee)
+ const transferOptions = {
+ token: jettonAddress,
+ recipient,
+ amount: amountBaseUnits
}
+ const paymasterToken = { address: paymasterJettonAddress }
- const paymasterBalance = await account.getPaymasterTokenBalance()
- console.log('Paymaster token balance:', paymasterBalance)
+ const transferBalance = await account.getTokenBalance(jettonAddress)
+ if (transferBalance < amountBaseUnits) {
+ throw new Error('Insufficient Jetton balance')
+ }
- const quote = await account.quoteTransfer({
- token: jettonAddress,
- recipient,
- amount
- })
+ const quote = await account.quoteTransfer(transferOptions, { paymasterToken })
console.log('Estimated fee (paymaster token):', quote.fee)
- const result = await account.transfer({
- token: jettonAddress,
- recipient,
- amount
+ if (quote.fee > maxFeeBaseUnits) {
+ throw new Error('Quoted fee exceeds application policy')
+ }
+
+ const paymasterBalance = await account.getTokenBalance(paymasterJettonAddress)
+ const sameJetton = Address.parse(jettonAddress).equals(Address.parse(paymasterJettonAddress))
+ const requiredPaymasterBalance = sameJetton
+ ? amountBaseUnits + quote.fee
+ : quote.fee
+
+ if (paymasterBalance < requiredPaymasterBalance) {
+ throw new Error('Insufficient paymaster Jetton balance')
+ }
+
+ const result = await account.transfer(transferOptions, {
+ paymasterToken,
+ transferMaxFee: quote.fee
})
- console.log('Transfer hash:', result.hash)
+ console.log('Signed transfer body hash:', result.hash)
console.log('Actual fee (paymaster token):', result.fee)
return result
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/index.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/index.mdx
index d42b410f..7a7d736a 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/index.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/index.mdx
@@ -14,11 +14,11 @@ Use the gasless TON wallet module when your app needs Jetton transfers without r
- **Multi-Account Management**: Create and manage multiple accounts from a single seed phrase
- **TON Address Support**: Generate and manage TON addresses using V5R1 wallet contracts
- **Message Signing**: Sign and verify messages using TON cryptography
-- **Gasless Transactions**: Execute transactions without requiring TON for gas fees
+- **Gasless Jetton Transfers**: Transfer Jettons with fees paid in a supported paymaster Jetton
- **Paymaster Integration**: Built-in support for paymaster-based fee delegation
- **Jetton Support**: Query native TON and Jetton token balances
- **TypeScript Support**: Full TypeScript definitions included
-- **Memory Safety**: Secure private key management with automatic memory cleanup using sodium-universal
+- **Derived Key Cleanup**: Account disposal zeroes derived private key bytes with sodium-universal
- **Provider Flexibility**: Support for both TON Center and TON API endpoints
## Supported Networks
@@ -41,7 +41,7 @@ Get started with WDK's TON Gasless Wallet configuration
Get started with WDK's TON Gasless Wallet API
-Get started with WDK's with TON Gasless Wallet usage
+Get started with WDK's TON Gasless Wallet usage
diff --git a/content/docs/sdk/wallet-modules/wallet-ton-gasless/usage.mdx b/content/docs/sdk/wallet-modules/wallet-ton-gasless/usage.mdx
index f8348c1d..de8b9134 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton-gasless/usage.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton-gasless/usage.mdx
@@ -6,7 +6,7 @@ schemaType: TechArticle
icon: BookOpen
---
-The `@tetherto/wdk-wallet-ton-gasless` module provides wallet management for the TON blockchain with gasless transaction support, where transfer fees are paid using a configured paymaster token instead of native TON.
+The `@tetherto/wdk-wallet-ton-gasless` module provides wallet management for the TON blockchain with gasless Jetton transfer support, where transfer fees are paid using a configured paymaster token instead of native TON.
@@ -28,7 +28,7 @@ Transfer Jetton tokens gaslessly with paymaster fees.
Sign messages and verify signatures.
-Handle errors, manage fees, and dispose of sensitive data.
+Handle errors, manage fees, and clean up derived account keys.
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-ton/api-reference.mdx
index 8befa2e0..0cbcd9a2 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/api-reference.mdx
@@ -33,9 +33,9 @@ new WalletManagerTon(seed, config)
- `seed` (string | Uint8Array): BIP-39 mnemonic seed phrase or seed bytes
- `config` (object): Configuration object
- `tonClient` (object | TonClient): TON client configuration or instance
- - `url` (string): TON Center API URL (e.g., 'https://toncenter.com/api/v3')
+ - `url` (string): TON Center v2 JSON-RPC URL (e.g., 'https://toncenter.com/api/v2/jsonRPC')
- `secretKey` (string, optional): API key for TON Center
- - `transferMaxFee` (number, optional): Maximum fee amount for transfer operations (in nanotons)
+ - `transferMaxFee` (number | bigint, optional): Maximum fee amount for transfer operations (in nanotons)
- `transactionMaxFee` (number | bigint, optional): Maximum fee amount for native `sendTransaction()` and `signTransaction()` operations (in nanotons)
@@ -43,7 +43,7 @@ new WalletManagerTon(seed, config)
```javascript
const wallet = new WalletManagerTon(seedPhrase, {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key'
},
transferMaxFee: 1000000000, // Maximum Jetton transfer fee in nanotons
@@ -58,8 +58,8 @@ const wallet = new WalletManagerTon(seedPhrase, {
|--------|-------------|---------|
| `getAccount(index)` | Returns a wallet account at the specified index | `Promise\` |
| `getAccountByPath(path)` | Returns a wallet account at the specified BIP-44 derivation path | `Promise\` |
-| `getFeeRates()` | Returns current fee rates for transactions | `Promise\<{normal: number, fast: number}\>` |
-| `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` |
+| `getFeeRates()` | Returns fee rates from the mainnet TON API configuration | `Promise\<{normal: bigint, fast: bigint}\>` |
+| `dispose()` | Disposes cached accounts and signers; the manager seed remains in memory | `void` |
##### `getAccount(index)`
@@ -89,7 +89,7 @@ const account = await wallet.getAccountByPath("0'/0/1")
```
##### `getFeeRates()`
-Returns current fee rates for normal and fast transactions.
+Returns normal and fast fee rates from the mainnet TON API configuration. Through `1.0.0-beta.12`, this method always requests `https://tonapi.io/v2`, does not follow the configured `tonClient` network, and returns the same calculated value for both fields.
**Returns:** `Promise\` - Object containing normal and fast fee rates
@@ -101,7 +101,7 @@ console.log('Fast fee rate:', feeRates.fast, 'nanotons')
```
##### `dispose()`
-Disposes all wallet accounts, clearing private keys from memory.
+Disposes cached wallet accounts and signers, clearing their derived private keys. In the current beta, this method does not zero or unset the wallet manager's seed bytes.
**Example:**
```javascript
@@ -111,14 +111,13 @@ wallet.dispose()
#### Properties
##### `seed`
-The wallet's seed phrase.
+The wallet manager's sensitive raw seed bytes. A manager created with a seed phrase converts it to bytes before storing it; a signer-backed manager returns `undefined`.
-**Type:** `string | Uint8Array`
+**Type:** `Uint8Array | undefined`
-**Example:**
-```javascript
-console.log('Seed phrase:', wallet.seed)
-```
+
+Do not log, serialize, or expose this property. In the current beta, [`wallet.dispose()`](#dispose) does not zero or unset these bytes; release all manager references and manage the original seed lifecycle separately.
+
### WalletAccountTon
@@ -136,9 +135,9 @@ new WalletAccountTon(seed, path, config)
- `path` (string): BIP-44 derivation path (e.g., "0'/0/0")
- `config` (object): Configuration object
- `tonClient` (object | TonClient): TON client configuration or instance
- - `url` (string): TON Center API URL
+ - `url` (string): TON Center v2 JSON-RPC URL
- `secretKey` (string, optional): API key for TON Center
- - `transferMaxFee` (number, optional): Maximum fee amount for transfer operations
+ - `transferMaxFee` (number | bigint, optional): Maximum fee amount for transfer operations
- `transactionMaxFee` (number | bigint, optional): Maximum fee amount for native `sendTransaction()` and `signTransaction()` operations
@@ -146,7 +145,7 @@ new WalletAccountTon(seed, path, config)
```javascript
const account = new WalletAccountTon(seedPhrase, "0'/0/0", {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key'
},
transferMaxFee: 10000000, // Maximum Jetton transfer fee in nanotons
@@ -161,11 +160,11 @@ const account = new WalletAccountTon(seedPhrase, "0'/0/0", {
| `getAddress()` | Returns the account's TON address | `Promise\` |
| `sign(message)` | Signs a message using the account's private key | `Promise\` |
| `verify(message, signature)` | Verifies a message signature | `Promise\` |
-| `signTransaction(tx)` | Signs a transaction offline without broadcasting it | `Promise\` |
-| `sendTransaction(tx)` | Sends a TON transaction | `Promise\<{hash: string, fee: number}\>` |
-| `quoteSendTransaction(tx)` | Estimates the fee for a TON transaction | `Promise\<{fee: number}\>` |
-| `transfer(options)` | Transfers Jetton tokens to another address | `Promise\<{hash: string, fee: number}\>` |
-| `quoteTransfer(options)` | Estimates the fee for a Jetton transfer | `Promise\<{fee: number}\>` |
+| `signTransaction(tx)` | Builds a signed external-message body using current chain state, without broadcasting it | `Promise\` |
+| `sendTransaction(tx)` | Builds and sends a transaction, or sends a signed transfer-body `Cell` | `Promise\<{hash: string, fee: bigint}\>` |
+| `quoteSendTransaction(tx)` | Estimates the fee for a transaction or signed transfer-body `Cell` | `Promise\<{fee: bigint}\>` |
+| `transfer(options)` | Transfers Jetton tokens to another address | `Promise\<{hash: string, fee: bigint}\>` |
+| `quoteTransfer(options)` | Estimates the fee for a Jetton transfer | `Promise\<{fee: bigint}\>` |
| `getBalance()` | Returns the native TON balance (in nanotons) | `Promise\` |
| `getTokenBalance(tokenAddress)` | Returns the balance of a specific Jetton token | `Promise\` |
| `getTransactionReceipt(hash)` | Returns a transaction's receipt | `Promise\` |
@@ -214,7 +213,7 @@ console.log('Signature:', signature)
```
##### `signTransaction(tx)`
-Signs a transaction offline and returns the signed external-message body without broadcasting it. Use this to build a transaction on a signing device and broadcast it elsewhere. Added in v1.0.0-beta.8.
+Builds and signs an external-message body without broadcasting it. This is not an offline operation: it requires a configured TON client to read the wallet's current sequence number and, when `transactionMaxFee` is set, to estimate the fee. Added in v1.0.0-beta.8.
**Parameters:**
- `tx` (object): The transaction object (same shape as `sendTransaction`)
@@ -223,7 +222,7 @@ Signs a transaction offline and returns the signed external-message body without
- `bounceable` (boolean, optional): Whether the destination address is bounceable
- `body` (string | Cell, optional): Optional message body
-**Returns:** `Promise\` - The signed external-message body as a TON `Cell`. Call `cell.toBoc().toString('base64')` to obtain a wire-format payload ready for broadcast.
+**Returns:** `Promise\` - The signed body as a TON `Cell`. It is the body accepted by the matching opened `WalletContractV5R1.send()` call, not a complete external-message BOC that can be posted directly to TON Center.
**Throws:** Error if the estimated transaction fee exceeds `transactionMaxFee` when configured.
@@ -233,21 +232,21 @@ const cell = await account.signTransaction({
to: 'EQ...', // TON address
value: 1000000000 // 1 TON in nanotons
});
-const boc = cell.toBoc().toString('base64');
-console.log('Signed payload:', boc);
+// `cell` is not broadcast by signTransaction().
```
##### `sendTransaction(tx)`
-Sends a TON transaction and returns the result with hash and fee.
+Sends a TON transaction and returns its signed transfer body hash and fee.
**Parameters:**
-- `tx` (object): The transaction object
+- `tx` (TonTransaction | Cell): A transaction object or signed transfer-body `Cell`
- `to` (string): Recipient TON address (e.g., 'EQ...')
- - `value` (number): Amount in nanotons (1 TON = 1,000,000,000 nanotons)
+ - `value` (number | bigint): Amount in nanotons (1 TON = 1,000,000,000 nanotons)
- `bounceable` (boolean, optional): Whether the address is bounceable (TON-specific, optional)
+When `tx` is a `Cell`, WDK estimates its fee, enforces `transactionMaxFee`, and passes that exact body to the matching opened `WalletContractV5R1.send()` call. It does not rebuild the body, refresh its sequence number, or re-sign it.
-**Returns:** `Promise\<{hash: string, fee: number}\>` - Object containing hash and fee (in nanotons)
+**Returns:** `Promise\<{hash: string, fee: bigint}\>` - Object containing the signed transfer body hash as lowercase hex and the fee in nanotons
**Throws:** Error if the estimated transaction fee exceeds `transactionMaxFee` when configured.
@@ -257,7 +256,7 @@ const result = await account.sendTransaction({
to: 'EQ...', // TON address
value: 1000000000 // 1 TON in nanotons
});
-console.log('Transaction hash:', result.hash);
+console.log('Signed transfer body hash:', result.hash);
console.log('Transaction fee:', result.fee, 'nanotons');
```
@@ -265,12 +264,12 @@ console.log('Transaction fee:', result.fee, 'nanotons');
Estimates the fee for a transaction.
**Parameters:**
-- `tx` (object): The transaction object (same as sendTransaction)
+- `tx` (TonTransaction | Cell): A transaction object or signed transfer-body `Cell`
- `to` (string): Recipient TON address (e.g., 'EQ...')
- - `value` (number): Amount in nanotons (1 TON = 1,000,000,000 nanotons)
+ - `value` (number | bigint): Amount in nanotons (1 TON = 1,000,000,000 nanotons)
- `bounceable` (boolean, optional): Whether the address is bounceable (TON-specific, optional)
-**Returns:** `Promise\<{fee: number}\>` - Object containing fee estimate (in nanotons)
+**Returns:** `Promise\<{fee: bigint}\>` - Object containing fee estimate (in nanotons)
**Example:**
```javascript
@@ -288,9 +287,9 @@ Transfers Jettons (TON tokens) to another address.
- `options` (object): Transfer options
- `token` (string): Jetton master contract address (TON format, e.g., 'EQ...')
- `recipient` (string): Recipient TON address (e.g., 'EQ...')
- - `amount` (number): Amount in Jetton's base units
+ - `amount` (number | bigint): Amount in Jetton's base units
-**Returns:** `Promise\<{hash: string, fee: number}\>` - Object containing hash and fee (in nanotons)
+**Returns:** `Promise\<{hash: string, fee: bigint}\>` - Object containing the signed transfer body hash as lowercase hex and the fee in nanotons
**Example:**
```javascript
@@ -299,7 +298,7 @@ const result = await account.transfer({
recipient: 'EQ...', // Recipient's TON address
amount: 1000000000 // Amount in Jetton's base units
});
-console.log('Transfer hash:', result.hash);
+console.log('Signed transfer body hash:', result.hash);
console.log('Transfer fee:', result.fee, 'nanotons');
```
@@ -310,9 +309,9 @@ Estimates the fee for a Jetton (TON token) transfer.
- `options` (object): Transfer options (same as transfer)
- `token` (string): Jetton master contract address (TON format, e.g., 'EQ...')
- `recipient` (string): Recipient TON address (e.g., 'EQ...')
- - `amount` (number): Amount in Jetton's base units
+ - `amount` (number | bigint): Amount in Jetton's base units
-**Returns:** `Promise\<{fee: number}\>` - Object containing fee estimate (in nanotons)
+**Returns:** `Promise\<{fee: bigint}\>` - Object containing fee estimate (in nanotons)
**Example:**
```javascript
@@ -346,21 +345,32 @@ Returns the balance of a specific Jetton (TON token).
**Example:**
```javascript
const tokenBalance = await account.getTokenBalance('EQ...');
-console.log('Token balance:', tokenBalance, 'nanotons');
+console.log('Token balance:', tokenBalance, 'Jetton base units');
```
##### `getTransactionReceipt(hash)`
Returns a transaction's receipt if it has been mined.
+Through `1.0.0-beta.12`, the initial receipt lookup always queries mainnet TON Center v3. Do not rely on this method for testnet receipts; see [Network Selection](/sdk/wallet-modules/wallet-ton/configuration#network-selection).
+
**Parameters:**
-- `hash` (string): The transaction hash
+- `hash` (string): The signed transfer body hash returned by `sendTransaction()` or `transfer()`
**Returns:** `Promise\` - Transaction receipt or null if not yet mined
**Example:**
```javascript
-const receipt = await account.getTransactionReceipt('EQ...')
-console.log('Transaction confirmed:', receipt.success)
+const result = await account.sendTransaction({
+ to: 'EQ...',
+ value: 1000000000n
+})
+const receipt = await account.getTransactionReceipt(result.hash)
+
+if (receipt) {
+ console.log('Transaction receipt:', receipt)
+} else {
+ console.log('Transaction not yet included in a block')
+}
```
##### `toReadOnlyAccount()`
@@ -410,7 +420,7 @@ new WalletAccountReadOnlyTon(publicKey, config)
```
**Parameters:**
-- `publicKey` (string): The account's public key (hex or base64)
+- `publicKey` (string | Uint8Array): The account's public key. String values must be hex encoded.
- `config` (object): TON client and retry configuration without send-only fee caps
#### Methods
@@ -459,15 +469,24 @@ console.log('Signature valid:', isValid)
##### `getTransactionReceipt(hash)`
Returns a transaction's receipt if it has been mined.
+Through `1.0.0-beta.12`, the initial receipt lookup always queries mainnet TON Center v3. Do not rely on this method for testnet receipts; see [Network Selection](/sdk/wallet-modules/wallet-ton/configuration#network-selection).
+
**Parameters:**
-- `hash` (string): The transaction hash
+- `hash` (string): The signed transfer body hash returned by `sendTransaction()` or `transfer()`
**Returns:** `Promise\` - Transaction receipt or null if not yet mined
**Example:**
```javascript
-const receipt = await readOnlyAccount.getTransactionReceipt('EQ...')
-console.log('Transaction confirmed:', receipt.success)
+async function logTransactionReceipt(readOnlyAccount, transactionHash) {
+ const receipt = await readOnlyAccount.getTransactionReceipt(transactionHash)
+
+ if (receipt) {
+ console.log('Transaction receipt:', receipt)
+ } else {
+ console.log('Transaction not yet included in a block')
+ }
+}
```
## Types
@@ -500,6 +519,16 @@ interface TonTransaction {
}
```
+### Cell
+
+The signed transaction body returned by `signTransaction()` is a `Cell` from `@ton/core`. It is not re-exported by `@tetherto/wdk-wallet-ton`.
+
+```typescript
+import type { Cell } from '@ton/core'
+```
+
+This value is the signed transfer body accepted by the matching opened `WalletContractV5R1.send()` call. It is not a complete external-message BOC.
+
### TransferOptions
```typescript
@@ -520,7 +549,7 @@ interface TransferOptions {
* Amount in Jetton's base units
* @example 1000000000 // Amount depends on token decimals
*/
- amount: number;
+ amount: number | bigint;
}
```
@@ -529,16 +558,16 @@ interface TransferOptions {
```typescript
interface TransactionResult {
/**
- * Transaction hash in base64 format
- * @example 'EQD4FPq...'
+ * Signed transfer body hash as a lowercase hex string; pass it to getTransactionReceipt()
+ * @example '7f83b1657ff1fc53b92dc18148a1d65dfa13501404a55e63ddfde593f4f5f9d8'
*/
hash: string;
/**
* Transaction fee in nanotons
- * @example 100000 // 0.0001 TON
+ * @example 100000n // 0.0001 TON
*/
- fee: number;
+ fee: bigint;
}
```
@@ -547,16 +576,16 @@ interface TransactionResult {
```typescript
interface FeeRates {
/**
- * Fee rate for normal priority transactions (in nanotons)
- * @example 100000000 // 0.1 TON
+ * Mainnet-derived fee rate in nanotons
+ * @example 100000000n // 0.1 TON
*/
- normal: number;
+ normal: bigint;
/**
- * Fee rate for high priority transactions (in nanotons)
- * @example 200000000 // 0.2 TON
+ * Same mainnet-derived fee rate as `normal` through v1.0.0-beta.12
+ * @example 100000000n // 0.1 TON
*/
- fast: number;
+ fast: bigint;
}
```
@@ -583,8 +612,8 @@ interface KeyPair {
interface TonWalletConfig {
/**
* TON Center client configuration, a TonClient instance, or an array of
- * either. When an array is provided, connection errors cause the wallet to
- * fail over to the next client in the list.
+ * either. When an array is provided, any thrown Error causes the wallet to
+ * retry on the next client by default.
*/
tonClient?: TonClientConfig | TonClient | Array;
@@ -611,7 +640,7 @@ interface TonWalletConfig {
interface TonClientConfig {
/**
* TON Center API endpoint
- * @example 'https://toncenter.com/api/v3'
+ * @example 'https://toncenter.com/api/v2/jsonRPC'
*/
url: string;
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/configuration.mdx b/content/docs/sdk/wallet-modules/wallet-ton/configuration.mdx
index 3f309c59..37ee3cb6 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/configuration.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/configuration.mdx
@@ -15,7 +15,7 @@ import WalletManagerTon from '@tetherto/wdk-wallet-ton'
const config = {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
},
transferMaxFee: 1000000000, // Optional: Maximum Jetton transfer fee in nanotons
@@ -32,7 +32,7 @@ import { WalletAccountTon } from '@tetherto/wdk-wallet-ton'
const accountConfig = {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
},
transferMaxFee: 1000000000, // Optional: Maximum Jetton transfer fee in nanotons
@@ -46,13 +46,13 @@ const account = new WalletAccountTon(seedPhrase, "0'/0/0", accountConfig)
### tonClient
-The `tonClient` option configures the TON Center API client for blockchain interactions. It accepts a single client configuration, a `TonClient` instance, or an array of either for endpoint failover.
+The `tonClient` option configures the TON Center v2 JSON-RPC client for blockchain interactions. It accepts a single client configuration, a `TonClient` instance, or an array of either for endpoint failover.
**Type:**
```typescript
interface TonClientConfig {
/**
- * TON Center API endpoint URL
+ * TON Center v2 JSON-RPC endpoint URL
*/
url: string;
@@ -75,22 +75,26 @@ type TonClientOption =
// Basic configuration
const config = {
tonClient: {
- url: 'https://toncenter.com/api/v3'
+ url: 'https://toncenter.com/api/v2/jsonRPC'
}
}
// With API key for higher rate limits
const config = {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key'
}
}
```
+
+Provide the full v2 JSON-RPC endpoint. The module passes this URL to `@ton/ton`'s `TonClient`, which posts JSON-RPC requests directly to it.
+
+
### tonClient (array) and retries
-Since v1.0.0-beta.8, `tonClient` also accepts an array of configurations or instances. When a connection error occurs, the wallet automatically fails over to the next client in the list. The `retries` option sets the number of additional retry attempts after the initial call fails.
+Since v1.0.0-beta.8, `tonClient` also accepts an array of configurations or instances. When a client call throws an `Error`, the wallet automatically retries on the next client. By default, this includes application errors as well as connection errors. The `retries` option sets the number of additional attempts after the initial call fails.
**Type:** `retries` is `number` (optional)
@@ -101,8 +105,8 @@ The total number of attempts is `1 + retries`. For example, `retries: 3` with fo
```javascript
const config = {
tonClient: [
- { url: 'https://toncenter.com/api/v3', secretKey: 'your-api-key' },
- { url: 'https://toncenter.com/api/v3' }
+ { url: 'https://toncenter.com/api/v2/jsonRPC', secretKey: 'your-api-key' },
+ { url: 'https://your-secondary-toncenter.example/api/v2/jsonRPC' } // Replace with a real independent provider
],
retries: 3 // Optional: additional retry attempts after the first failure
}
@@ -112,7 +116,7 @@ const config = {
The `transferMaxFee` option sets the maximum allowed fee, in nanotons, for Jetton `transfer()` operations.
-**Type:** `number` (nanotons)
+**Type:** `number | bigint` (nanotons)
**Default:** No maximum (undefined)
@@ -125,7 +129,7 @@ const config = {
// Example with both options
const config = {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key'
},
transferMaxFee: 1000000000
@@ -144,7 +148,7 @@ The `transactionMaxFee` option sets the maximum allowed fee, in nanotons, for na
```javascript
const config = {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key'
},
transactionMaxFee: 1000000000 // 1 TON in nanotons
@@ -160,7 +164,7 @@ import { WalletAccountReadOnlyTon } from '@tetherto/wdk-wallet-ton'
const readOnlyConfig = {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
}
}
@@ -170,10 +174,14 @@ const readOnlyAccount = new WalletAccountReadOnlyTon(publicKey, readOnlyConfig)
## Network Selection
-The TON network (mainnet or testnet) is determined by the TON Center API endpoint URL:
+For client-backed balance and transaction operations, the TON network is determined by the TON Center API endpoint URL:
-- Mainnet: `https://toncenter.com/api/v3`
-- Testnet: `https://testnet.toncenter.com/api/v3`
+- Mainnet: `https://toncenter.com/api/v2/jsonRPC`
+- Testnet: `https://testnet.toncenter.com/api/v2/jsonRPC`
+
+
+Through `@tetherto/wdk-wallet-ton` `1.0.0-beta.12`, `getTransactionReceipt()` starts its lookup against a hard-coded mainnet TON Center v3 endpoint, even when `tonClient` points to testnet. `getFeeRates()` likewise always reads mainnet TON API configuration. Do not rely on these methods for testnet-specific receipts or fee rates.
+
## Derivation Paths
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/guides/check-balances.mdx b/content/docs/sdk/wallet-modules/wallet-ton/guides/check-balances.mdx
index 16d80a76..97be9c9b 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/guides/check-balances.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/guides/check-balances.mdx
@@ -39,7 +39,7 @@ import { WalletAccountReadOnlyTon } from '@tetherto/wdk-wallet-ton'
const readOnlyAccount = new WalletAccountReadOnlyTon(publicKey, {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
}
})
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/guides/get-started.mdx b/content/docs/sdk/wallet-modules/wallet-ton/guides/get-started.mdx
index 39408225..64508958 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/guides/get-started.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/guides/get-started.mdx
@@ -25,18 +25,19 @@ You can create a new wallet instance using the [`WalletManagerTon`](/sdk/wallet-
```javascript title="Create TON Wallet"
import WalletManagerTon, { WalletAccountTon, WalletAccountReadOnlyTon } from '@tetherto/wdk-wallet-ton'
-const seedPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
+const seedPhrase = process.env.WDK_SEED_PHRASE
+if (!seedPhrase) throw new Error('WDK_SEED_PHRASE is required')
const wallet = new WalletManagerTon(seedPhrase, {
tonClient: {
- url: 'https://toncenter.com/api/v3',
+ url: 'https://toncenter.com/api/v2/jsonRPC',
secretKey: 'your-api-key' // Optional
}
})
```
-**Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds.
+**Secure the Seed Phrase:** Load seed phrases from secure storage; never hardcode or log them. This server-side example uses an environment variable. If the seed phrase is lost, the user will permanently lose access to their funds.
## 3. Get Your First Account
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/guides/handle-errors.mdx b/content/docs/sdk/wallet-modules/wallet-ton/guides/handle-errors.mdx
index 157721a1..67aa5d93 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/guides/handle-errors.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/guides/handle-errors.mdx
@@ -1,11 +1,11 @@
---
title: Handle Errors
-description: Handle errors, manage fees, and dispose of sensitive data in TON wallets.
+description: Handle errors, manage fees, and clean up derived account keys in TON wallets.
docType: how-to
schemaType: TechArticle
---
-This guide covers how to [handle transaction errors](#handle-transaction-errors) and [handle token transfer errors](#handle-token-transfer-errors), plus [best practices](#best-practices) for fee management and memory cleanup.
+This guide covers how to [handle transaction errors](#handle-transaction-errors) and [handle token transfer errors](#handle-token-transfer-errors), plus [best practices](#best-practices) for fee management and derived key cleanup.
## Handle Transaction Errors
@@ -18,12 +18,12 @@ try {
value: 1000000000,
bounceable: true
})
- console.log('Transaction hash:', result.hash)
+ console.log('Signed transfer body hash:', result.hash)
console.log('Fee paid:', result.fee, 'nanotons')
} catch (error) {
if (error.message.includes('insufficient balance')) {
console.error('Not enough TON to complete transaction')
- } else if (error.message.includes('Exceeded maximum fee')) {
+ } else if (error.message === 'Exceeded maximum fee cost for transaction operation.') {
console.error('Transaction fee exceeds transactionMaxFee')
} else if (error.message.includes('invalid address')) {
console.error('Invalid recipient address')
@@ -46,12 +46,12 @@ try {
recipient: 'EQ...',
amount: 1000000
})
- console.log('Transfer submitted:', result.hash)
+ console.log('Signed transfer body hash:', result.hash)
} catch (error) {
console.error('Transfer failed:', error.message)
if (error.message.toLowerCase().includes('insufficient')) {
console.log('Please add more tokens to your wallet')
- } else if (error.message.toLowerCase().includes('fee')) {
+ } else if (error.message === 'Exceeded maximum fee cost for transfer operations.') {
console.log('The transfer fee exceeds your configured maximum')
}
}
@@ -61,7 +61,7 @@ try {
### Manage Fee Limits
-Set `transactionMaxFee` when creating the wallet to cap native `sendTransaction()` and `signTransaction()` costs. Set `transferMaxFee` separately for Jetton `transfer()` costs. You can retrieve current network rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton/api-reference):
+Set `transactionMaxFee` when creating the wallet to cap native `sendTransaction()` and `signTransaction()` costs. Set `transferMaxFee` separately for Jetton `transfer()` costs. You can retrieve mainnet TON API rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton/api-reference). Through `1.0.0-beta.12`, this method does not follow a configured testnet client and returns the same calculated value for `normal` and `fast`:
```javascript title="Fee Management"
const feeRates = await wallet.getFeeRates()
@@ -69,9 +69,9 @@ console.log('Normal fee rate:', feeRates.normal, 'nanotons')
console.log('Fast fee rate:', feeRates.fast, 'nanotons')
```
-### Dispose of Sensitive Data
+### Dispose Derived Account Keys
-Call [`dispose()`](/sdk/wallet-modules/wallet-ton/api-reference) on accounts and wallet managers to clear private keys and sensitive data from memory when they are no longer needed:
+Call [`dispose()`](/sdk/wallet-modules/wallet-ton/api-reference) on accounts and wallet managers to clear cached accounts' derived private keys when they are no longer needed:
```javascript title="Memory Cleanup"
account.dispose()
@@ -80,5 +80,5 @@ wallet.dispose()
```
-Always call [`dispose()`](/sdk/wallet-modules/wallet-ton/api-reference) in a `finally` block or cleanup handler to ensure sensitive data is cleared even if an error occurs.
+Call [`dispose()`](/sdk/wallet-modules/wallet-ton/api-reference) in a `finally` block or cleanup handler so derived account keys are cleared even if an error occurs. In the current beta, `wallet.dispose()` does not zero or unset `wallet.seed`; manage the seed lifecycle separately and release all manager references when finished.
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-ton/guides/send-transactions.mdx
index 8dbc0652..ee54a8bc 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/guides/send-transactions.mdx
@@ -5,7 +5,7 @@ docType: how-to
schemaType: TechArticle
---
-This guide explains how to [send native TON](#send-native-ton), [estimate transaction fees](#estimate-transaction-fees), [cap transaction fees](#cap-transaction-fees), [use dynamic fee rates](#use-dynamic-fee-rates), and [sign a transaction offline](#sign-a-transaction-offline).
+This guide explains how to [send native TON](#send-native-ton), [estimate transaction fees](#estimate-transaction-fees), [cap transaction fees](#cap-transaction-fees), [read mainnet fee rates](#read-mainnet-fee-rates), [prepare a signed transaction body](#prepare-a-signed-transaction-body), and [quote and send a signed transaction body](#quote-and-send-a-signed-transaction-body).
On TON, values are expressed in nanotons (1 TON = 10^9 nanotons). Transactions support an optional `bounceable` parameter specific to the TON network.
@@ -21,7 +21,7 @@ const result = await account.sendTransaction({
value: 1000000000, // 1 TON in nanotons
bounceable: true // Optional: specify if the address is bounceable
})
-console.log('Transaction hash:', result.hash)
+console.log('Signed transfer body hash:', result.hash)
console.log('Transaction fee:', result.fee, 'nanotons')
```
@@ -44,14 +44,14 @@ Set [`transactionMaxFee`](/sdk/wallet-modules/wallet-ton/configuration#transacti
```javascript title="Cap Native Transaction Fees"
const wallet = new WalletManagerTon(seedPhrase, {
- tonClient: { url: 'https://toncenter.com/api/v3' },
+ tonClient: { url: 'https://toncenter.com/api/v2/jsonRPC' },
transactionMaxFee: 1000000000n
})
```
-## Use Dynamic Fee Rates
+## Read Mainnet Fee Rates
-You can retrieve current fee rates from the wallet manager using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton/api-reference):
+You can retrieve mainnet TON API fee rates from the wallet manager using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-ton/api-reference#getfeerates). Through `1.0.0-beta.12`, this method does not follow a configured testnet client and returns the same calculated value for `normal` and `fast`:
```javascript title="Get Fee Rates"
const feeRates = await wallet.getFeeRates()
@@ -59,21 +59,40 @@ console.log('Normal fee rate:', feeRates.normal, 'nanotons')
console.log('Fast fee rate:', feeRates.fast, 'nanotons')
```
-## Sign a Transaction Offline
+## Prepare a Signed Transaction Body
-You can sign a transaction without broadcasting it using [`account.signTransaction()`](/sdk/wallet-modules/wallet-ton/api-reference#signtransactiontx). This returns the signed external-message body as a TON `Cell`, which you can serialize and broadcast from another environment.
+You can build a signed transaction body without broadcasting it using [`account.signTransaction()`](/sdk/wallet-modules/wallet-ton/api-reference#signtransactiontx). The method still requires the configured TON client to read the current sequence number and, when a fee cap is configured, estimate the fee. It returns the body `Cell` accepted by the matching opened `WalletContractV5R1.send()` call, not a complete external-message BOC.
-```javascript title="Sign Transaction Offline"
+```javascript title="Prepare Signed Transaction Body"
const cell = await account.signTransaction({
to: 'EQ...', // TON address
value: 1000000000 // 1 TON in nanotons
})
-// Serialize to a wire-format payload for broadcast elsewhere
-const boc = cell.toBoc().toString('base64')
-console.log('Signed payload:', boc)
+// `cell` is not broadcast by signTransaction().
```
+## Quote and Send a Signed Transaction Body
+
+Pass the `Cell` returned by `signTransaction()` to the quote and send methods when review and submission are separate steps.
+
+```javascript title="Quote and Send a Signed Body"
+const cell = await account.signTransaction({
+ to: 'EQ...',
+ value: 1000000000n
+})
+
+const quote = await account.quoteSendTransaction(cell)
+console.log('Estimated fee:', quote.fee, 'nanotons')
+
+const result = await account.sendTransaction(cell)
+console.log('Signed transfer body hash:', result.hash)
+```
+
+
+The signed body contains the wallet sequence number read during signing. Submit it through the same matching account before that sequence number changes. WDK sends the `Cell` unchanged and does not rebuild or re-sign it; `sendTransaction()` estimates its fee again and enforces `transactionMaxFee`. The returned hash identifies the signed transfer body, not a network transaction hash.
+
+
## Next Steps
To transfer Jetton tokens instead of native TON, see [Transfer Jetton Tokens](/sdk/wallet-modules/wallet-ton/guides/transfer-tokens).
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/guides/transfer-tokens.mdx b/content/docs/sdk/wallet-modules/wallet-ton/guides/transfer-tokens.mdx
index 9c891c59..ada6de07 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/guides/transfer-tokens.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/guides/transfer-tokens.mdx
@@ -17,7 +17,7 @@ const transferResult = await account.transfer({
recipient: 'EQ...', // Recipient's TON address
amount: 1000000 // Amount in Jetton's base units
})
-console.log('Transfer hash:', transferResult.hash)
+console.log('Signed transfer body hash:', transferResult.hash)
console.log('Transfer fee:', transferResult.fee, 'nanotons')
```
@@ -69,7 +69,7 @@ async function transferJettonWithValidation(account, jettonAddress, recipient, a
recipient,
amount
})
- console.log('Transfer hash:', result.hash)
+ console.log('Signed transfer body hash:', result.hash)
console.log('Actual fee:', result.fee, 'nanotons')
return result
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/index.mdx b/content/docs/sdk/wallet-modules/wallet-ton/index.mdx
index b0a1b747..a89ab12e 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/index.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/index.mdx
@@ -28,9 +28,10 @@ Use [`getAccountByPath`](/sdk/wallet-modules/wallet-ton/api-reference) to supply
- **TON Address Support**: Generate and manage TON addresses using V5R1 wallet contracts
- **Message Signing**: Sign and verify messages using TON cryptography
- **Transaction Management**: Send transactions and get fee estimates
+- **Signed Body Submission**: Quote and send the signed transfer-body `Cell` returned by `signTransaction()`
- **Jetton Support**: Query native TON and Jetton token balances
- **TypeScript Support**: Full TypeScript definitions included
-- **Memory Safety**: Secure private key management with automatic memory cleanup using sodium-universal
+- **Derived Key Cleanup**: Account disposal zeroes derived private key bytes with sodium-universal
- **Provider Flexibility**: Support for custom TON RPC endpoints and TON Center API
## Supported Networks
diff --git a/content/docs/sdk/wallet-modules/wallet-ton/usage.mdx b/content/docs/sdk/wallet-modules/wallet-ton/usage.mdx
index 449d6024..d058c2c0 100644
--- a/content/docs/sdk/wallet-modules/wallet-ton/usage.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-ton/usage.mdx
@@ -28,7 +28,7 @@ Transfer Jetton tokens and estimate fees.
Sign messages and verify signatures.
-Handle errors, manage fees, and dispose of sensitive data.
+Handle errors, manage fees, and clean up derived account keys.
diff --git a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/api-reference.mdx
index d172d99e..2650bd3f 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/api-reference.mdx
@@ -34,7 +34,8 @@ new WalletManagerTronGasfree(seed, config)
- `verifyingContract` (string): Gas-free verifying contract address
- `gasFreeApiKey` (string, optional): API key for signed GasFree provider requests
- `gasFreeApiSecret` (string, optional): API secret for signed GasFree provider requests. Provide together with `gasFreeApiKey`.
- - `transferMaxFee` (number, optional): Maximum fee for transfer operations
+ - `transferMaxFee` (number | bigint, optional): Shared config field; the current runtime does not use it as a default transfer cap
+ - `transactionMaxFee` (number | bigint, optional): Shared config field; native transaction methods remain unsupported, so it has no runtime effect
**Example:**
```javascript
@@ -45,8 +46,7 @@ const wallet = new WalletManagerTronGasfree(seedPhrase, {
serviceProvider: 'T...',
verifyingContract: 'T...',
gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret
- gasFreeApiSecret: 'your-api-secret',
- transferMaxFee: 10000000 // Optional
+ gasFreeApiSecret: 'your-api-secret'
})
```
@@ -56,7 +56,7 @@ const wallet = new WalletManagerTronGasfree(seedPhrase, {
|--------|-------------|---------|
| `getAccount(index)` | Returns a wallet account at the specified index | `Promise\` |
| `getAccountByPath(path)` | Returns a wallet account at the specified BIP-44 derivation path | `Promise\` |
-| `getFeeRates()` | Returns current fee rates for normal and fast transactions | `Promise\<{normal: number, fast: number}\>` |
+| `getFeeRates()` | Returns current fee rates for normal and fast transactions | `Promise\<{normal: bigint, fast: bigint}\>` |
| `dispose()` | Disposes all wallet accounts, clearing private keys from memory | `void` |
##### `getAccount(index)`
@@ -88,7 +88,7 @@ const account = await wallet.getAccountByPath("0'/0/1")
##### `getFeeRates()`
Returns current fee rates for normal and fast transactions.
-**Returns:** `Promise\<{normal: number, fast: number}\>` - Object containing fee rates in sun
+**Returns:** `Promise\<{normal: bigint, fast: bigint}\>` - Object containing fee rates in sun
- `normal`: Fee rate for normal priority transactions
- `fast`: Fee rate for high priority transactions
@@ -129,7 +129,7 @@ new WalletAccountTronGasfree(seed, path, config)
| `getAddress()` | Returns the account's address | `Promise\` |
| `getBalance()` | Returns the native TRX balance (in sun) | `Promise\` |
| `getTokenBalance(tokenAddress)` | Returns the balance of a specific TRC20 token | `Promise\` |
-| `transfer(options)` | Transfers TRC20 tokens to another address | `Promise\<{hash: string, fee: bigint, activationFee: bigint}\>` |
+| `transfer(options, config?)` | Transfers TRC20 tokens with an optional per-call fee cap | `Promise\<{hash: string, fee: bigint, activationFee: bigint}\>` |
| `quoteTransfer(options)` | Estimates the fee for a TRC20 transfer | `Promise\<{fee: bigint, activationFee: bigint}\>` |
| `sign(message)` | Signs a message using the account's private key | `Promise\` |
| `signTransaction(tx)` | Unsupported on Tron GasFree; always throws | `Promise\` |
@@ -181,7 +181,9 @@ Transfers TRC20 tokens to another address using the gas-free service.
- `options` (TransferOptions): Transfer options
- `token` (string): TRC20 contract address
- `recipient` (string): Recipient's Tron address
- - `amount` (number): Amount in token base units
+ - `amount` (number | bigint): Amount in token base units
+- `config` (object, optional): Per-call transfer configuration
+ - `transferMaxFee` (number | bigint, optional): Reject when the quoted total fee is greater than this token-base-unit cap. A fee equal to the cap is allowed.
**Returns:** `Promise\<{hash: string, fee: bigint, activationFee: bigint}\>` - Object containing transaction hash, total fee paid in token base units, and the activation-fee portion. `activationFee` is `0n` when no account activation fee applies.
@@ -190,7 +192,9 @@ Transfers TRC20 tokens to another address using the gas-free service.
const result = await account.transfer({
token: 'T...', // TRC20 contract address
recipient: 'T...', // Recipient's address
- amount: 1000000 // Amount in token base units
+ amount: 1000000n // Amount in token base units
+}, {
+ transferMaxFee: 5000n
})
console.log('Transaction hash:', result.hash)
console.log('Fee paid:', result.fee, 'token base units')
@@ -413,26 +417,29 @@ interface TransferOptions {
* Amount to transfer in token base units
* @example 1000000 // 1 USDT (6 decimals)
*/
- amount: number;
+ amount: number | bigint;
}
```
-### TransferResult
-Result object returned by transfer operations.
+### Transfer Results
+
+The GasFree package re-exports `TransferResult` and `TronActivationFee` from `@tetherto/wdk-wallet-tron`. GasFree transfer and quote methods return intersections of those types:
```typescript
-interface TransferResult {
- /**
- * The transaction hash
- * @example '0x123...'
- */
- hash: string;
+import type {
+ TransferResult,
+ TronActivationFee
+} from '@tetherto/wdk-wallet-tron-gasfree'
- /**
- * Fee paid in token base units
- * @example 1000 // Fee in token base units
- */
- fee: bigint;
+type TronGasfreeTransferResult = TransferResult & TronActivationFee
+type TronGasfreeTransferQuote = Omit & TronActivationFee
+```
+
+`TransferResult` supplies `hash: string` and `fee: bigint`. `TronActivationFee` supplies the activation-fee portion separately:
+
+```typescript
+interface TronActivationFee {
+ activationFee: bigint;
}
```
@@ -445,13 +452,13 @@ interface FeeRates {
* Fee rate for normal priority transactions (in sun)
* @example 1000
*/
- normal: number;
+ normal: bigint;
/**
* Fee rate for high priority transactions (in sun)
* @example 2000
*/
- fast: number;
+ fast: bigint;
}
```
@@ -585,9 +592,20 @@ interface TronGasfreeWalletConfig {
* @example 10000000
*/
transferMaxFee?: number | bigint;
+
+ /**
+ * Shared wallet config field for native transaction methods.
+ * The GasFree module does not support quoteSendTransaction(),
+ * signTransaction(), or sendTransaction(), so this field has no
+ * runtime effect in the current release.
+ * @optional
+ */
+ transactionMaxFee?: number | bigint;
}
```
+The current runtime also does not use constructor-level `transferMaxFee` as a default. Enforce a GasFree transfer cap with the second argument to `account.transfer()`.
+
### KeyPair
Account key pair information.
diff --git a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/configuration.mdx b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/configuration.mdx
index 575cd323..1e3f1576 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/configuration.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/configuration.mdx
@@ -27,13 +27,8 @@ const config = {
chainId: 728126428, // Blockchain ID
provider: 'https://api.trongrid.io', // Tron RPC endpoint
gasFreeProvider: 'https://open.gasfree.io/tron/', // Gas-free service URL
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', // Service provider address
- verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', // Verifying contract address
-
- // Optional parameters
- gasFreeApiKey: 'your-api-key', // Required only for signed GasFree API requests
- gasFreeApiSecret: 'your-api-secret', // Provide together with gasFreeApiKey
- transferMaxFee: 10000000 // Maximum fee in token base units
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS',
+ verifyingContract: 'TFFAMQLZybALaLb4uxHA9RBE7pxhUAjF3U'
}
const wallet = new WalletManagerTronGasfree(seedPhrase, config)
@@ -44,15 +39,23 @@ const config2 = {
chainId: 728126428,
provider: tronWeb,
gasFreeProvider: 'https://open.gasfree.io/tron/',
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- gasFreeApiKey: 'your-api-key',
- gasFreeApiSecret: 'your-api-secret'
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS',
+ verifyingContract: 'TFFAMQLZybALaLb4uxHA9RBE7pxhUAjF3U'
}
```
`gasFreeApiKey` and `gasFreeApiSecret` are optional. Omit both when your GasFree provider accepts unsigned requests. If you configure signed GasFree API requests, provide both values together; the constructor rejects partial credentials.
+
+The module performs HMAC request signing in the application process. Never embed `gasFreeApiSecret` in browser, mobile, or distributed desktop code. For client applications, use unsigned access only when the selected provider explicitly permits it, or route GasFree API requests through an authenticated backend that stores the secret.
+
+
+Retrieve the current service-provider address from the provider's [`GET /api/v1/config/provider/all`](https://docs.gasfree.io/#get-apiv1configproviderall) response. Do not substitute the verifying-contract address for `serviceProvider`.
+
+
+`TronGasfreeWalletConfig` includes `transferMaxFee` and `transactionMaxFee` for shared wallet type compatibility. The current GasFree runtime does not use either constructor field as a fee cap. Pass `transferMaxFee` in the second argument to `account.transfer()`; native transaction methods are unsupported, so `transactionMaxFee` has no effect.
+
+
## Account Configuration
Both `WalletAccountTronGasfree` and `WalletAccountReadOnlyTronGasfree` share similar configuration requirements:
@@ -68,25 +71,20 @@ const account = new WalletAccountTronGasfree(
chainId: 728126428,
provider: 'https://api.trongrid.io',
gasFreeProvider: 'https://open.gasfree.io/tron/',
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret
- gasFreeApiSecret: 'your-api-secret',
- transferMaxFee: 10000000 // Optional
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS',
+ verifyingContract: 'TFFAMQLZybALaLb4uxHA9RBE7pxhUAjF3U'
}
)
-// Read-only account (transferMaxFee not needed)
+// Read-only account (fee-cap fields are omitted)
const readOnlyAccount = new WalletAccountReadOnlyTronGasfree(
'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', // Tron address
{
chainId: 728126428,
provider: 'https://api.trongrid.io',
gasFreeProvider: 'https://open.gasfree.io/tron/',
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret
- gasFreeApiSecret: 'your-api-secret'
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS',
+ verifyingContract: 'TFFAMQLZybALaLb4uxHA9RBE7pxhUAjF3U'
}
)
@@ -163,14 +161,15 @@ The `gasFreeApiKey` option is your API key for signed requests to the gas-free s
**Example:**
```javascript
+// Trusted server runtime only
const config = {
- gasFreeApiKey: 'your-api-key',
- gasFreeApiSecret: 'your-api-secret'
+ gasFreeApiKey: process.env.GASFREE_API_KEY,
+ gasFreeApiSecret: process.env.GASFREE_API_SECRET
}
```
-Provide `gasFreeApiKey` and `gasFreeApiSecret` together. Passing only one of them throws during account construction.
+Provide `gasFreeApiKey` and `gasFreeApiSecret` together. Passing only one of them throws during account construction. Because the module uses the secret to sign requests locally, configure these fields only in a trusted server runtime.
### Gas-Free API Secret
@@ -183,14 +182,15 @@ The `gasFreeApiSecret` option is your API secret for signed requests to the gas-
**Example:**
```javascript
+// Trusted server runtime only
const config = {
- gasFreeApiKey: 'your-api-key',
- gasFreeApiSecret: 'your-api-secret'
+ gasFreeApiKey: process.env.GASFREE_API_KEY,
+ gasFreeApiSecret: process.env.GASFREE_API_SECRET
}
```
-Provide `gasFreeApiSecret` and `gasFreeApiKey` together. Passing only one of them throws during account construction.
+Provide `gasFreeApiSecret` and `gasFreeApiKey` together. Passing only one of them throws during account construction. Never ship the secret in a browser, mobile, or desktop application bundle.
### Service Provider
@@ -204,11 +204,13 @@ The `serviceProvider` option is the Tron address of the gas-free service provide
```javascript
const config = {
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH'
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS'
}
```
+Retrieve the current address from the provider's [`GET /api/v1/config/provider/all`](https://docs.gasfree.io/#get-apiv1configproviderall) response.
+
### Verifying Contract
The `verifyingContract` option is the Tron address of the contract that verifies gas-free transactions.
@@ -221,14 +223,14 @@ The `verifyingContract` option is the Tron address of the contract that verifies
```javascript
const config = {
- verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH'
+ verifyingContract: 'TFFAMQLZybALaLb4uxHA9RBE7pxhUAjF3U'
}
```
### Transfer Max Fee
-The `transferMaxFee` option sets a maximum limit for transaction fees to prevent unexpectedly high costs.
+Pass `transferMaxFee` in the optional second argument to `account.transfer()` to cap one GasFree TRC20 transfer. The constructor-level field exists in `TronGasfreeWalletConfig` but is not used as a default by the current runtime.
**Type:** `number | bigint`
@@ -240,26 +242,29 @@ The `transferMaxFee` option sets a maximum limit for transaction fees to prevent
```javascript
const config = {
- transferMaxFee: 10000000 // Maximum fee in token base units
+ transferMaxFee: 5000n
}
-// Usage with error handling
-try {
- const result = await account.transfer({
- token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
- recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- amount: 1000000
- }, {
- transferMaxFee: 5000 // Override default max fee
- })
-} catch (error) {
- if (error.message.includes('exceeds the transfer max fee')) {
- console.error('Transfer cancelled: Fee too high')
- }
-}
+const result = await account.transfer({
+ token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
+ recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
+ amount: 1000000n
+}, config)
```
+The transfer is rejected only when the quoted total fee is greater than the cap. A fee equal to `transferMaxFee` is allowed. Use an integer or `bigint` value because the runtime converts the cap with `BigInt()`.
+
+### Transaction Max Fee
+
+`transactionMaxFee` is present in the public config type for base-wallet compatibility.
+
+**Type:** `number | bigint`
+
+**Required:** No (optional)
+
+The GasFree module does not support `quoteSendTransaction()`, `signTransaction()`, or `sendTransaction()`. Those methods always throw, so this field does not control a runtime operation in the current release. Use the base `@tetherto/wdk-wallet-tron` package for native TRX transactions.
+
## Network-Specific Configurations
### Tron Mainnet
@@ -269,10 +274,8 @@ const mainnetConfig = {
chainId: 728126428,
provider: 'https://api.trongrid.io',
gasFreeProvider: 'https://open.gasfree.io/tron/',
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- verifyingContract: 'TFFAMLQZybALab4uxHA9RBE7pxhUAjfF3U', // Official Mainnet Contract
- gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret
- gasFreeApiSecret: 'your-api-secret'
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS',
+ verifyingContract: 'TFFAMQLZybALaLb4uxHA9RBE7pxhUAjF3U' // Official mainnet contract
}
```
@@ -284,10 +287,8 @@ const nileConfig = {
chainId: 3448148188, // Nile Testnet (Specific ID required for GasFree)
provider: 'https://nile.trongrid.io',
gasFreeProvider: 'https://open-test.gasfree.io/nile/',
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- verifyingContract: 'THQGuFzL87ZqhxkgqYEryRAd7gqFqL5rdc', // Official Testnet Contract
- gasFreeApiKey: 'your-testnet-api-key', // Optional: provide with gasFreeApiSecret
- gasFreeApiSecret: 'your-testnet-api-secret'
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS',
+ verifyingContract: 'THQGuFzL87ZqhxkgqYEryRAd7gqFqL5rdc' // Official Nile testnet contract
}
```
diff --git a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/check-balances.mdx b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/check-balances.mdx
index 65eddf5b..241b90a1 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/check-balances.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/check-balances.mdx
@@ -40,11 +40,9 @@ import { WalletAccountReadOnlyTronGasfree } from '@tetherto/wdk-wallet-tron-gasf
const readOnlyAccount = new WalletAccountReadOnlyTronGasfree('TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH', {
chainId: 728126428,
provider: 'https://api.trongrid.io',
- gasFreeProvider: 'https://gasfree.provider.url',
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- gasFreeApiKey: 'your-api-key', // Optional: provide with gasFreeApiSecret
- gasFreeApiSecret: 'your-api-secret'
+ gasFreeProvider: 'https://open.gasfree.io/tron/',
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS',
+ verifyingContract: 'TFFAMQLZybALaLb4uxHA9RBE7pxhUAjF3U'
})
```
@@ -61,4 +59,4 @@ You can also create a read-only account from an existing owned account using [`a
## Next Steps
-With balance checks in place, learn how to [send TRX](/sdk/wallet-modules/wallet-tron-gasfree/guides/send-transactions).
+With balance checks in place, learn how to [transfer TRC20 tokens](/sdk/wallet-modules/wallet-tron-gasfree/guides/transfer-tokens).
diff --git a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/get-started.mdx b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/get-started.mdx
index 0a246972..d04e52b7 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/get-started.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/get-started.mdx
@@ -33,17 +33,20 @@ const seedPhrase = 'your twelve word seed phrase here'
const wallet = new WalletManagerTronGasfree(seedPhrase, {
chainId: 728126428,
provider: 'https://api.trongrid.io',
- gasFreeProvider: 'https://gasfree.provider.url',
- serviceProvider: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- verifyingContract: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
- gasFreeApiKey: 'your-gasfree-api-key', // Optional: provide with gasFreeApiSecret
- gasFreeApiSecret: 'your-gasfree-api-secret',
- transferMaxFee: 10000000 // Optional: maximum fee in token base units
+ gasFreeProvider: 'https://open.gasfree.io/tron/',
+ serviceProvider: 'REPLACE_WITH_PROVIDER_ADDRESS',
+ verifyingContract: 'TFFAMQLZybALaLb4uxHA9RBE7pxhUAjF3U'
})
```
If your GasFree provider does not require signed API requests, omit both `gasFreeApiKey` and `gasFreeApiSecret`. If you provide one, provide both.
+
+Never put `gasFreeApiSecret` in a browser, mobile, or distributed desktop application. The module signs requests in-process. If your provider requires authenticated requests, keep the credentials in a trusted backend and proxy the GasFree API calls.
+
+
+Retrieve `serviceProvider` from the provider's [`GET /api/v1/config/provider/all`](https://docs.gasfree.io/#get-apiv1configproviderall) response. Pass any transfer fee cap to `account.transfer()` for that transfer; the current runtime does not use a constructor-level `transferMaxFee` default.
+
**Secure the Seed Phrase:** You must securely store this seed phrase immediately. If it is lost, the user will permanently lose access to their funds.
diff --git a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/handle-errors.mdx b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/handle-errors.mdx
index d942e817..2561b760 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/handle-errors.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/handle-errors.mdx
@@ -17,6 +17,8 @@ try {
token: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // USDT
recipient: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
amount: 1000000
+ }, {
+ transferMaxFee: 1000n
})
console.log('Transfer successful:', result.hash)
console.log('Fee paid:', result.fee, 'token units')
@@ -51,7 +53,7 @@ try {
### Manage Fee Limits
-Set `transferMaxFee` when creating the wallet or per-transfer to prevent gas-free transfers from exceeding a maximum cost. You can retrieve current network rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference):
+Pass `transferMaxFee` in the second argument to each `account.transfer()` call that needs a cap. The constructor-level field is not used as a default by the current runtime. You can retrieve current network rates using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference):
```javascript title="Fee Management"
const feeRates = await wallet.getFeeRates()
diff --git a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/send-transactions.mdx
index 68538b53..d6e2f57d 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/send-transactions.mdx
@@ -7,6 +7,8 @@ schemaType: TechArticle
The Tron GasFree wallet module is for gas-free TRC20 token transfers. It does not support native TRX transaction execution or native transaction fee quotes.
+Although `transactionMaxFee` is present in the shared config type, it has no effect here because the native quote, sign, and send methods remain unsupported.
+
For gas-free TRC20 transfers, use [`transfer(options)`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#transferoptions) and [`quoteTransfer(options)`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#quotetransferoptions). If you need native TRX transactions, use the base `@tetherto/wdk-wallet-tron` module instead.
## Unsupported Methods
diff --git a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/transfer-tokens.mdx b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/transfer-tokens.mdx
index 21f312cf..3bbecac2 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/transfer-tokens.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron-gasfree/guides/transfer-tokens.mdx
@@ -22,7 +22,7 @@ console.log('Transfer fee:', transferResult.fee, 'token units')
console.log('Activation fee:', transferResult.activationFee, 'token units')
```
-The returned `fee` includes the GasFree transfer fee and, when the GasFree account is not active yet, the token activation fee returned by the provider. Use `activationFee` to show that activation portion separately; it is `0n` when no activation fee applies.
+The returned `fee` includes the GasFree transfer fee and, when the GasFree account is not active yet, the token activation fee returned by the provider. Both `fee` and `activationFee` are returned as `bigint`; `activationFee` is `0n` when no activation fee applies.
## Override Fee Limit
@@ -41,6 +41,8 @@ console.log('Transfer fee:', result.fee, 'token units')
console.log('Activation fee:', result.activationFee, 'token units')
```
+The call rejects only when the quoted total fee is greater than `transferMaxFee`; a fee equal to the cap is allowed. The constructor-level `transferMaxFee` field is not used as a default by the current runtime, so pass the cap on every transfer that needs one.
+
## Estimate Transfer Fees
You can get a fee estimate before executing the transfer using [`account.quoteTransfer()`](/sdk/wallet-modules/wallet-tron-gasfree/api-reference#quotetransferoptions):
diff --git a/content/docs/sdk/wallet-modules/wallet-tron/api-reference.mdx b/content/docs/sdk/wallet-modules/wallet-tron/api-reference.mdx
index 5e2b8346..e1fe3fa1 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron/api-reference.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron/api-reference.mdx
@@ -169,9 +169,9 @@ const account = new WalletAccountTron(seedPhrase, "0'/0/0", {
| `getAddress()` | Returns the account's Tron address | `Promise\` | - |
| `sign(message)` | Signs a message using the account's private key | `Promise\` | - |
| `verify(message, signature)` | Verifies a message signature | `Promise\` | - |
-| `signTransaction(tx)` | Signs a Tron transaction without broadcasting it | `Promise\` | If no provider, fee exceeds `transactionMaxFee`, or a pre-built transaction owner does not match the account |
-| `sendTransaction(tx)` | Sends a Tron transaction | `Promise\<{hash: string, fee: bigint, activationFee: bigint}\>` | If no provider, fee exceeds `transactionMaxFee`, or a pre-built transaction owner does not match the account |
-| `quoteSendTransaction(tx)` | Estimates the fee for a Tron transaction | `Promise\<{fee: bigint, activationFee: bigint}\>` | If no provider |
+| `signTransaction(tx)` | Signs a Tron transaction without broadcasting it | `Promise\` | If no provider, fee exceeds `transactionMaxFee`, or an unsigned pre-built transaction owner does not match the account |
+| `sendTransaction(tx)` | Builds and sends a transaction, or broadcasts a signed transaction | `Promise\<{hash: string, fee: bigint, activationFee: bigint}\>` | If no provider or fee exceeds `transactionMaxFee`; unsigned pre-built inputs also require a matching owner |
+| `quoteSendTransaction(tx)` | Estimates the fee for an unsigned or signed Tron transaction | `Promise\<{fee: bigint, activationFee: bigint}\>` | If no provider |
| `transfer(options)` | Transfers TRC20 tokens to another address | `Promise\<{hash: string, fee: bigint}\>` | If no provider or fee exceeds `transferMaxFee` |
| `quoteTransfer(options)` | Estimates the fee for a TRC20 transfer | `Promise\<{fee: bigint}\>` | If no provider |
| `getBalance()` | Returns the native TRX balance (in sun) | `Promise\` | If no provider |
@@ -227,7 +227,7 @@ Signs a Tron transaction and returns the signed transaction object. This method
**Parameters:**
- `tx` (TronTransaction): Native TRX transfer, smart-contract call descriptor, or pre-built TronWeb transaction
-**Returns:** `Promise\` - Signed Tron transaction object with a `signature` array
+**Returns:** `Promise\` - Signed Tron transaction object with a `signature` array
**Throws:**
- Error if no TronWeb provider is configured
@@ -248,14 +248,16 @@ console.log('Signed transaction:', signedTransaction)
Sends a Tron transaction and returns the result with hash, fee, and activation fee details.
**Parameters:**
-- `tx` (TronTransaction): Native TRX transfer, smart-contract call descriptor, or pre-built TronWeb transaction
+- `tx` (TronTransaction | TronSignedTransaction): Native TRX transfer, smart-contract call descriptor, pre-built unsigned TronWeb transaction, or signed transaction
+
+When `tx` has a signature, WDK quotes it again, enforces `transactionMaxFee`, and forwards the exact object to `tronWeb.trx.sendRawTransaction()`. It does not rebuild the transaction, refresh its reference block or expiration, re-sign it, or repeat the unsigned pre-built owner check.
**Returns:** `Promise\<{hash: string, fee: bigint, activationFee: bigint}\>` - Transaction hash, total fee in sun, and the portion used for account activation
**Throws:**
- Error if no TronWeb provider is configured
- Error if fee exceeds `transactionMaxFee` when configured
-- Error if a pre-built transaction is owned by a different account
+- Error if an unsigned pre-built transaction is owned by a different account
**Example:**
```javascript
@@ -272,7 +274,7 @@ console.log('Activation fee:', result.activationFee, 'sun')
Estimates the cost for a Tron transaction. Quotes include bandwidth for every transaction, energy for smart-contract execution, and activation fee for native transfers to inactive recipients.
**Parameters:**
-- `tx` (TronTransaction): The transaction object (same as sendTransaction)
+- `tx` (TronTransaction | TronSignedTransaction): An unsigned transaction input or signed transaction
**Returns:** `Promise\<{fee: bigint, activationFee: bigint}\>` - Fee estimate in sun and the portion used for account activation
@@ -583,16 +585,14 @@ interface TransactionResult {
}
```
-### SignedTransaction
+### TronSignedTransaction
```typescript
-interface SignedTransaction {
- txID: string; // Tron transaction ID
- signature: string[]; // Serialized secp256k1 signature
- [key: string]: unknown; // Other fields from the unsigned Tron transaction
-}
+type TronSignedTransaction = import('tronweb').Types.SignedTransaction
```
+The WDK package re-exports this TronWeb type under the exact name `TronSignedTransaction`.
+
### TronActivationFee
```typescript
diff --git a/content/docs/sdk/wallet-modules/wallet-tron/guides/send-transactions.mdx b/content/docs/sdk/wallet-modules/wallet-tron/guides/send-transactions.mdx
index 09980e2e..cf7cf9e7 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron/guides/send-transactions.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron/guides/send-transactions.mdx
@@ -5,7 +5,7 @@ docType: how-to
schemaType: TechArticle
---
-This guide explains how to [send native TRX](#send-native-trx), [send smart-contract calls](#send-smart-contract-calls), [send pre-built TronWeb transactions](#send-pre-built-tronweb-transactions), [estimate transaction fees](#estimate-transaction-fees), [cap transaction fees](#cap-transaction-fees), [sign without broadcasting](#sign-without-broadcasting), and [use dynamic fee rates](#use-dynamic-fee-rates).
+This guide explains how to [send native TRX](#send-native-trx), [send smart-contract calls](#send-smart-contract-calls), [send pre-built TronWeb transactions](#send-pre-built-tronweb-transactions), [estimate transaction fees](#estimate-transaction-fees), [cap transaction fees](#cap-transaction-fees), [sign without broadcasting](#sign-without-broadcasting), [quote and send a signed transaction](#quote-and-send-a-signed-transaction), and [use dynamic fee rates](#use-dynamic-fee-rates).
On Tron, values are expressed in sun (1 TRX = 1,000,000 sun).
@@ -65,7 +65,7 @@ const result = await account.sendTransaction(transaction)
console.log('Transaction hash:', result.hash)
```
-Before signing or sending a pre-built transaction, WDK checks that the transaction owner address matches the wallet account address.
+Before signing or sending an unsigned pre-built transaction, WDK checks that the transaction owner address matches the wallet account address.
## Estimate Transaction Fees
@@ -104,6 +104,27 @@ const signedTransaction = await account.signTransaction({
console.log('Signed transaction:', signedTransaction)
```
+## Quote and Send a Signed Transaction
+
+Pass the `TronSignedTransaction` returned by `signTransaction()` to the quote and send methods when review and submission are separate steps.
+
+```javascript title="Quote and Send a Signed Transaction"
+const signedTransaction = await account.signTransaction({
+ to: 'TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH',
+ value: 1000000
+})
+
+const quote = await account.quoteSendTransaction(signedTransaction)
+console.log('Estimated fee:', quote.fee, 'sun')
+
+const result = await account.sendTransaction(signedTransaction)
+console.log('Transaction hash:', result.hash)
+```
+
+
+WDK forwards a signed object unchanged and does not refresh its reference block or expiration, re-sign it, or repeat the unsigned owner-address check. Validate that the signed transaction belongs to the intended account and submit it while it remains valid. `sendTransaction()` quotes it again and enforces `transactionMaxFee`.
+
+
## Use Dynamic Fee Rates
You can retrieve current fee rates from the wallet manager using [`wallet.getFeeRates()`](/sdk/wallet-modules/wallet-tron/api-reference):
diff --git a/content/docs/sdk/wallet-modules/wallet-tron/index.mdx b/content/docs/sdk/wallet-modules/wallet-tron/index.mdx
index a7bfc647..54b57490 100644
--- a/content/docs/sdk/wallet-modules/wallet-tron/index.mdx
+++ b/content/docs/sdk/wallet-modules/wallet-tron/index.mdx
@@ -16,6 +16,7 @@ Use the TRON wallet module for standard TRON accounts where users can handle reg
- **Message Signing:** Sign and verify messages using Tron cryptography
- **Transaction Management**: Send transactions and get fee estimates, including activation fee details for native TRX sends
- **Arbitrary Transactions**: Quote, sign, and send smart-contract call descriptors or pre-built TronWeb transactions in addition to native TRX transfers
+- **Signed Transaction Relay**: Quote and broadcast the exact `TronSignedTransaction` returned by `signTransaction()`
- **TRC20 Support:** Query native TRX and TRC20 token balances.
- **TypeScript Support**: Full TypeScript definitions included
- **Memory Safety**: Secure private key management with automatic memory cleanup
diff --git a/content/docs/sdk/wallet-modules/which-wallet-module.mdx b/content/docs/sdk/wallet-modules/which-wallet-module.mdx
index f34b44e6..e6a5cb4b 100644
--- a/content/docs/sdk/wallet-modules/which-wallet-module.mdx
+++ b/content/docs/sdk/wallet-modules/which-wallet-module.mdx
@@ -26,6 +26,7 @@ Start with the chain your app needs to support. Then choose the account model:
| Gasfree TRON TRC20 transfers | [Gasfree TRON](/sdk/wallet-modules/wallet-tron-gasfree) |
| Standard Solana wallets | [Standard Solana](/sdk/wallet-modules/wallet-solana) |
| Gasless Solana transactions | [Gasless Solana](/sdk/wallet-modules/wallet-solana-gasless) |
+| Aptos wallets | [Aptos](/sdk/wallet-modules/wallet-aptos) |
| RGB assets on Bitcoin | [RGB](/sdk/community-modules/wdk-wallet-rgb) |
| Cosmos-compatible chains | [Cosmos](/sdk/community-modules/wdk-wallet-cosmos) |
diff --git a/content/docs/start-building/build-with-ai.mdx b/content/docs/start-building/build-with-ai.mdx
index 9639f017..ac63ff96 100644
--- a/content/docs/start-building/build-with-ai.mdx
+++ b/content/docs/start-building/build-with-ai.mdx
@@ -11,8 +11,11 @@ WDK documentation is optimized for AI coding assistants. Give your AI tool conte
Use this page as the starting point for building WDK apps with AI assistance.
+
+Run a ready-made local wallet CLI, daemon, and MCP server for AI agents
+
-Give AI agents access to WDK wallet tools through MCP
+Build a custom MCP server that exposes WDK wallet tools
Use WDK instructions with agentic coding tools
@@ -31,7 +34,7 @@ There are two practical ways to provide WDK context to your AI:
2. **[Add WDK project rules](#add-wdk-project-rules-optional)** - Give your AI assistant persistent context about package names, architecture, and coding patterns.
-**Want to give AI agents wallet access?** The [MCP Toolkit](/ai/mcp-toolkit) creates an MCP server that exposes WDK wallets as tools, letting AI agents check balances, send transactions, swap tokens, bridge assets, and more.
+**Want to give AI agents wallet access?** Use [WDK CLI](/cli/) for a ready-made local wallet CLI, daemon, and MCP server. Use the [MCP Toolkit](/ai/mcp-toolkit/) when you are building a custom MCP server in code.
---
@@ -65,7 +68,7 @@ Copy the rules content below and save it at the file path for your tool.
- Core module: `@tetherto/wdk`.
- Wallet modules follow the pattern: `@tetherto/wdk-wallet-`.
- Examples: `@tetherto/wdk-wallet-evm`, `@tetherto/wdk-wallet-btc`, `@tetherto/wdk-wallet-solana`, `@tetherto/wdk-wallet-ton`, `@tetherto/wdk-wallet-tron`, `@tetherto/wdk-wallet-spark`.
-- Specialized wallet modules: `@tetherto/wdk-wallet-evm-erc4337`, `@tetherto/wdk-wallet-ton-gasless`, `@tetherto/wdk-wallet-tron-gasfree`.
+- Specialized wallet modules: `@tetherto/wdk-wallet-evm-erc-4337`, `@tetherto/wdk-wallet-ton-gasless`, `@tetherto/wdk-wallet-tron-gasfree`.
- Protocol modules follow the pattern: `@tetherto/wdk-protocol---`.
- Examples: `@tetherto/wdk-protocol-swap-velora-evm`, `@tetherto/wdk-protocol-bridge-usdt0-evm`, `@tetherto/wdk-protocol-lending-aave-evm`.
diff --git a/content/docs/start-building/react-native-quickstart.mdx b/content/docs/start-building/react-native-quickstart.mdx
index 83b0561c..87a2cba1 100644
--- a/content/docs/start-building/react-native-quickstart.mdx
+++ b/content/docs/start-building/react-native-quickstart.mdx
@@ -125,9 +125,15 @@ Integrate WDK into your existing React Native or Expo project using `@tetherto/w
### Step 1: Install
```bash
-npm install @tetherto/wdk-react-native-core
+npm install @tetherto/wdk-react-native-core react-native-bare-kit
```
+
+The `v1.0.0-beta.15` npm artifact includes the React Native source entry at `src/index.ts`, but it does not include the declared default JavaScript or type declaration files under `dist/`. Use a React Native resolver that selects the package's `react-native` condition. Resolvers that select `default` or `types` target files that are not present in this artifact.
+
+
+Starting in beta.14, `react-native-bare-kit` is a peer dependency of React Native Core and must be installed explicitly.
+
### Step 2: Configure Android minSdkVersion
The library requires **Android API 29** or higher to support `react-native-bare-kit`.
@@ -175,11 +181,7 @@ buildscript {
### Step 3: Configure the Bundle
-The WDK engine runs inside a Bare worklet. You need to provide a bundle - choose one of two approaches:
-
-
-
-Use the `@tetherto/wdk-worklet-bundler` CLI to generate a bundle with only the modules you need:
+The WDK engine runs inside a Bare worklet. Use the `@tetherto/wdk-worklet-bundler` CLI to generate an HRPC bundle with only the modules you need:
```bash
# 1. Install the bundler CLI
@@ -206,23 +208,8 @@ import { bundle } from './.wdk'
**Which WDK modules do I need?** Each blockchain requires its own wallet module (e.g., `wdk-wallet-evm-erc-4337` for Ethereum/Polygon, `wdk-wallet-btc` for Bitcoin). See the full list of available modules in the [wdk-worklet-bundler documentation](https://github.com/tetherto/wdk-worklet-bundler).
-
-
-For quick prototyping, install and import the ready-made bundle from `@tetherto/pear-wrk-wdk`:
-
-```bash
-npm install @tetherto/pear-wrk-wdk
-```
-```typescript
-import { bundle } from '@tetherto/pear-wrk-wdk'
-```
-
-
-The pre-built bundle includes all blockchain modules, resulting in a larger bundle size. For production apps, generate a custom bundle with only the modules you need.
-
-
-
+`@tetherto/pear-wrk-wdk` provides worklet transport and runtime handlers; it does not export a pre-built WDK bundle.
### Step 4: Configure WDK Settings
@@ -501,7 +488,7 @@ Some native dependencies may lack type definitions. Add to your `tsconfig.json`:
**For Expo projects:**
-- Install `@tetherto/wdk-react-native-core`
+- Install `@tetherto/wdk-react-native-core` and its `react-native-bare-kit` peer dependency
- Configure Android minSdkVersion to 29 in `app.json`
- Set up bundle (custom or pre-built)
- Create `WdkConfigs` configuration
@@ -511,7 +498,7 @@ Some native dependencies may lack type definitions. Add to your `tsconfig.json`:
**For bare React Native:**
-- Install package
+- Install `@tetherto/wdk-react-native-core` and its `react-native-bare-kit` peer dependency
- Set minSdkVersion to 29 in `android/build.gradle`
- Set up bundle (custom or pre-built)
- Create `WdkConfigs` configuration
diff --git a/content/docs/tools/asset-registry/api-reference.mdx b/content/docs/tools/asset-registry/api-reference.mdx
index 6c7aac33..d6601921 100644
--- a/content/docs/tools/asset-registry/api-reference.mdx
+++ b/content/docs/tools/asset-registry/api-reference.mdx
@@ -91,7 +91,7 @@ const byOneOfMany = registry.getAsset([
])
```
-String comparisons are case-insensitive by default. Pass `{ caseSensitive: true }` to require exact casing.
+String comparisons in `getAsset()` are case-insensitive by default. Pass `{ caseSensitive: true }` to require exact casing.
## Class: `WdkTokenAssetRegistry`
@@ -110,16 +110,22 @@ const registry = new WdkTokenAssetRegistry(commonTokens)
| --- | --- | --- |
| `getTokens()` | Return all registered token assets | `TokenAsset[]` |
| `getTokenById(id)` | Return one token by exact ID | `TokenAsset \| null` |
-| `getTokenByAddress(address, options?)` | Return all tokens matching an address | `TokenAsset[]` |
+| `getTokenByAddress(address, options?)` | Return all tokens matching an address; exact casing is required by default | `TokenAsset[]` |
| `getTokenBySymbol(symbol, options?)` | Return all tokens matching a symbol | `TokenAsset[]` |
| `getTokenByChain(chainId, options?)` | Return all tokens matching a chain ID | `TokenAsset[]` |
```javascript title="Token Lookups"
const usdtTokens = registry.getTokenBySymbol('USDT')
const tokensByAddress = registry.getTokenByAddress('0xdAC17F958D2ee523a2206206994597C13D831ec7')
+const tokensByAddressIgnoringCase = registry.getTokenByAddress(
+ '0xdac17f958d2ee523a2206206994597c13d831ec7',
+ { caseSensitive: false }
+)
const chainTokens = registry.getTokenByChain('eip155:1')
```
+`getTokenByAddress()` is case-sensitive by default. Pass `{ caseSensitive: false }` when the registry and lookup input may use different casing. Symbol and chain lookups remain case-insensitive by default.
+
## Helper Functions
### `fromUniswapToken(token)`
@@ -179,6 +185,8 @@ type BaseAssetOptions = {
}
```
+The address helper accepts the same `{ caseSensitive?: boolean }` shape at runtime. The `TokenAddressLookupOptions` declaration is not re-exported from the package's top-level type entrypoint in `v1.0.0-beta.2`, so use an inline object when importing from the package root.
+
### `BaseAssetFilter`
```typescript
@@ -187,7 +195,7 @@ type BaseAssetFilter = Partial
## Bundled Assets
-`@tetherto/wdk-asset-registry/assets/common-tokens` exports bundled token metadata. In `v1.0.0-beta.1`, the published list contains USDT, USDT0, and XAUt token records across supported chains.
+`@tetherto/wdk-asset-registry/assets/common-tokens` exports bundled token metadata. In `v1.0.0-beta.2`, the published list contains USDT, USDT0, and XAUt token records across supported chains.
## Errors
diff --git a/content/docs/tools/asset-registry/configuration.mdx b/content/docs/tools/asset-registry/configuration.mdx
index 63e278be..a82095d0 100644
--- a/content/docs/tools/asset-registry/configuration.mdx
+++ b/content/docs/tools/asset-registry/configuration.mdx
@@ -62,11 +62,15 @@ registry.registerAsset(
## Look up tokens
-String lookups are case-insensitive by default. Pass `{ caseSensitive: true }` when casing must match exactly.
+Symbol and chain lookups are case-insensitive by default. Address lookups are case-sensitive by default in `v1.0.0-beta.2`; pass `{ caseSensitive: false }` only when the registry and lookup input may use different casing.
```javascript title="Lookup Tokens"
const bySymbol = registry.getTokenBySymbol('usdt')
const byAddress = registry.getTokenByAddress('0xdAC17F958D2ee523a2206206994597C13D831ec7')
+const byAddressIgnoringCase = registry.getTokenByAddress(
+ '0xdac17f958d2ee523a2206206994597c13d831ec7',
+ { caseSensitive: false }
+)
const byChain = registry.getTokenByChain('eip155:1')
const exactSymbol = registry.getTokenBySymbol('USDT', { caseSensitive: true })
```
diff --git a/content/docs/tools/create-wdk-module.mdx b/content/docs/tools/create-wdk-module.mdx
index e389edcb..e999739d 100644
--- a/content/docs/tools/create-wdk-module.mdx
+++ b/content/docs/tools/create-wdk-module.mdx
@@ -43,7 +43,7 @@ npm test
## Module Types
-The CLI supports all six WDK module categories. The generated package name follows WDK naming conventions automatically.
+The CLI supports seven WDK module categories. The generated package name follows WDK naming conventions automatically.
| Type | Description | Generated Package Example |
|------|-------------|---------------------------|
@@ -53,6 +53,7 @@ The CLI supports all six WDK module categories. The generated package name follo
| `lending` | DeFi lending protocol | `wdk-protocol-lending-compound-evm` |
| `fiat` | Fiat on/off-ramp provider | `wdk-protocol-fiat-moonpay` |
| `swidge` | Cross-chain swap (swap + bridge combined) | `wdk-protocol-swidge-lifi` |
+| `sda` | Smart Deposit Address provider | `wdk-protocol-sda-rhino` |
---
@@ -60,16 +61,20 @@ The CLI supports all six WDK module categories. The generated package name follo
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
-| `[type]` | | Module type (wallet, swap, bridge, lending, fiat, swidge) | (interactive prompt) |
+| `[type]` | | Module type (wallet, swap, bridge, lending, fiat, swidge, sda) | (interactive prompt) |
| `[name]` | | Module or protocol name | (interactive prompt) |
| `[blockchain]` | | Target blockchain | (interactive prompt) |
| `--scope \` | `-s` | npm scope (e.g., `@myorg`) | none |
| `--git` | | Initialize a git repository | `true` |
| `--no-git` | | Skip git initialization | |
| `--yes` | `-y` | Skip prompts, use defaults | `false` |
-| `--version` | `-v` | Show version | |
+| `--version` | `-V` | Show version | |
| `--help` | `-h` | Show help | |
+
+In `1.0.0-beta.3`, `--version` still prints the hard-coded beta.1 value. Use npm or the installed package metadata when you need the actual installed version.
+
+
### Examples
```bash title="Common CLI usage patterns"
@@ -79,6 +84,9 @@ npx @tetherto/create-wdk-module@latest wallet stellar --scope @myorg
# Create a swap protocol module
npx @tetherto/create-wdk-module@latest swap jupiter solana
+# Create an SDA provider module (provider name only; no blockchain argument)
+npx @tetherto/create-wdk-module@latest sda rhino
+
# Create with all defaults, no prompts
npx @tetherto/create-wdk-module@latest wallet stellar --yes
```
@@ -101,6 +109,7 @@ $ npx @tetherto/create-wdk-module@latest
Lending Module (DeFi lending protocol)
Fiat Module (fiat on/off-ramp)
Swidge Module (cross-chain swap (swidge = swap + bridge))
+ SDA Module (smart deposit address)
? What is the blockchain name? (e.g., "stellar", "solana")
> stellar
@@ -179,7 +188,7 @@ Wallet modules generate three core files that correspond to the WDK wallet archi
-
+
```text title="Protocol module project tree"
wdk-protocol-swap-jupiter-solana/
@@ -209,7 +218,11 @@ wdk-protocol-swap-jupiter-solana/
└── tsconfig.json
```
-Protocol modules (swap, bridge, lending, fiat, swidge) generate a single provider file rather than the three-file wallet pattern, since protocols interact with external services through a unified interface.
+Protocol modules (swap, bridge, lending, fiat, swidge, sda) generate a single provider file rather than the three-file wallet pattern, since protocols interact with external services through a unified interface. An SDA invocation such as `sda rhino` generates `wdk-protocol-sda-rhino` with `src/rhino-protocol.js`; it asks for a provider name rather than a blockchain.
+
+
+The beta.3 SDA scaffold is a starting point, not an operational provider. Implement the required `getSupportedRoutes()` and `createDepositAddress()` methods, replace the generated `test.todo` cases, and either implement or delete every optional method stub before publishing. Empty optional stubs override the base class and return `undefined`; deleting an unsupported optional override restores the base `UnsupportedOperationError` behavior. The generated `apiUrl` and `apiKey` fields are placeholders, so replace them with the provider's actual configuration and document custody, authentication, routes, output assets, expiry, and recovery behavior.
+
diff --git a/content/docs/tools/failover-provider/api-reference.mdx b/content/docs/tools/failover-provider/api-reference.mdx
index 6d4f3c6f..e37061fd 100644
--- a/content/docs/tools/failover-provider/api-reference.mdx
+++ b/content/docs/tools/failover-provider/api-reference.mdx
@@ -25,7 +25,7 @@ new FailoverProvider({
```
- `retries` (`number`, optional): Number of additional attempts after the first failure. Total attempts are `1 + retries`.
-- `shouldRetryOn` (`(error: Error) =\> boolean`, optional): Predicate that decides whether the proxy should switch to the next provider after a failure.
+- `shouldRetryOn` (`(error: unknown) =\> boolean`, optional): Predicate that decides whether the proxy should switch to the next provider after a failure.
#### Methods
@@ -73,13 +73,19 @@ The package exports the `FailoverProviderConfig` type through its top-level type
## Runtime behavior
-- `initialize()` returns a JavaScript `Proxy` over the first added provider.
+- `initialize()` returns a JavaScript `Proxy` with a neutral target whose property reads are delegated to the active provider.
- Non-function properties are forwarded from the currently active provider.
+- Getters and methods run with the underlying provider as `this`, so instance state and private fields remain available.
- If a property getter throws and `shouldRetryOn(error)` returns `true`, the runtime advances to the next provider before retrying the property access.
- If a synchronous method throws and `shouldRetryOn(error)` returns `true`, the runtime advances to the next provider and retries the method call.
- If an asynchronous method rejects and `shouldRetryOn(error)` returns `true`, the runtime advances to the next provider and retries the method call.
+- A result is treated as promise-like only when its `then` property is a function. An object with a non-function `then` property is returned synchronously.
- Provider switching is round-robin. If the active provider already changed while another call was failing, the runtime keeps the newer active provider instead of advancing twice.
+
+The proxy implements property reads only. Writes, enumeration, and property-descriptor operations are not forwarded to the active provider.
+
+
## Example
This example shows a transient failure on the first provider and a successful retry on the second provider:
@@ -101,7 +107,8 @@ const secondary = {
const provider = new FailoverProvider({
retries: 1,
- shouldRetryOn: (error) => error.message.includes('temporary')
+ shouldRetryOn: (error) =>
+ error instanceof Error && error.message.includes('temporary')
})
.addProvider(primary)
.addProvider(secondary)
diff --git a/content/docs/tools/failover-provider/configuration.mdx b/content/docs/tools/failover-provider/configuration.mdx
index a30cffaf..ef515a4d 100644
--- a/content/docs/tools/failover-provider/configuration.mdx
+++ b/content/docs/tools/failover-provider/configuration.mdx
@@ -36,7 +36,8 @@ const secondary = {
const provider = new FailoverProvider({
retries: 1,
- shouldRetryOn: (error) => error.message.includes('temporary')
+ shouldRetryOn: (error) =>
+ error instanceof Error && error.message.includes('temporary')
})
.addProvider(primary)
.addProvider(secondary)
@@ -58,7 +59,8 @@ Use a narrow predicate when you only want to retry transient failures:
```javascript title="Retry Only On Transient Errors"
const provider = new FailoverProvider({
retries: 2,
- shouldRetryOn: (error) => /timeout|temporary|429/.test(error.message)
+ shouldRetryOn: (error) =>
+ error instanceof Error && /timeout|temporary|429/.test(error.message)
})
```
@@ -97,10 +99,16 @@ factory.initialize()
## Runtime notes
- Non-function properties are read from the currently active provider.
+- Property getters and methods run with the underlying provider as `this`, including providers that use private fields or instance state.
- When a synchronous method throws and `shouldRetryOn(error)` returns `true`, the proxy switches to the next provider and retries.
- When an asynchronous method rejects and `shouldRetryOn(error)` returns `true`, the proxy switches to the next provider and retries.
+- A method result is treated as asynchronous only when its `then` property is a function. Other values, including objects with a non-function `then` property, are returned synchronously.
- Provider selection advances in round-robin order. If `retries` is larger than the number of providers, the runtime loops back through the list.
+
+Use the initialized proxy for property reads and method calls. Writes, enumeration, and property-descriptor operations apply to the proxy target rather than the active provider.
+
+
***
## Need Help?
diff --git a/content/docs/tools/failover-provider/index.mdx b/content/docs/tools/failover-provider/index.mdx
index 10419562..f8e6a069 100644
--- a/content/docs/tools/failover-provider/index.mdx
+++ b/content/docs/tools/failover-provider/index.mdx
@@ -12,7 +12,7 @@ Powered by [`@tetherto/wdk-failover-provider`](https://github.com/tetherto/wdk-f
- **Generic provider wrapper**: `FailoverProvider\` accepts any provider-like object type and returns a proxied `T` from `initialize()`.
- **Configurable retries**: Set `retries` to control how many additional attempts happen after the first failure.
- **Retry predicate**: Use `shouldRetryOn(error)` to decide which errors should advance to the next provider.
-- **Sync and async failover**: The runtime retries both synchronous throws and rejected promises.
+- **Sync and async failover**: The runtime retries synchronous throws and rejected promise-like results whose `then` property is a function.
- **Zero runtime dependencies**: The published package ships a single default export with no runtime dependency tree.
## Why this matters
@@ -21,6 +21,10 @@ Powered by [`@tetherto/wdk-failover-provider`](https://github.com/tetherto/wdk-f
- You can narrow failover behavior to transient errors instead of retrying every exception.
- You can add redundancy to read-heavy or submission-heavy flows without building your own retry proxy.
+
+The returned proxy forwards property reads and method calls. It does not forward writes, enumeration, or property-descriptor operations to the active provider.
+
+
Install the package, add provider candidates, and tune retry behavior.
diff --git a/content/docs/tools/p2p-address-book/api-reference.mdx b/content/docs/tools/p2p-address-book/api-reference.mdx
new file mode 100644
index 00000000..9b53262a
--- /dev/null
+++ b/content/docs/tools/p2p-address-book/api-reference.mdx
@@ -0,0 +1,132 @@
+---
+title: P2P Address Book API Reference
+description: Public API, records, configuration, and data semantics for @tetherto/wdk-p2p-address-book.
+docType: reference
+schemaType: APIReference
+icon: Code
+---
+
+## Exports
+
+```javascript
+import AddressBook, {
+ AddressBook as NamedAddressBook,
+ ADDRESS_TYPES
+} from '@tetherto/wdk-p2p-address-book'
+```
+
+The default and named `AddressBook` exports refer to the same class.
+
+## Construction
+
+| API | Description |
+|---|---|
+| `AddressBook.fromSeed(seed, corestore, options)` | Derives the book identity and encryption material from seed bytes and a namespace, then opens the local book. |
+| `new AddressBook(corestore, options?)` | Low-level constructor for advanced integrations. |
+| `AddressBook.createWorkletModule({ seed, config })` | Creates a worklet-compatible module with seed-isolated storage. |
+| `AddressBook.deriveAutobaseKey(keyPair, options?)` | Derives the public Autobase key from a bootstrap public key or key pair. |
+| `AddressBook.selectMirrors(autobaseKey, pool, n?)` | Deterministically selects up to `n` mirror keys from a pool. |
+
+Prefer `fromSeed()`. The low-level constructor permits replication without an encryption key; advanced callers must supply `encryptionKey` before opening or set `replicate: false`.
+
+### `fromSeed()` options
+
+| Option | Required | Behavior |
+|---|---|---|
+| `namespace` | Yes | Stable application scope for book identity, encryption derivation, and Corestore isolation. |
+| `replicate` | No | Defaults to `true`; set `false` for local-only use. |
+| `mirrors` | No | Blind-peer keys used for peering. Does not replace `addMirror()` during restoration. |
+| `timeout` | No | Bootstrap and enrollment wait. Defaults to 20 seconds. |
+| `name` | No | Label stored for the enrolling device writer. |
+| `bootstrap`, `swarm`, `relayThrough` | No | Advanced networking hooks whose concrete types are not declared stable. `close()` destroys a supplied swarm. |
+
+The declared low-level `optimistic` option is ignored in `1.0.0-beta.2`; Autobase optimistic mode is always enabled.
+
+## Lifecycle and identity
+
+| Surface | Description |
+|---|---|
+| `ready()` | Opens the local book. In beta.2, initial swarm flush and peering continue in the background. |
+| `create()` | Enrolls the local writer for a new book without attempting restoration. |
+| `close()` | Closes book-owned Autobase, peering, and swarm resources. |
+| `suspend()` / `resume()` | Suspends or resumes networking resources. |
+| `getInfo()` | Returns `{ autobaseKey, writable }`. |
+| `writerKey` | Current device writer key. |
+| `key` | Public Autobase key. |
+| `discoveryKey` | Discovery key used for replication. |
+| `writable` | Whether the current device writer is enrolled. |
+| `encryptionKey` | Content-encryption key. Treat it as secret. |
+
+## Contact and address records
+
+| Record | Required input | Optional input | Generated fields |
+|---|---|---|---|
+| Contact | `name` | `username` | `id`, `createdAt`, `updatedAt` |
+| Address | `address`, `type`, `network` | `label` | `id`, `contactId`, `createdAt`, `updatedAt` |
+
+Supported address types are:
+
+```javascript
+ADDRESS_TYPES.BITCOIN
+ADDRESS_TYPES.EVM
+ADDRESS_TYPES.TRON
+ADDRESS_TYPES.UMA
+ADDRESS_TYPES.LIGHTNING_ADDRESS
+ADDRESS_TYPES.LNURL
+ADDRESS_TYPES.SPARK
+```
+
+The package trims text fields and lowercases `network`, but it does not validate destination syntax. An address and normalized network pair must be unique; UMA values must also be unique across UMA records. Address comparisons are case-sensitive.
+
+Contact names and usernames do not need to be unique. The schema has no first-class email or custom-field support.
+
+## Contact and address methods
+
+| Area | Methods |
+|---|---|
+| Contacts | `addContact()`, `editContact()`, `deleteContact()`, `getContact()`, `listContacts()` |
+| Addresses | `addAddress()`, `editAddress()`, `deleteAddress()`, `listAddresses()` |
+| Query and events | `search()`, `on('update')`, `off('update')` |
+
+- `listContacts()` sorts by name. `listAddresses()` does not promise an order.
+- `search()` matches contact names, addresses, and address labels without case sensitivity. It does not search usernames, networks, or types.
+- Deleting a contact removes it and its currently visible addresses from the materialized view.
+- `update` events have no record payload. Refetch the required view.
+
+
+Conflicts follow Autobase's accepted operation order, not `updatedAt`. A later accepted contact update can recreate a deleted contact, and a delayed address operation can appear after its parent contact was deleted. The beta release has no tombstone or revision mechanism for preventing these outcomes.
+
+
+## Writer methods
+
+| Method | Description |
+|---|---|
+| `addWriter(data)` | Adds a writer key or writer record. |
+| `removeWriter(key)` | Removes current write membership. |
+| `listWriters(query?)` | Returns a HyperDB query for writer records. |
+| `getWriter(key)` | Returns a matching writer or `null`. |
+
+Writer-management methods are not included in the exported worklet bridge interface.
+
+## Mirror methods
+
+| Method | Description |
+|---|---|
+| `addMirror(key)` | Registers one mirror, peers with it, and enrolls a deferred local writer. Returns the selected key in an array. |
+| `addMirror(pool, n?)` | Selects up to `n` entries, then registers and peers with them. |
+| `listMirrors()` | Returns persisted mirror records. |
+| `removeMirror(key)` | Removes a persisted mirror record. See the beta limitation below. |
+
+
+In `1.0.0-beta.2`, `removeMirror()` leaves the key active in the current in-memory peering instance. Close and reopen the book without that key before treating peering as stopped. The method does not delete blocks already held by the server.
+
+
+## Worklet API
+
+The worklet bridge exposes `getInfo()`, `create()`, contact and address CRUD, `search()`, and mirror methods. Byte-array mirror keys are normalized to strings by the bridge. Writer-management methods and raw key properties are direct-API surfaces only.
+
+## Related guides
+
+- [Get Started](/tools/p2p-address-book/get-started)
+- [Sync and Recovery](/tools/p2p-address-book/sync-and-recovery)
+- [Production and Privacy](/tools/p2p-address-book/production-and-privacy)
diff --git a/content/docs/tools/p2p-address-book/get-started.mdx b/content/docs/tools/p2p-address-book/get-started.mdx
new file mode 100644
index 00000000..dea28e04
--- /dev/null
+++ b/content/docs/tools/p2p-address-book/get-started.mdx
@@ -0,0 +1,94 @@
+---
+title: P2P Address Book Get Started
+description: Install @tetherto/wdk-p2p-address-book and create a local wallet contact directory.
+docType: how-to
+schemaType: TechArticle
+icon: Rocket
+---
+
+This guide creates a local address book without peer replication. Start here before enrolling other devices or configuring blind peers.
+
+## Prerequisites
+
+You need:
+
+- Wallet seed bytes as a `Uint8Array`, not mnemonic text.
+- A stable application namespace.
+- A persistent writable Corestore directory dedicated to that wallet seed and device.
+- A Node.js ESM or tested Bare environment with native-module support.
+- Network access when replication is enabled.
+
+Install the Address Book and declare Corestore because application code constructs it directly:
+
+```bash title="Install P2P Address Book"
+npm install @tetherto/wdk-p2p-address-book corestore
+```
+
+## Create a local address book
+
+The following example disables networking, creates a new book, stores a caller-validated destination, reads the records, and closes both package-owned and caller-owned resources:
+
+```javascript title="local-address-book.js"
+import Corestore from 'corestore'
+import AddressBook, {
+ ADDRESS_TYPES
+} from '@tetherto/wdk-p2p-address-book'
+
+export async function createLocalBook(seed, storagePath, recipientAddress) {
+ const store = new Corestore(storagePath)
+ let book
+
+ try {
+ book = await AddressBook.fromSeed(seed, store, {
+ namespace: 'example-wallet',
+ replicate: false
+ })
+ await book.create()
+
+ const contact = await book.addContact({
+ name: 'Alice',
+ username: 'alice'
+ })
+
+ await book.addAddress(contact.id, {
+ address: recipientAddress,
+ type: ADDRESS_TYPES.EVM,
+ network: 'ethereum',
+ label: 'Primary wallet'
+ })
+
+ return {
+ contact: await book.getContact(contact.id),
+ addresses: await book.listAddresses(contact.id)
+ }
+ } finally {
+ try {
+ if (book) await book.close()
+ } finally {
+ await store.close()
+ }
+ }
+}
+```
+
+Use `create()` only when the book is known to be new. To join an existing book, follow [Sync and Recovery](/tools/p2p-address-book/sync-and-recovery) and call `addMirror()` instead.
+
+## Understand readiness
+
+`fromSeed()` opens the local Autobase before resolving. With replication enabled, it also joins the Hyperswarm discovery topic.
+
+
+In `1.0.0-beta.2`, the initial swarm flush and blind-peer setup continue in the background after `ready()` and `fromSeed()` resolve. Errors from that background startup are not surfaced to the caller. Do not treat either promise as proof that a peer is reachable or that replication has completed.
+
+
+`addMirror()` has stronger semantics for a device joining an existing book: it awaits peering, waits for existing genesis, and enrolls the local writer. It still does not prove that every record has been restored or retained remotely.
+
+## Close resources
+
+`book.close()` closes the book's networking and replication resources. The direct API does not close the root Corestore supplied by the application, so close that store separately as shown above.
+
+If you supply a custom swarm through configuration, `book.close()` destroys it. Do not share that swarm with another owner that expects to keep using it.
+
+## Next steps
+
+Continue with [Sync and Recovery](/tools/p2p-address-book/sync-and-recovery), or review exact record and method behavior in the [API Reference](/tools/p2p-address-book/api-reference).
diff --git a/content/docs/tools/p2p-address-book/index.mdx b/content/docs/tools/p2p-address-book/index.mdx
new file mode 100644
index 00000000..131a5ed6
--- /dev/null
+++ b/content/docs/tools/p2p-address-book/index.mdx
@@ -0,0 +1,92 @@
+---
+title: P2P Address Book
+description: Store and synchronize wallet contacts across a user's devices with encrypted peer-to-peer replication
+docType: explanation
+schemaType: TechArticle
+icon: BookUser
+---
+
+`@tetherto/wdk-p2p-address-book` gives a wallet application a user-specific directory for named contacts and payment destinations. It is an optional WDK utility, not a wallet implementation or a required part of every WDK application.
+
+Devices that use the same wallet seed and application namespace can open the same logical address book and enroll their own writers. Updates replicate between those devices through Holepunch networking. This is same-user device synchronization—not a public contact directory or a protocol for sharing contacts with other users.
+
+The recommended `fromSeed()` workflow encrypts replicated address-book blocks before they leave the device. A blind peer can retain those blocks for recovery without receiving the content key, but the wallet operator must still plan for peer availability, local storage protection, and key recovery.
+
+## When to use it
+
+Use the P2P Address Book when:
+
+- A wallet needs named recipients instead of repeated address entry.
+- The same user needs contacts on multiple devices.
+- The product wants cross-device sync without a central plaintext contacts database.
+- The team can operate or depend on the P2P availability needed for recovery.
+
+Choose a conventional contacts service when the product requires server-side plaintext search, moderation, analytics, or account-based recovery. This package is also a poor fit when its beta status, native dependencies, or eventual-synchronization model do not meet the product's requirements.
+
+The package stores destinations; it does not validate chain syntax, resolve identifiers, prove address ownership, or send payments. Validate a selected destination before using it in a wallet flow. See [WDK Utils](/tools/wdk-utils/) for available validators and parsers.
+
+## How it works
+
+1. The wallet opens a persistent Corestore and calls `AddressBook.fromSeed()` with wallet seed bytes and a stable namespace.
+2. The first device calls `create()` to enroll its device-specific writer.
+3. The wallet can register a blind-peer public key with `addMirror()` for offline availability.
+4. Another device uses the same seed and namespace, but its own Corestore, and calls `addMirror()` to restore the existing history and enroll its writer.
+5. Authorized writers append operations. Autobase produces the shared operation order and HyperDB exposes the current contacts and addresses.
+
+derived on each device"]
+ A["Device A Corestore + writer"]
+ B["Device B Corestore + writer"]
+ M["Blind peer encrypted replicated blocks no content key"]
+ U["Unrelated wallet different identity"]
+
+ I --> A
+ I --> B
+ A <-->|"Direct Hyperswarm replication"| B
+ A -->|"Mirror blocks"| M
+ M -->|"Restore history"| B
+ U -. "Cannot derive this book or content key" .-> M`}
+/>
+
+Random peers do not automatically store every address book. A blind peer is an explicitly configured availability peer.
+
+## Builder path
+
+
+
+Install the package and create a local address book with explicit resource cleanup.
+
+
+Enroll devices, configure blind peers, and understand recovery guarantees.
+
+
+Review public methods, record shapes, configuration, and data semantics.
+
+
+Plan availability, storage, privacy, deletion, and beta-version operations.
+
+
+
+## Related resources
+
+
+
+Review the released implementation, declarations, and tests.
+
+
+Validate supported destinations before storing or using them.
+
+
+Build advanced Bare worklet integrations for supported host applications.
+
+
+Set up the wider WDK runtime before adding optional tools.
+
+
+
+***
+
+## Need Help?
+
+
diff --git a/content/docs/tools/p2p-address-book/production-and-privacy.mdx b/content/docs/tools/p2p-address-book/production-and-privacy.mdx
new file mode 100644
index 00000000..fc1bd3d9
--- /dev/null
+++ b/content/docs/tools/p2p-address-book/production-and-privacy.mdx
@@ -0,0 +1,79 @@
+---
+title: P2P Address Book Production and Privacy
+description: Plan blind-peer availability, storage, privacy, deletion, and upgrades for the P2P Address Book.
+docType: explanation
+schemaType: TechArticle
+icon: ShieldCheck
+---
+
+P2P replication reduces central readable data custody. It does not eliminate infrastructure, operational responsibilities, or privacy risk.
+
+## Compare it with a CRUD backend
+
+| Area | P2P Address Book | Conventional CRUD backend |
+|---|---|---|
+| Primary storage | User devices and configured replicas | Application database |
+| Cross-device sync | Peer replication | Server API |
+| Blind-peer plaintext access | Blind-peer process is not given the content key; wallet process can read records | Usually available to the server unless separately encrypted |
+| Offline writes | Enrolled devices can append locally | Requires an application-specific offline queue |
+| Recovery | Correct keys plus an available history | Account authentication plus retained server data |
+| Server-side processing | Plaintext indexing is unavailable to a blind peer | Straightforward |
+| Operations | Peer availability, keys, local storage, and recovery | API, database, authentication, and backups |
+
+## Operate blind peers
+
+The package includes the blind-peer client integration, not the server. Deploy the separate [`blind-peer`](https://github.com/holepunchto/blind-peer) package and distribute its public key through trusted application configuration.
+
+- One blind-peer server can accept multiple cryptographically distinct address books, but they share its storage and garbage-collection capacity. The release tests recovery through one peer.
+- `addMirror(pool, n)` registers selected keys, but the underlying client contacts at most two configured peers for an Autobase request by default. Neither `n` nor `listMirrors()` is a confirmed replica count.
+- Pin and configure the server version you operate. In `blind-peer@3.12.1`, the GC-managed store defaults to 100 GB; address-book cores use priority 2 but can still be collected under storage pressure.
+- Monitor storage, garbage collection, requests, database health, and transports. These signals do not prove per-book durability or recovery completion.
+
+
+In `1.0.0-beta.2`, `removeMirror()` removes the persisted record but leaves the key active in the current in-memory instance. Close and reopen the book without that key before treating peering as stopped. The method also does not erase blocks already held by the blind peer.
+
+
+## Understand encryption boundaries
+
+`fromSeed()` supplies Autobase with a seed-derived content key before replication starts. Blind peers receive encrypted blocks, not that key. The wallet process and any device retaining the key can decrypt the book.
+
+Replication encryption is not local encryption at rest. Protect the Corestore directory as sensitive wallet data.
+
+A blind peer can withhold blocks or become unavailable, but signed Hypercore feeds prevent it from forging writer-authenticated history that clients accept.
+
+Blind-peer operators can still observe connection public keys and IP addresses, feed identifiers, timing, requested lengths, and approximate data volume. Logs, analytics, crash reports, screenshots, and backups can expose contact data independently of replication.
+
+## Plan deletion and recovery
+
+Contact deletion changes the current view; it does not erase append-only history or copies already held by devices and blind peers. The address-book API has no cross-replica physical-erasure or cryptographic-erasure workflow. Legal and privacy obligations depend on the complete product and deployment, not this package alone.
+
+The seed and namespace reproduce the book identity and keys, not the history. If every device and retained replica becomes unavailable, the contacts cannot be recovered from the seed.
+
+## Production checklist
+
+Before production:
+
+- Persist and protect each device's Corestore and wallet seed.
+- Pin compatible Address Book, Holepunch, and blind-peer versions.
+- Supervise blind-peer processes and monitor storage pressure, garbage collection, errors, and reachability.
+- Define replica count, retention, deletion, and backup procedures outside this package.
+- Test restoration with all original devices offline.
+- Test concurrent offline edits and upgrades against existing histories.
+- Validate destinations before using them in payment flows.
+
+The beta release publishes no package-level migration, downgrade, or cross-version replication guarantee.
+
+## Holepunch components
+
+| Component | Responsibility |
+|---|---|
+| [Corestore](https://github.com/holepunchto/corestore) | Persists device writers, replicated cores, the current view, and networking keys. |
+| [Hypercore](https://github.com/holepunchto/hypercore) | Provides signed append-only feeds for replicated operations. |
+| [Autobase](https://github.com/holepunchto/autobase) | Orders operations from authorized writers and encrypts replicated blocks when given the content key. |
+| [HyperDB](https://github.com/holepunchto/hyperdb) | Materializes contacts, addresses, writers, and mirror configuration for queries. |
+| [Hyperswarm](https://github.com/holepunchto/hyperswarm) | Discovers peers and carries direct replication connections. |
+| [`blind-peering`](https://github.com/holepunchto/blind-peering) | Requests background replication from configured blind peers. |
+
+## Next steps
+
+Return to [Sync and Recovery](/tools/p2p-address-book/sync-and-recovery) for device enrollment, or use the [API Reference](/tools/p2p-address-book/api-reference) while implementing lifecycle and storage handling.
diff --git a/content/docs/tools/p2p-address-book/sync-and-recovery.mdx b/content/docs/tools/p2p-address-book/sync-and-recovery.mdx
new file mode 100644
index 00000000..85989f51
--- /dev/null
+++ b/content/docs/tools/p2p-address-book/sync-and-recovery.mdx
@@ -0,0 +1,82 @@
+---
+title: P2P Address Book Sync and Recovery
+description: Enroll devices, configure blind peers, and recover an existing P2P Address Book.
+docType: how-to
+schemaType: TechArticle
+icon: RefreshCw
+---
+
+Devices that use the same seed and namespace derive the same book identity, content key, and bootstrap signing key. Each device stores a separate writer key in its own Corestore.
+
+## Choose the correct enrollment flow
+
+- Call `create()` only for a book that is known to be new.
+- Call `addMirror()` when an existing book may need to be restored.
+
+`addMirror()` waits for existing genesis before enrolling the local writer and does not create a fallback book on timeout. This avoids silently forking a missing book.
+
+## Register availability on the first device
+
+A blind peer is optional while two enrolled devices can reach each other directly. It becomes important when a new device must restore while every existing user device is offline.
+
+Create the new book before registering the mirror:
+
+```javascript
+const first = await AddressBook.fromSeed(seed, firstDeviceStore, {
+ namespace: 'example-wallet'
+})
+
+await first.create()
+await first.addMirror(mirrorKey)
+```
+
+The package contains the blind-peer client integration, not the server. See [Production and Privacy](/tools/p2p-address-book/production-and-privacy) before relying on a blind peer for recovery.
+
+## Restore another device
+
+Use the same seed and namespace with a different persistent Corestore. Do not call `create()`:
+
+```javascript
+const restored = await AddressBook.fromSeed(seed, secondDeviceStore, {
+ namespace: 'example-wallet',
+ timeout: 20_000
+})
+
+await restored.addMirror(mirrorKey)
+
+const refresh = () => restored
+ .listContacts()
+ .then(renderContacts)
+ .catch(reportError)
+
+restored.on('update', refresh)
+await refresh()
+```
+
+The `mirrors` constructor option configures peering but does not enroll a restored writer by itself. Call `addMirror()`.
+
+When `addMirror()` resolves, the device has observed existing genesis and enrolled its writer. Resolution does not confirm that every record has arrived or that the blind peer will retain the history. Refetch on payload-free `update` events, unregister listeners during cleanup, and close both the book and root Corestore.
+
+
+`ready()` and `fromSeed()` are not connectivity signals in `1.0.0-beta.2`. Initial swarm flush and blind-peer setup run in the background. Use the explicit `addMirror()` restore path and application-level recovery checks rather than assuming that local readiness means synchronization is complete.
+
+
+The seed and namespace can reproduce the book identity and keys, but not missing history. If all devices and retained replicas are unavailable, the contacts cannot be rebuilt from the seed alone.
+
+## Understand writer permissions
+
+Writer permissions are flat. Every enrolled writer can change contacts and addresses, and the direct API allows writers to add or remove other writers. There is no administrator role, read-only role, or invitation protocol.
+
+
+`removeWriter()` removes current write membership only. It does not rotate the seed-derived authority or content key, revoke read access, erase the removed device's data, or prevent a device holding the seed from enrolling again. Autobase also rejects removal of the final writer. Do not use this method as a complete compromised-device control.
+
+
+## Select mirrors from a pool
+
+`AddressBook.selectMirrors(autobaseKey, pool, n)` deterministically ranks mirror keys for a book. Passing the same pool to `addMirror(pool, n)` selects and registers the top entries.
+
+The returned selection is not proof of storage or availability. Test restoration with the original devices offline.
+
+## Next steps
+
+Review mirror and writer methods in the [API Reference](/tools/p2p-address-book/api-reference), then plan deployment and retention in [Production and Privacy](/tools/p2p-address-book/production-and-privacy).
diff --git a/content/docs/tools/pear-wrk-wdk/api-reference.mdx b/content/docs/tools/pear-wrk-wdk/api-reference.mdx
index 95783a77..9f2d9b1f 100644
--- a/content/docs/tools/pear-wrk-wdk/api-reference.mdx
+++ b/content/docs/tools/pear-wrk-wdk/api-reference.mdx
@@ -7,7 +7,7 @@ schemaType: APIReference
## Package: `@tetherto/pear-wrk-wdk`
-### Export: `HRPC`
+### Top-level export: `HRPC`
#### Command Methods
@@ -20,10 +20,12 @@ schemaType: APIReference
| `generateEntropyAndEncrypt()` | `generateEntropyAndEncrypt(args: WdkGenerateEntropyParams): Promise\` | Generates encrypted seed and entropy buffers inside the worklet. |
| `getMnemonicFromEntropy()` | `getMnemonicFromEntropy(args: WdkGetMnemonicParams): Promise\<{ mnemonic: string }\>` | Decrypts an encrypted entropy payload and returns the mnemonic. |
| `getSeedAndEntropyFromMnemonic()` | `getSeedAndEntropyFromMnemonic(args: { mnemonic: string }): Promise\` | Converts a mnemonic into encrypted seed and entropy buffers. |
-| `dispose()` | `dispose(args: DisposeRequest): void` | Disposes the worklet WDK instance. |
+| `dispose()` | `dispose(args: DisposeRequest): void` | Disposes the full worklet WDK instance or only selected blockchains. |
| `callMethod()` | `callMethod(args: CallMethodRequest): Promise\` | Looks up the target account and invokes one wallet or protocol method by name. |
| `registerWallet()` | `registerWallet(args: { config: string }): Promise\<{ status: string, blockchains: string }\>` | Dynamically registers additional wallets from a JSON config string. |
| `registerProtocol()` | `registerProtocol(args: { config: string }): Promise\<{ status: string }\>` | Dynamically registers additional protocols from a JSON config string. |
+| `callModule()` | `callModule(args: CallModuleRequest): Promise\` | Calls a method on a configured generic module over HRPC. |
+| `moduleEvent()` | `moduleEvent(args: ModuleEventRequest): void` | Sends a generic-module event from the worklet to the HRPC host. |
#### Handler Registration Methods
@@ -40,6 +42,8 @@ schemaType: APIReference
| `onCallMethod()` | `onCallMethod(responseFn): void` | Registers the server-side handler for `callMethod()`. |
| `onRegisterWallet()` | `onRegisterWallet(responseFn): void` | Registers the server-side handler for `registerWallet()`. |
| `onRegisterProtocol()` | `onRegisterProtocol(responseFn): void` | Registers the server-side handler for `registerProtocol()`. |
+| `onCallModule()` | `onCallModule(responseFn): void` | Registers the worklet-side handler for generic-module calls. |
+| `onModuleEvent()` | `onModuleEvent(responseFn): void` | Registers the host-side handler for generic-module events. |
#### `log`
@@ -65,13 +69,15 @@ Returns:
- `encryptedSeed?` (`string`): Base64-encoded encrypted seed buffer.
- `config` (`string`): JSON stringified `WdkWorkletConfig`.
-The handler requires `encryptionKey` and `encryptedSeed` to be passed together or omitted together. When a seeded WDK instance already exists, the runtime disposes it before re-registering the wallets and optional protocols in `config`.
+The handler requires `encryptionKey` and `encryptedSeed` to be passed together or omitted together. When a seeded WDK instance already exists, the runtime disposes it and closes its generic modules before re-registering the wallets and optional protocols in `config`.
+
+In beta.10, HRPC generic modules are constructed from `context.moduleManagers` and `config.modules` only when that request includes the encrypted seed pair. A seedless reinitialization closes existing module instances without reconstructing them. Supply both seed fields on every initialization that must construct or reconstruct modules.
#### `resetWdkWallets`
- `config` (`string`): JSON stringified object containing a `networks` map.
-The runtime validates `config.networks`, extracts each target `blockchain`, calls `wdk.dispose(targetChains)`, and re-registers only those wallet managers. This method does not re-register protocols.
+The runtime validates `config.networks`, extracts each target `blockchain`, calls `wdk.dispose(targetChains)`, and re-registers only those wallet managers. This method does not re-register protocols or close generic modules; existing module instances keep running.
#### `generateEntropyAndEncrypt`
@@ -104,7 +110,9 @@ Returns:
#### `dispose`
-- `args` (`DisposeRequest`): Empty request object.
+- `args` (`DisposeRequest`): Optional `blockchains` array. Omit it or pass an empty array for a full disposal.
+
+A full disposal closes all generic modules and clears the WDK instance. A non-empty `blockchains` array disposes only those wallets and leaves generic modules running.
#### `callMethod`
@@ -114,7 +122,7 @@ Returns:
- `args?` (`string`): JSON string of the method arguments.
- `options?` (`string`): JSON string of `CallMethodOptions`.
-`options.protocolType` may be `swap`, `bridge`, `lending`, or `fiat`. When present, the runtime resolves the protocol-specific account wrapper before invoking `methodName`.
+`options.protocolType` may be `swap`, `swidge`, `bridge`, `lending`, or `fiat`. When present, the runtime requires a non-empty `options.protocolName` and resolves the protocol-specific account wrapper before invoking `methodName`. Swidge calls resolve the wrapper with `account.getSwidgeProtocol(protocolName)`.
#### `registerWallet`
@@ -133,6 +141,24 @@ Returns:
- `status` (`string`)
+#### `callModule`
+
+Call one method on a configured generic module. This command is available on HRPC only.
+
+- `module` (`string`): Module name shared by `RpcContext.moduleManagers` and `WdkWorkletConfig.modules`.
+- `method` (`string`): Non-empty method name on the constructed module instance.
+- `args?` (`string`): Optional JSON string of arguments. Arrays are spread as positional arguments; a non-array value is passed as one argument.
+
+Returns `CallModuleResponse` with optional `result`, a JSON string. The runtime awaits promises, materializes values with `.toArray()`, and recursively converts `Uint8Array` values to hex before serialization.
+
+#### `moduleEvent`
+
+Send an HRPC module event to the host.
+
+- `module` (`string`): Module name.
+- `event` (`string`): Event name.
+- `payload?` (`string | null`): Optional JSON string payload.
+
#### `onLog`
Registers the server-side handler used to service `log()` requests.
@@ -177,12 +203,20 @@ Registers the server-side handler used to service `registerWallet()` requests.
Registers the server-side handler used to service `registerProtocol()` requests.
-### Export: `registerRpcHandlers(rpc, context)`
+#### `onCallModule`
+
+Registers the worklet-side handler used to service `callModule()` requests.
+
+#### `onModuleEvent`
+
+Registers the host-side handler used to receive `moduleEvent()` messages.
-Registers the package's server-side handlers on the provided RPC instance.
+### Worklet export: `registerRpcHandlers(rpc, context)`
+
+Import this helper from `@tetherto/pear-wrk-wdk/worklet`. It registers the package's server-side handlers on the provided RPC instance.
- `rpc` (`any`): RPC server instance that supports the generated handler registration methods.
-- `context` (`RpcContext`): Runtime context containing `wdk`, `WDK`, `walletManagers`, `protocolManagers`, and `wdkLoadError`.
+- `context` (`RpcContext`): Runtime context containing `wdk`, `WDK`, `walletManagers`, `protocolManagers`, and `wdkLoadError`. HRPC generic modules can additionally supply `moduleManagers` and `capabilities`; the runtime manages `moduleRuntime` and `moduleInstances`.
### Types
@@ -203,6 +237,47 @@ interface WdkWorkletConfig {
config: unknown
}
}
+ modules?: {
+ [moduleName: string]: Record
+ }
+}
+```
+
+The `modules` map contains runtime module configuration. Its names must match the module managers generated by Worklet Bundler or supplied manually in `RpcContext`.
+
+#### `WdkModuleManager`
+
+```ts
+interface WdkModuleManager {
+ events?: string[]
+ createModule: (context: {
+ seed: any
+ config: any
+ capabilities: Record
+ emit: (event: string, payload?: any) => void
+ }) => any | Promise
+}
+```
+
+The factory must consume `seed` synchronously rather than retain it. Module instances can optionally implement `close()`, `suspend()`, and `resume()`. The runtime calls `close()` during full disposal or reinitialization; targeted blockchain disposal and `resetWdkWallets()` leave generic modules running. Worklet Bundler-generated HRPC entrypoints forward Bare lifecycle events to `suspend()` and `resume()`; manual Pear integrations must wire those events themselves. Declared `events` are forwarded from the instance, and the injected `emit()` function can emit events directly.
+
+#### Module request types
+
+```ts
+interface CallModuleRequest {
+ module: string
+ method: string
+ args?: string
+}
+
+interface CallModuleResponse {
+ result?: string | null
+}
+
+interface ModuleEventRequest {
+ module: string
+ event: string
+ payload?: string | null
}
```
@@ -217,14 +292,60 @@ interface WdkResetWalletParams {
#### `CallMethodOptions`
```ts
+enum ProtocolType {
+ SWAP = 'swap',
+ SWIDGE = 'swidge',
+ BRIDGE = 'bridge',
+ LENDING = 'lending',
+ FIAT = 'fiat'
+}
+
interface CallMethodOptions {
transformResult: Function
defaultValue: any
- protocolType: 'swap' | 'bridge' | 'lending' | 'fiat'
+ protocolType: ProtocolType
protocolName: string
}
```
+The published declarations include `ProtocolType`, but the top-level JavaScript entry does not export that enum value at runtime. Pass the corresponding string literal, such as `'swidge'`, in serialized request options.
+
+The published `CallMethodOptions` declaration marks every field as required. The request's `options` string remains optional at runtime, and the handler reads fields only when their behavior is used.
+
+## JSON-RPC Transport
+
+Import `registerJsonRpcHandlers()` from the separate JSON-RPC entrypoint:
+
+```javascript
+const { registerJsonRpcHandlers } = require('@tetherto/pear-wrk-wdk/jsonrpc')
+
+registerJsonRpcHandlers(ipc, context)
+```
+
+The server reads UTF-8 JSON-RPC 2.0 messages framed with a four-byte unsigned big-endian payload length. Every request requires an ID, and an ID cannot be reused while its earlier request is still in flight. Malformed frames are dropped without a response. The package does not export a JSON-RPC client or native-host helper.
+
+Beta.10 supports these JSON-RPC method names:
+
+- `workletStart`
+- `generateEntropyAndEncrypt`
+- `getMnemonicFromEntropy`
+- `getSeedAndEntropyFromMnemonic`
+- `initializeWDK`
+- `callMethod`
+- `registerWallet`
+- `registerProtocol`
+- `dispose`
+
+JSON-RPC does not support `resetWdkWallets`, generic-module calls, or module events in this release. Use HRPC for those operations.
+
+The HRPC and JSON-RPC transports share the `callMethod` handler, so both support the `swidge` protocol type in beta.10.
+
+
+INFO-level logging can include wallet-call arguments and JSON-RPC parameters or results. Production defaults to ERROR logging; do not enable more verbose logging for requests that may contain seeds, mnemonics, keys, or other sensitive values.
+
+
+Mnemonic strings, encryption-key strings, and encrypted payload strings cannot be zeroed in JavaScript. Discard references promptly and never log them. The runtime validates imported mnemonics for 12 or 24 English BIP-39 words and clears temporary byte buffers where possible.
+
***
## Need Help?
diff --git a/content/docs/tools/pear-wrk-wdk/configuration.mdx b/content/docs/tools/pear-wrk-wdk/configuration.mdx
index e453986f..7d6b44f3 100644
--- a/content/docs/tools/pear-wrk-wdk/configuration.mdx
+++ b/content/docs/tools/pear-wrk-wdk/configuration.mdx
@@ -1,33 +1,69 @@
---
title: Pear Worklet WDK Configuration
-description: Configure the Bare worklet context and the JSON payloads passed to initializeWDK and resetWdkWallets
+description: Configure Pear Worklet HRPC and JSON-RPC contexts, WDK payloads, and generic modules
---
-This page explains how to [build the worklet context](#worklet-context), [shape the worklet config payload](#worklet-config-payload), [initialize-wdk](#initialize-wdk), and [reset-selected-wallets](#reset-selected-wallets).
+This page explains how to [build the worklet context](#worklet-context), [shape the worklet config payload](#worklet-config-payload), [initialize WDK](#initialize-wdk), [call generic modules](#call-generic-module-methods), and [choose a transport](#json-rpc-transport).
## Worklet Context
You can bind the shipped RPC handlers to your Bare worklet using `registerRpcHandlers()`:
```javascript title="Register RPC Handlers"
+require('bare-node-runtime/global')
+
const { registerRpcHandlers } = require('@tetherto/pear-wrk-wdk/worklet')
-const { WDK } = require('@tetherto/wdk')
-const EvmWalletManager = require('@tetherto/wdk-wallet-evm')
-const SparkWalletManager = require('@tetherto/wdk-wallet-spark')
+const wdkModule = require('@tetherto/wdk', { with: { imports: 'bare-node-runtime/imports' } })
+const { createModule: createPreferencesModule } = require('@your-org/wdk-module-preferences')
+
+const WDK = wdkModule.default || wdkModule.WDK || wdkModule
+const walletCache = {}
+
+function loadWalletManager(network) {
+ if (walletCache[network]) return walletCache[network]
+
+ let walletModule
+ if (network === 'ethereum') {
+ walletModule = require('@tetherto/wdk-wallet-evm', { with: { imports: 'bare-node-runtime/imports' } })
+ }
+ if (network === 'spark') {
+ walletModule = require('@tetherto/wdk-wallet-spark', { with: { imports: 'bare-node-runtime/imports' } })
+ }
+
+ if (walletModule) walletCache[network] = walletModule.default || walletModule
+ return walletCache[network] || null
+}
+
+const walletManagers = new Proxy({}, {
+ get: (_, network) => loadWalletManager(network),
+ has: (_, network) => ['ethereum', 'spark'].includes(network)
+})
const context = {
wdk: null,
WDK,
- walletManagers: {
- ethereum: EvmWalletManager,
- spark: SparkWalletManager
- },
+ walletManagers,
protocolManagers: {},
+ moduleManagers: {
+ preferences: {
+ createModule: createPreferencesModule,
+ events: ['changed']
+ }
+ },
+ capabilities: {},
wdkLoadError: null
}
module.exports = (rpc) => {
registerRpcHandlers(rpc, context)
+
+ Bare.on('suspend', async () => {
+ await context.moduleRuntime?.suspendAll()
+ })
+
+ Bare.on('resume', async () => {
+ await context.moduleRuntime?.resumeAll()
+ })
}
```
@@ -39,11 +75,13 @@ module.exports = (rpc) => {
- `protocolManagers`: A map from protocol name to protocol manager implementation.
- `wdkLoadError`: Any startup error captured while loading WDK. Use `null` when there is no load failure.
+For HRPC generic modules, `moduleManagers` optionally maps module names to `{ createModule, events? }`. The factory receives `{ seed, config, capabilities, emit }` and can return an instance or a promise. `capabilities` is an optional host-supplied object and is empty by default. The runtime manages `moduleRuntime` and `moduleInstances`; do not initialize those fields yourself. Manual integrations must forward Bare `suspend` and `resume` events as shown if module instances should receive those lifecycle calls. Worklet Bundler-generated HRPC entrypoints wire them automatically.
+
## Worklet Config Payload
Both [`initializeWDK()`](/tools/pear-wrk-wdk/api-reference) and [`resetWdkWallets()`](/tools/pear-wrk-wdk/api-reference) expect `config` to be a JSON string. The decoded object must contain at least one entry under `networks`.
-```javascript title="Minimal Worklet Config JSON"
+```javascript title="Worklet Config JSON"
const workletConfig = {
networks: {
ethereum: {
@@ -61,6 +99,11 @@ const workletConfig = {
environment: 'sandbox'
}
}
+ },
+ modules: {
+ preferences: {
+ storagePath: '/app-data/preferences'
+ }
}
}
```
@@ -70,8 +113,13 @@ const workletConfig = {
- `networks` is required and must contain at least one network entry.
- Each network entry must include `blockchain` and an object `config`.
- `protocols` is optional during initialization.
+- `modules` is optional and contains runtime config for named generic modules. Each key must match a `moduleManagers` key in the HRPC context and the corresponding build-time Worklet Bundler module name.
- `resetWdkWallets()` reads only the `networks` portion of the decoded config.
+
+Generic modules are HRPC-only in beta.10. The JSON-RPC handler does not construct modules or expose module calls and events.
+
+
## Initialize WDK
You can create and register the WDK instance inside the worklet using [`initializeWDK()`](/tools/pear-wrk-wdk/api-reference):
@@ -92,7 +140,9 @@ await hrpc.initializeWDK({
- Pass both `encryptionKey` and `encryptedSeed`, or omit both together.
- On first initialization, the worklet must receive an encrypted seed pair so it can create `context.wdk`.
-- If `context.wdk` already exists, a later `initializeWDK()` call disposes the existing instance before re-registering wallets and protocols from the new config.
+- If `context.wdk` already exists, a later `initializeWDK()` call disposes the existing instance and closes its generic modules before re-registering wallets and protocols from the new config.
+- In beta.10, generic modules are constructed only when that `initializeWDK()` request includes both `encryptionKey` and `encryptedSeed`. A seedless reinitialization closes existing module instances but does not rebuild them, even when `config.modules` is present. Supply the seed pair on every initialization that must construct or reconstruct modules.
+- Module `close()` is called during full disposal or reinitialization. Targeted blockchain disposal leaves generic modules running. Optional `suspend()` and `resume()` methods run only when the host forwards Bare lifecycle events; the manual context above and Worklet Bundler-generated HRPC entrypoints do so.
## Reset Selected Wallets
@@ -119,8 +169,9 @@ await hrpc.resetWdkWallets({
- The handler calls `wdk.dispose(targetChains)` with the blockchains extracted from `config.networks`.
- Only wallets listed in the request `networks` object are re-registered.
- The reset flow does not re-register protocols.
+- The reset flow does not close or reconstruct generic modules; existing module instances keep running.
-## Call Wallet Methods
+## Call Wallet and Protocol Methods
You can execute wallet account methods through [`callMethod()`](/tools/pear-wrk-wdk/api-reference):
@@ -138,6 +189,54 @@ const result = await hrpc.callMethod({
- `options` is optional and must be a JSON string when provided.
- When `args` decodes to an array, the handler spreads the values as positional method arguments.
- When `args` decodes to an object or primitive, the handler passes it as a single argument.
+- Set `options.protocolType` to `swap`, `swidge`, `bridge`, `lending`, or `fiat` to call a protocol wrapper. Every protocol call requires a non-empty `options.protocolName`.
+
+## Call Generic Module Methods
+
+On an HRPC worklet configured with matching `moduleManagers` and runtime `modules`, call a module method by name:
+
+```javascript title="Call A Generic Module"
+const response = await hrpc.callModule({
+ module: 'preferences',
+ method: 'getTheme',
+ args: JSON.stringify([])
+})
+
+const theme = response.result ? JSON.parse(response.result) : undefined
+```
+
+`args` is an optional JSON string. Arrays are spread into positional arguments; a non-array value is passed as one argument. Promise results are awaited, `.toArray()` results are materialized, and `Uint8Array` values are normalized to hex before the response is serialized.
+
+Subscribe to events declared by the module manager:
+
+```javascript title="Receive A Module Event"
+hrpc.onModuleEvent(({ module, event, payload }) => {
+ if (module === 'preferences' && event === 'changed') {
+ const value = payload ? JSON.parse(payload) : undefined
+ console.log('Preferences changed:', value)
+ }
+})
+```
+
+## JSON-RPC Transport
+
+Native hosts can register the separate framed JSON-RPC server entrypoint:
+
+```javascript title="Register JSON-RPC Handlers"
+const { registerJsonRpcHandlers } = require('@tetherto/pear-wrk-wdk/jsonrpc')
+
+module.exports = (ipc) => {
+ registerJsonRpcHandlers(ipc, context)
+}
+```
+
+Messages are UTF-8 JSON-RPC 2.0 objects prefixed by a four-byte unsigned big-endian payload length. Requests require an ID, and IDs must be unique while a request is in flight. The package exports no JSON-RPC host/client helper; the native host must implement framing and correlation.
+
+JSON-RPC beta.10 supports WDK initialization and disposal, secret/mnemonic operations, wallet and protocol calls, and dynamic wallet/protocol registration. Its shared `callMethod` handler supports the `swidge` protocol type. It does not support `resetWdkWallets`, `callModule`, or module events. See the [API reference](/tools/pear-wrk-wdk/api-reference#json-rpc-transport) for the exact method list.
+
+
+INFO-level logs can contain wallet-call arguments and JSON-RPC parameters or results. Keep production logging at its default ERROR level when requests may contain sensitive values.
+
***
diff --git a/content/docs/tools/pear-wrk-wdk/index.mdx b/content/docs/tools/pear-wrk-wdk/index.mdx
index b88842e1..be68773d 100644
--- a/content/docs/tools/pear-wrk-wdk/index.mdx
+++ b/content/docs/tools/pear-wrk-wdk/index.mdx
@@ -1,9 +1,9 @@
---
title: Pear Worklet WDK
-description: Low-level HRPC infrastructure for running WDK inside a Bare worklet
+description: HRPC and framed JSON-RPC infrastructure for running WDK inside a Bare worklet
---
-Pear Worklet WDK is the low-level transport and handler layer for running WDK inside a Bare worklet. It provides the `HRPC` client, the `registerRpcHandlers()` server helper, and the typed request payloads needed to initialize WDK, call wallet methods, and reset selected wallet modules from the host app.
+Pear Worklet WDK is the low-level transport and handler layer for running WDK inside a Bare worklet. It provides an `HRPC` client and server helper, a separate framed JSON-RPC server entrypoint for native hosts, and typed request payloads for WDK, wallet, protocol, and HRPC-only generic-module operations.
Powered by `@tetherto/pear-wrk-wdk`.
@@ -12,7 +12,9 @@ Powered by `@tetherto/pear-wrk-wdk`.
- **Bare worklet bridge**: Connect a host app to a worklet through the shipped `HRPC` client and generated HRPC schema
- **Typed lifecycle requests**: Initialize WDK with `initializeWDK({ config, encryptionKey, encryptedSeed })` and tear it down with `dispose()`
- **Selective wallet resets**: Re-register only the wallet modules listed in a new worklet `networks` config using `resetWdkWallets({ config })`
-- **Generic wallet method calls**: Call account methods through `callMethod({ methodName, network, accountIndex, args, options })`
+- **Generic wallet and protocol calls**: Call account methods through `callMethod({ methodName, network, accountIndex, args, options })`, including Swidge protocols by name
+- **HRPC generic modules**: Construct named modules during WDK initialization, call their methods with `callModule()`, and forward declared events to the host
+- **Framed JSON-RPC server**: Register a length-prefixed JSON-RPC 2.0 server through `@tetherto/pear-wrk-wdk/jsonrpc`
- **Dynamic registration hooks**: Register additional wallets or protocols with `registerWallet()` and `registerProtocol()`
## Why this matters
@@ -20,14 +22,23 @@ Powered by `@tetherto/pear-wrk-wdk`.
- You can keep WDK state and signing operations off the main thread in Bare-based apps
- You can reconfigure selected wallet modules without fully disposing the worklet
- You can use one transport layer across custom mobile, desktop, or embedded Bare integrations
+- You can host the same wallet and protocol handlers behind HRPC or a native JSON-RPC bridge, within each transport's supported method set
Use this package when you need direct control over the worklet host and RPC layer. If you want generated worklet entry files instead, start with [`@tetherto/wdk-worklet-bundler`](https://github.com/tetherto/wdk-worklet-bundler).
+
+The top-level package exports the `HRPC` runtime value. Import `registerRpcHandlers()` from `@tetherto/pear-wrk-wdk/worklet` and `registerJsonRpcHandlers()` from `@tetherto/pear-wrk-wdk/jsonrpc`.
+
+
+
+Generic-module calls and events are HRPC-only in `v1.0.0-beta.10`; the JSON-RPC handler does not expose them. This package also does not export a pre-built WDK bundle.
+
+
-Build the worklet context and pass the JSON config payloads used by initialize and reset requests
+Build HRPC or JSON-RPC contexts and configure WDK, wallet, protocol, and generic-module initialization
Review the exported class, server helper, and request shapes
diff --git a/content/docs/tools/react-native-core/api-reference.mdx b/content/docs/tools/react-native-core/api-reference.mdx
index c1d42709..bda64376 100644
--- a/content/docs/tools/react-native-core/api-reference.mdx
+++ b/content/docs/tools/react-native-core/api-reference.mdx
@@ -12,6 +12,8 @@ schemaType: APIReference
| [`useWalletManager`](#usewalletmanager) | Hook | Wallet lifecycle (create, restore, lock, unlock) |
| [`useAccount`](#useaccount) | Hook | Account operations (send, sign, verify, estimateFee) |
| [`useProtocol`](#useprotocol) | Hook | Protocol method calls through the active worklet account |
+| [`useModule`](#usemodule) | Hook | Named generic-module calls and event subscriptions |
+| [`ModuleService`](#moduleservice) | Class | Low-level static gateway for generic-module calls and events |
| [`useAddresses`](#useaddresses) | Hook | Load and query wallet addresses |
| [`useBalance`](#usebalance) | Hook | Single asset balance with TanStack Query |
| [`useBalancesForWallet`](#usebalancesforwallet) | Hook | Bulk balance fetch for multiple assets |
@@ -25,6 +27,8 @@ schemaType: APIReference
| `UseAccountParams` | Type | Parameters for `useAccount` |
| `UseAccountReturn` | Type | Return type of `useAccount` |
| `UseProtocolParams` | Type | Parameters for `useProtocol` |
+| `UseModuleProxy` | Type | Typed asynchronous module proxy returned by `useModule` |
+| `ModuleEventListener` | Type | Listener for parsed module-event payloads |
| `UseAddressesReturn` | Type | Return type of `useAddresses` |
| `UseWalletManagerResult` | Type | Return type of `useWalletManager` |
@@ -335,7 +339,7 @@ Hook to call configured protocol modules through the active WDK worklet account.
|-----------|------|-------------|
| `params.network` | `string` | Network identifier configured in `wdkConfigs` |
| `params.accountIndex` | `number` | Account index to use for the protocol call |
-| `params.protocolType` | `'bridge' \| 'swap' \| 'lending' \| 'fiat'` | Protocol category |
+| `params.protocolType` | `'bridge' \| 'swap' \| 'swidge' \| 'lending' \| 'fiat'` | Protocol category |
| `params.protocolName` | `string` | Registered protocol name |
### Returns
@@ -344,6 +348,8 @@ Returns `T`, a proxy for the protocol methods you type with the hook generic. Ea
If no active account is available, protocol method calls log an error and return `undefined`.
+The `swidge` protocol type is available in `v1.0.0-beta.15`. It requires a non-empty `protocolName` and forwards calls through the matching Swidge protocol wrapper on the selected account.
+
### Example
```tsx
@@ -381,6 +387,76 @@ function SwapQuoteScreen() {
---
+## useModule
+
+Hook that creates a typed proxy for one named generic module in the active HRPC worklet. Every method access is forwarded through `ModuleService.callModule()`. The proxy also exposes `on(event, listener)` for module events.
+
+
+React Native Core `v1.0.0-beta.14` and later pin a Pear Worklet version with the HRPC methods required by this API.
+
+
+### Parameters
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `moduleName` | `string` | Name shared by the bundler's build-time `modules` map and runtime `wdkConfigs.modules` map |
+
+### Returns
+
+Returns `UseModuleProxy`. Define module methods as asynchronous in `T` because every proxy call crosses the worklet boundary and returns a promise.
+
+```typescript title="UseModuleProxy"
+type UseModuleProxy = T & {
+ on: (event: string, listener: ModuleEventListener) => () => void
+}
+
+type ModuleEventListener = (payload: unknown) => void
+```
+
+### Example
+
+```tsx title="Call A Named Module"
+import { useEffect } from 'react'
+import { useModule } from '@tetherto/wdk-react-native-core'
+
+type PreferencesModule = {
+ getTheme: () => Promise<'light' | 'dark'>
+ setTheme: (theme: 'light' | 'dark') => Promise
+}
+
+function PreferencesScreen() {
+ const preferences = useModule('preferences')
+
+ useEffect(() => {
+ return preferences.on('changed', payload => {
+ console.log('Preferences changed:', payload)
+ })
+ }, [preferences])
+
+ // Module calls are asynchronous.
+ // const theme = await preferences.getTheme()
+}
+```
+
+Subscriptions are removed automatically on unmount or when `moduleName` changes. Calling the returned unsubscribe function removes a subscription earlier.
+
+---
+
+## ModuleService
+
+Low-level static service used by `useModule()`. Use it when a hook is not appropriate but the React Native worklet has already initialized.
+
+| Method | Signature | Description |
+|--------|-----------|-------------|
+| `callModule()` | `callModule(moduleName: string, method: string, ...args: unknown[]): Promise` | Serialize positional arguments, call the named module method, and parse the JSON response |
+| `onModuleEvent()` | `onModuleEvent(moduleName: string, event: string, listener: ModuleEventListener): () => void` | Subscribe to one named event and return an unsubscribe function |
+
+`callModule()` requires non-empty module and method names plus an initialized worklet. It returns `undefined` when the worklet response is absent or empty. Arguments and results must be safely JSON-serializable; `bigint` arguments are serialized as strings, while circular and non-standard-prototype objects are rejected.
+
+`onModuleEvent()` parses JSON event payloads when possible and otherwise delivers the raw string. Listener failures do not stop delivery to other listeners.
+
+---
+
## useAddresses
Hook to load and query wallet addresses across all networks.
@@ -732,9 +808,14 @@ interface WdkConfigs, TProtocol = Record
}
+ modules?: {
+ [moduleName: string]: Record
+ }
}
```
+`modules` contains runtime configuration for generic modules. It is distinct from the Worklet Bundler's build-time `modules` map, which selects package names, factories, and forwarded events. Use the same module name in both maps.
+
#### WdkNetworkConfig
Wrapper around `NetworkConfig` with typed `config` field.
diff --git a/content/docs/tools/react-native-core/index.mdx b/content/docs/tools/react-native-core/index.mdx
index bd31c587..d2c6d648 100644
--- a/content/docs/tools/react-native-core/index.mdx
+++ b/content/docs/tools/react-native-core/index.mdx
@@ -8,21 +8,32 @@ description: Hooks-based React Native library for building multi-chain wallet ap
## Features
- **Hooks-based architecture** - `useWdkApp`, `useWalletManager`, `useAccount`, `useProtocol`, `useBalance`, `useBalancesForWallets`, and more
+- **Generic module API** - `useModule` and `ModuleService` expose named worklet-module calls and events
- **TanStack Query caching** - automatic balance fetching across one or many account indices, per-token fallback for modules without batch balance support, cache invalidation, and optimistic updates
- **Zustand state management** - persisted wallet state with MMKV storage
- **Worklet runtime** - runs WDK in an isolated Bare worklet
- **Biometric authentication** - secure storage with device biometrics
- **Multi-wallet support** - create, restore, switch, lock, unlock, and delete wallets
-- **TypeScript-first** - full type safety with exported types and interfaces
+- **Typed React Native source API** - exported hook and service types are available through the package's React Native source condition
+
+
+Starting in `v1.0.0-beta.14`, React Native Core pins a Pear Worklet version with the HRPC methods required by `useModule()` and `ModuleService`. Beta.15 pins Pear Worklet beta.10.
+
+
+
+The `v1.0.0-beta.15` npm artifact includes the React Native source entry at `src/index.ts`, but it does not include the declared default JavaScript or type declaration files under `dist/`. Use a React Native resolver that selects the package's `react-native` condition. Resolvers that select `default` or `types` target files that are not present in this artifact.
+
## Quick Start
### 1. Install
```bash
-npm install @tetherto/wdk-react-native-core
+npm install @tetherto/wdk-react-native-core react-native-bare-kit
```
+Starting in beta.14, `react-native-bare-kit` is a peer dependency with a supported range of `>=0.14.5`, so the app must install it explicitly.
+
### 2. Wrap Your App
```tsx
@@ -67,9 +78,7 @@ For a full integration guide, see the [React Native Quickstart](/start-building/
## Bundle Configuration
-The WDK engine runs inside a Bare worklet. You need to provide a bundle - there are two approaches:
-
-### Custom Bundle (Recommended)
+The WDK engine runs inside a Bare worklet. Generate an HRPC bundle with `@tetherto/wdk-worklet-bundler` and import it from the generated `./.wdk` entrypoint.
Use the `@tetherto/wdk-worklet-bundler` CLI to generate a bundle with only the blockchain modules you need:
@@ -93,27 +102,20 @@ Example `wdk.config.js`:
```javascript
module.exports = {
- modules: {
- core: '@tetherto/wdk',
- erc4337: '@tetherto/wdk-wallet-evm-erc-4337',
- },
networks: {
ethereum: {
- module: 'erc4337',
- chainId: 1,
- blockchain: 'ethereum',
- provider: 'https://eth.drpc.org',
+ package: '@tetherto/wdk-wallet-evm-erc-4337'
},
polygon: {
- module: 'erc4337',
- chainId: 137,
- blockchain: 'polygon',
- provider: 'https://polygon.drpc.org',
- },
+ package: '@tetherto/wdk-wallet-evm-erc-4337'
+ }
},
+ transport: 'hrpc'
}
```
+This file selects packages at build time. Pass chain IDs, providers, protocol settings, and any per-module runtime config through `wdkConfigs` on `WdkAppProvider`. The optional build-time `modules` map in the bundler is distinct from runtime `wdkConfigs.modules`; their names must match when generic-module support is available.
+
After running `wdk-worklet-bundler generate`, import and use the bundle:
```typescript
@@ -124,23 +126,7 @@ import { bundle } from './.wdk'
```
-For full bundler documentation, see [wdk-worklet-bundler](https://github.com/tetherto/wdk-worklet-bundler).
-
-### Pre-built Bundle
-
-For quick prototyping, use the pre-built bundle from `@tetherto/pear-wrk-wdk` which includes all blockchain modules:
-
-```typescript
-import { bundle } from '@tetherto/pear-wrk-wdk'
-
-
-
-
-```
-
-
-The pre-built bundle includes all blockchain modules, resulting in a larger bundle size. For production apps, generate a custom bundle with only the modules you need.
-
+For the complete config and transport rules, see the [Worklet Bundler configuration guide](/tools/worklet-bundler/configuration). `@tetherto/pear-wrk-wdk` provides the worklet transport/runtime layer; it does not export a pre-built WDK bundle.
## Architecture
@@ -156,7 +142,8 @@ WdkAppProvider
+-- useWdkApp() - app state (INITIALIZING, NO_WALLET, LOCKED, READY, ERROR)
+-- useWalletManager() - create, restore, lock, unlock, delete wallets
+-- useAccount() - address, send, sign, verify, estimateFee
- +-- useProtocol() - call bridge, swap, lending, and fiat protocol methods
+ +-- useProtocol() - call bridge, swap, Swidge, lending, and fiat protocol methods
+ +-- useModule() - call named generic modules and subscribe to events
+-- useAddresses() - load and query addresses
+-- useBalance() - single balance with TanStack Query
+-- useBalancesForWallet() - bulk balance fetch for one account index
diff --git a/content/docs/tools/react-native-secure-storage/configuration.mdx b/content/docs/tools/react-native-secure-storage/configuration.mdx
index 74a1b3dd..a2a17215 100644
--- a/content/docs/tools/react-native-secure-storage/configuration.mdx
+++ b/content/docs/tools/react-native-secure-storage/configuration.mdx
@@ -10,9 +10,13 @@ This page shows how to install `@tetherto/wdk-react-native-secure-storage`, crea
## Install the package
```bash title="Install React Native Secure Storage"
-npm install @tetherto/wdk-react-native-secure-storage react-native-keychain expo-crypto expo-local-authentication
+npm install @tetherto/wdk-react-native-secure-storage@1.0.0-beta.4 react-native-keychain expo-crypto expo-local-authentication
```
+
+The `v1.0.0-beta.5` npm artifact includes the React Native source entry at `src/index.ts`, but it does not include the `dist/` files declared by `main`, `types`, and `exports.default`. The install command pins beta.4, the latest artifact with those compiled JavaScript and declaration files. Use beta.5 only with a React Native resolver configuration you have verified against its source entry.
+
+
The package declares `react-native` as a peer dependency and uses native modules through `react-native-keychain`.
diff --git a/content/docs/tools/react-native-secure-storage/index.mdx b/content/docs/tools/react-native-secure-storage/index.mdx
index 5c2b7dc6..7af94f7b 100644
--- a/content/docs/tools/react-native-secure-storage/index.mdx
+++ b/content/docs/tools/react-native-secure-storage/index.mdx
@@ -9,6 +9,10 @@ React Native Secure Storage provides a typed wrapper for storing encrypted walle
Powered by [`@tetherto/wdk-react-native-secure-storage`](https://www.npmjs.com/package/@tetherto/wdk-react-native-secure-storage).
+
+The `v1.0.0-beta.5` npm artifact includes the React Native source entry at `src/index.ts`, but it does not include the `dist/` files declared by `main`, `types`, and `exports.default`. Pin beta.4 when the consumer needs those compiled JavaScript and declaration files; use beta.5 only with a React Native resolver configuration you have verified against its source entry.
+
+
## Features
- **Wallet credential helpers**: Store and retrieve encrypted seeds, encrypted entropy, and encryption keys.
diff --git a/content/docs/tools/wdk-utils/api-reference.mdx b/content/docs/tools/wdk-utils/api-reference.mdx
index 20825c1f..86147f93 100644
--- a/content/docs/tools/wdk-utils/api-reference.mdx
+++ b/content/docs/tools/wdk-utils/api-reference.mdx
@@ -1,6 +1,6 @@
---
title: WDK Utils API Reference
-description: Validation, encryption, BIP-21, BOLT11, and EIP-681 APIs for @tetherto/wdk-utils
+description: Validation, encryption, mnemonic sharing, BIP-21, BOLT11, and EIP-681 APIs for @tetherto/wdk-utils
docType: reference
schemaType: APIReference
---
@@ -11,11 +11,13 @@ schemaType: APIReference
| Function | Description | Returns |
| --- | --- | --- |
+| `validateAddress(chainId, address)` | Dispatch address validation by CAIP-2 chain ID; Bitcoin and Spark references also enforce network compatibility. | Chain-specific result or a validation failure. |
| `validateBase58(address)` | Validate a Base58Check Bitcoin address payload and version byte. | See details below. |
| `validateBech32(address)` | Validate a SegWit v0 Bitcoin address. | `BtcAddressValidationResult` |
| `validateBech32m(address)` | Validate a SegWit v1+ Bitcoin address. | `BtcAddressValidationResult` |
| `validateBitcoinAddress(address)` | Validate a Bitcoin address across Base58Check, Bech32, and Bech32m formats. | `BtcAddressValidationResult` |
| `validateEVMAddress(address)` | Validate an EVM address, including EIP-55 checksum rules for mixed-case inputs. | `EvmAddressValidationResult` |
+| `validateSolanaAddress(address)` | Validate a base58-encoded 32-byte Solana public key, including off-curve program-derived addresses. | `SolanaAddressValidationResult` |
| `stripLightningPrefix(input)` | Remove a leading `lightning:` prefix before other Lightning validation steps. | `string` |
| `validateLightningInvoice(address)` | Validate a Lightning invoice string. | `LightningInvoiceValidationResult` |
| `decode(invoice)` | Decode a BOLT11 Lightning invoice into structured invoice data. | `LightningInvoiceDecodingResult` |
@@ -30,11 +32,33 @@ schemaType: APIReference
| `validateUmaAddress(address)` | Validate a Universal Money Address. | `UmaAddressValidationResult` |
| `resolveUmaUsername(uma)` | Split a valid UMA string into `localPart`, `domain`, and `lightningAddress`. | Parsed UMA details or `null`. |
+#### `validateAddress(chainId, address)`
+
+Validate an address with the chain-specific validator selected by a CAIP-2 chain ID or a bare namespace accepted by WDK Utils. The supported namespaces are `bip122`, `eip155`, `solana`, `spark`, and `tron`.
+
+```javascript title="Validate An Address For A CAIP-2 Chain"
+import { validateAddress } from '@tetherto/wdk-utils'
+
+const result = validateAddress(
+ 'bip122:000000000019d6689c085ae165831e93',
+ '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
+)
+// { success: true, type: 'p2pkh', compatibleNetworks: ['bitcoin'] }
+```
+
+The function returns the selected chain validator's result. Failures specific to dispatch are:
+
+- `INVALID_CHAIN_ID`: `chainId` is not a string or does not match the accepted input shape: a 3–8 character lowercase namespace, optionally followed by a 1–32 character reference. A bare namespace is a WDK Utils extension; standard CAIP-2 identifiers require `:`.
+- `UNSUPPORTED_CHAIN`: the namespace has no validator.
+- `NETWORK_MISMATCH`: a `bip122` or `spark` reference is missing, unknown, or incompatible with the address.
+
+For `bip122` and `spark`, the reference selects the expected network. Other supported namespaces validate the address but do not enforce their reference value. This is structural validation; it does not confirm that an account exists, belongs to the intended recipient, or can receive a particular asset.
+
#### `validateBase58(address)`
Validate a Base58Check Bitcoin address and identify whether it matches a supported P2PKH or P2SH network version byte.
-The published `beta.2` type declaration currently includes an internal `{ decoded: Uint8Array }` branch in this return type. The shipped runtime returns validation results rather than exposing that intermediate decode object.
+The current beta.11 type declaration includes an internal `{ decoded: Uint8Array }` branch in this return type. The shipped runtime returns validation results rather than exposing that intermediate decode object.
```javascript title="Validate A Base58 Bitcoin Address"
import { validateBase58 } from '@tetherto/wdk-utils'
@@ -72,11 +96,13 @@ import { validateBitcoinAddress } from '@tetherto/wdk-utils'
const result = validateBitcoinAddress('tb1pu9yqnhc6wjj6s62s9x0shnl5l2r7gq5cudm94r7mvwv0uw4s7acqjg9r2f')
```
-`BtcAddressValidationResult` is one of these shapes:
+`BtcAddressValidationResult` is one of these shapes. Beta.11 replaces the earlier singular `network` field with `compatibleNetworks` and names mainnet `bitcoin`:
-- `{ success: true, type: 'p2pkh' | 'p2sh' | 'bech32' | 'bech32m', network: 'mainnet' | 'testnet' | 'regtest' }`
+- `{ success: true, type: 'p2pkh' | 'p2sh' | 'bech32' | 'bech32m', compatibleNetworks: Array<'bitcoin' | 'testnet' | 'regtest'> }`
- `{ success: false, reason: string }`
+Legacy Base58 testnet formats are also valid on regtest, so those results contain both `testnet` and `regtest`.
+
#### `validateEVMAddress(address)`
Validate an EVM address. Mixed-case inputs must satisfy EIP-55 checksum casing, while all-lowercase and all-uppercase inputs remain valid.
@@ -92,6 +118,25 @@ const result = validateEVMAddress('0x742d35Cc6634C0532925a3b844Bc454e4438f44e')
- `{ success: true, type: 'evm' }`
- `{ success: false, reason: string }`
+#### `validateSolanaAddress(address)`
+
+Validate a Solana address as a base58-encoded 32-byte public key. The validator trims surrounding whitespace and accepts off-curve program-derived addresses because they can be valid recipients.
+
+```javascript title="Validate A Solana Address"
+import { validateSolanaAddress } from '@tetherto/wdk-utils'
+
+const result = validateSolanaAddress(
+ 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
+)
+```
+
+`SolanaAddressValidationResult` is one of these shapes:
+
+- `{ success: true, type: 'solana' }`
+- `{ success: false, reason: 'EMPTY_ADDRESS' | 'INVALID_FORMAT' | 'INVALID_LENGTH' }`
+
+Solana addresses do not include a checksum. This helper verifies structure and decoded length, not whether an account exists, is on curve, or belongs to the intended recipient.
+
#### `stripLightningPrefix(input)`
Remove a `lightning:` URI prefix before validating or parsing Lightning inputs.
@@ -253,9 +298,11 @@ const l1Deposit = validateSparkAddress(
`SparkAddressValidationResult` is one of these shapes:
-- `{ success: true, type: 'spark' | 'btc' }`
+- `{ success: true, type: 'spark' | 'btc', compatibleNetworks: Array<'mainnet' | 'testnet' | 'regtest' | 'signet' | 'local'> }`
- `{ success: false, reason: string }`
+Bitcoin L1 deposit-address formats can be shared by Spark testnet/signet or regtest/local, so the compatibility array can contain two networks.
+
#### `validateTronAddress(address)`
Validate a Tron Base58Check address. The helper checks the `T` prefix, the 34-character address length, the Tron prefix byte, and the checksum.
@@ -361,6 +408,107 @@ import { decryptWithKey } from '@tetherto/wdk-utils'
const plaintext = decryptWithKey(encrypted, key)
```
+### Seed Key Derivation Helpers
+
+Derive deterministic, domain-separated key material from high-entropy seed bytes.
+
+| Function | Description | Returns |
+| --- | --- | --- |
+| `deriveSeedKey(seed, options)` | Derive a byte key with HKDF-SHA256. | `Uint8Array` |
+| `deriveSeedKeyPair(seed, options)` | Derive a deterministic Ed25519 keypair. | `KeyPair` |
+
+```typescript title="Seed Key Derivation Types"
+type SeedKeyOptions = {
+ salt: string | Uint8Array
+ info: string | Uint8Array
+ length?: number
+}
+
+type KeyPair = {
+ publicKey: Uint8Array
+ secretKey: Uint8Array
+}
+```
+
+#### `deriveSeedKey(seed, options)`
+
+Derive a key using HKDF-SHA256. `salt` and `info` are required caller-controlled domain-separation values, and `length` defaults to 32 bytes.
+
+```javascript title="Derive A Seed Key"
+import { deriveSeedKey } from '@tetherto/wdk-utils'
+
+const key = deriveSeedKey(seedBytes, {
+ salt: 'com.example.wallet/v1',
+ info: 'local-encryption',
+ length: 32
+})
+```
+
+The function throws `salt is required` or `info is required` when either field is absent. String inputs are consumed as raw UTF-8 bytes; a mnemonic string is not converted into BIP-39 seed bytes.
+
+#### `deriveSeedKeyPair(seed, options)`
+
+Derive a deterministic Ed25519 keypair from the same seed, salt, and info inputs. The function always uses a 32-byte derived seed, even if `options.length` is supplied, and returns a 32-byte public key plus a 64-byte secret key laid out as the derived seed followed by the public key.
+
+```javascript title="Derive An Ed25519 Keypair"
+import { deriveSeedKeyPair } from '@tetherto/wdk-utils'
+
+const keyPair = deriveSeedKeyPair(seedBytes, {
+ salt: 'com.example.wallet/v1',
+ info: 'device-signing'
+})
+```
+
+
+Treat the returned `secretKey` as sensitive and clear it after its final use. The implementation clears its intermediate derived seed, but the returned secret key remains in caller-managed memory.
+
+
+### BIP-39 Mnemonic Sharing Helpers
+
+| Function | Description | Returns |
+| --- | --- | --- |
+| `splitMnemonic(mnemonic, options)` | Split valid BIP-39 mnemonic entropy into hex-encoded Shamir shares. | `Promise` |
+| `combineMnemonic(shares)` | Reconstruct and validate a BIP-39 mnemonic from a threshold-sized share set. | `Promise` |
+
+#### `splitMnemonic(mnemonic, options)`
+
+Decode a valid 12-, 15-, 18-, 21-, or 24-word English BIP-39 mnemonic to entropy and split it into Shamir shares.
+
+```javascript title="Split A Mnemonic"
+import { splitMnemonic } from '@tetherto/wdk-utils'
+
+const shares = await splitMnemonic(seedPhrase, {
+ shares: 5,
+ threshold: 3
+})
+```
+
+- `shares` must be an integer from `2` through `255`.
+- `threshold` must be an integer from `2` through `shares`.
+- The returned shares are unencrypted lowercase hex strings.
+- Invalid BIP-39 words, word counts, and checksums are rejected.
+- The runtime must provide secure `crypto.getRandomValues`; React Native apps may need to load `react-native-get-random-values` before importing WDK Utils.
+
+#### `combineMnemonic(shares)`
+
+Reconstruct a mnemonic from enough shares produced by `splitMnemonic()`.
+
+```javascript title="Reconstruct A Mnemonic"
+import { combineMnemonic } from '@tetherto/wdk-utils'
+
+const restored = await combineMnemonic([
+ shares[0],
+ shares[2],
+ shares[4]
+])
+```
+
+The helper verifies the embedded four-byte integrity checksum and rejects malformed, corrupted, duplicate, mixed-length, or insufficient share sets.
+
+
+Each share is sensitive recovery material. Store shares in separate trusted locations and never log or transmit them through analytics. The embedded checksum detects accidental corruption; it does not authenticate shares or protect against maliciously crafted input.
+
+
### BIP-21 Bitcoin Payment URI Helpers
| Function | Description | Returns |
diff --git a/content/docs/tools/wdk-utils/configuration.mdx b/content/docs/tools/wdk-utils/configuration.mdx
index 3a115c60..30918d05 100644
--- a/content/docs/tools/wdk-utils/configuration.mdx
+++ b/content/docs/tools/wdk-utils/configuration.mdx
@@ -1,6 +1,6 @@
---
title: WDK Utils Configuration
-description: Install and import validation, encryption, BIP-21, BOLT11, and EIP-681 helpers from @tetherto/wdk-utils
+description: Install and import validation, encryption, mnemonic-sharing, key-derivation, and payment-request helpers from @tetherto/wdk-utils
---
This package does not have constructor options or runtime configuration. This page shows how to install `@tetherto/wdk-utils`, import the helpers you need, and understand the published runtime surface.
@@ -13,18 +13,28 @@ You can install `@tetherto/wdk-utils` from npm:
npm install @tetherto/wdk-utils
```
+React Native runtimes without `globalThis.crypto.getRandomValues` also need a secure random-values polyfill:
+
+```bash title="Install React Native Random Values"
+npm install react-native-get-random-values
+```
+
+Load the polyfill before importing WDK Utils in the application entrypoint.
+
## Import address validation helpers
You can import only the validators your flow needs from the package entrypoint:
```javascript title="Import Address Validators"
import {
+ validateAddress,
validateBitcoinAddress,
validateEVMAddress,
validateLightningInvoice,
validateLnurl,
decodeLnurl,
validateLightningAddress,
+ validateSolanaAddress,
validateSparkAddress,
validateTronAddress,
validateUmaAddress,
@@ -32,6 +42,17 @@ import {
} from '@tetherto/wdk-utils'
```
+## Import mnemonic sharing helpers
+
+Split a valid English BIP-39 mnemonic into threshold shares and reconstruct it from enough shares:
+
+```javascript title="Import Mnemonic Sharing Helpers"
+import {
+ combineMnemonic,
+ splitMnemonic
+} from '@tetherto/wdk-utils'
+```
+
## Import EIP-681 helpers
You can detect and parse token transfer requests using the EIP-681 helpers:
@@ -68,6 +89,17 @@ import {
} from '@tetherto/wdk-utils'
```
+## Import seed key derivation helpers
+
+Derive domain-separated byte keys or deterministic Ed25519 keypairs from high-entropy seed bytes:
+
+```javascript title="Import Seed Key Derivation Helpers"
+import {
+ deriveSeedKey,
+ deriveSeedKeyPair
+} from '@tetherto/wdk-utils'
+```
+
## Import BOLT11 helpers
You can validate, decode, sign, and encode BOLT11 Lightning invoices from the package entrypoint:
@@ -86,11 +118,21 @@ import {
- `@tetherto/wdk-utils` exports plain functions. There is no client object to initialize.
- The package publishes a default module entrypoint through `index.js` and a bare runtime entrypoint through `bare.js`.
+- `validateAddress()` dispatches the CAIP-2 namespaces `bip122`, `eip155`, `solana`, `spark`, and `tron` to their chain validators. Unsupported namespaces return `UNSUPPORTED_CHAIN`, and malformed chain IDs return `INVALID_CHAIN_ID`.
+- For `bip122` and `spark`, the chain reference selects the expected network. A missing, unknown, or incompatible reference returns `NETWORK_MISMATCH`. Other supported namespaces validate the address format but do not enforce the reference value.
+- Successful Bitcoin and Spark validators return `compatibleNetworks`, because some address formats are valid on more than one network. WDK Utils validates structure and network compatibility, not account existence, ownership, or recipient intent.
+- `validateSolanaAddress()` accepts base58-encoded 32-byte public keys, including off-curve program-derived addresses. Solana has no address checksum, so the helper cannot detect every mistyped address.
+- `splitMnemonic()` accepts valid 12-, 15-, 18-, 21-, or 24-word English BIP-39 phrases. It returns hex-encoded Shamir shares and supports threshold schemes from 2-of-2 through 255-of-255.
+- `combineMnemonic()` verifies an embedded integrity checksum before returning the reconstructed phrase. The checksum detects corruption but does not authenticate shares.
+- `encrypt()` and `splitMnemonic()` require `globalThis.crypto.getRandomValues`. In React Native, load `react-native-get-random-values` before importing `@tetherto/wdk-utils` when the runtime does not provide secure random bytes.
- `parseBip21Request()` accepts `bitcoin:` URIs with a validated Bitcoin address and optional `amount`, `label`, and `message` parameters.
- `encodeBip21Request()` validates the Bitcoin address and amount before returning a `bitcoin:` URI.
- `encrypt()` returns a versioned payload with hex-encoded `salt`, `iv`, `tag`, and `ciphertext` fields plus the scrypt cost parameters used for key derivation.
- `decrypt()` reads the scrypt cost parameters from the encrypted payload when they are present.
- `deriveKey()` returns a 32-byte `Uint8Array` key, and `decryptWithKey()` can reuse that key for decrypting a payload.
+- `deriveSeedKey()` uses HKDF-SHA256 and defaults to a 32-byte output. Both `salt` and `info` are required and are chosen by the caller; the package provides no built-in domain labels.
+- Pass high-entropy BIP-39 seed bytes to the seed key derivation helpers, not a mnemonic phrase. Accepted string inputs are consumed literally as UTF-8 and are not converted from mnemonic words into seed bytes.
+- `deriveSeedKeyPair()` always derives a 32-byte Ed25519 seed and returns a 32-byte `publicKey` plus a sensitive 64-byte `secretKey`, regardless of an `options.length` value.
- BIP-21 amounts are decimal BTC strings with up to eight decimal places and a maximum value of `21000000`.
- BOLT11 helpers support invoices for `bitcoin`, `testnet`, `regtest`, and `signet` networks.
- `decode()` returns user-provided invoice descriptions when they are present. Sanitize descriptions before rendering them in HTML or storing them.
@@ -104,14 +146,21 @@ You can validate common wallet inputs before handing them to a module:
```javascript title="Validate Common Inputs"
import {
+ validateAddress,
validateBitcoinAddress,
validateLightningAddress,
+ validateSolanaAddress,
validateTronAddress,
validateUmaAddress
} from '@tetherto/wdk-utils'
+const chainAware = validateAddress(
+ 'bip122:000000000019d6689c085ae165831e93',
+ '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'
+)
const btc = validateBitcoinAddress('bc1qu9yqnhc6wjj6s62s9x0shnl5l2r7gq5cudm94r7mvwv0uw4s7acq0hn9g6')
const lightning = validateLightningAddress('sprycomfort92@waletofsatoshi.com')
+const solana = validateSolanaAddress('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
const tron = validateTronAddress('TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH')
const uma = validateUmaAddress('$you@uma.money')
```
@@ -161,6 +210,46 @@ const encrypted = encrypt(seedPhrase, passphrase)
const restoredSeedPhrase = decrypt(encrypted, passphrase)
```
+You can split a BIP-39 mnemonic into shares that require a threshold to recover:
+
+```javascript title="Split And Recover A Mnemonic"
+import 'react-native-get-random-values' // React Native only; load before WDK Utils
+import { combineMnemonic, splitMnemonic } from '@tetherto/wdk-utils'
+
+const shares = await splitMnemonic(seedPhrase, {
+ shares: 5,
+ threshold: 3
+})
+
+const restored = await combineMnemonic([
+ shares[0],
+ shares[2],
+ shares[4]
+])
+```
+
+Treat every unencrypted share as sensitive recovery material. Store shares separately and do not send them to logs, analytics, or untrusted services.
+
+You can derive independent keys for application-specific purposes by using distinct domain labels:
+
+```javascript title="Derive Domain-Separated Keys"
+import { deriveSeedKey, deriveSeedKeyPair } from '@tetherto/wdk-utils'
+
+const syncKey = deriveSeedKey(seedBytes, {
+ salt: 'com.example.wallet/v1',
+ info: 'sync-encryption'
+})
+
+const signingKeyPair = deriveSeedKeyPair(seedBytes, {
+ salt: 'com.example.wallet/v1',
+ info: 'device-signing'
+})
+
+// Clear sensitive outputs after their final use.
+syncKey.fill(0)
+signingKeyPair.secretKey.fill(0)
+```
+
***
## Need Help?
diff --git a/content/docs/tools/wdk-utils/index.mdx b/content/docs/tools/wdk-utils/index.mdx
index 3bc2f552..0d81c29f 100644
--- a/content/docs/tools/wdk-utils/index.mdx
+++ b/content/docs/tools/wdk-utils/index.mdx
@@ -1,15 +1,18 @@
---
title: WDK Utils
-description: Address validation, seed encryption, BIP-21, BOLT11 invoice, and EIP-681 request helpers for @tetherto/wdk-utils
+description: Chain-aware address validation, mnemonic sharing, seed encryption, deterministic key derivation, and payment-request helpers for @tetherto/wdk-utils
---
-WDK Utils provides validation helpers for Bitcoin, EVM, Lightning, Spark, Tron, and UMA identifiers, passphrase-based seed encryption helpers, BIP-21 Bitcoin payment URI helpers, BOLT11 invoice helpers, and EIP-681 request parsing helpers for token transfer deep links. Powered by [`@tetherto/wdk-utils`](https://github.com/tetherto/wdk-utils).
+WDK Utils provides CAIP-2-aware and chain-specific validation helpers for Bitcoin, EVM, Lightning, Solana, Spark, Tron, and UMA identifiers, Shamir-based mnemonic sharing, passphrase-based seed encryption, deterministic HKDF-SHA256 and Ed25519 key derivation, BIP-21 Bitcoin payment URI helpers, BOLT11 invoice helpers, and EIP-681 request parsing for token transfer deep links. Powered by [`@tetherto/wdk-utils`](https://github.com/tetherto/wdk-utils).
## Features
-- **Address validation helpers**: Validate Bitcoin, EVM, Lightning invoice, LNURL, Lightning address, Spark, Tron, and UMA inputs before you hand them to a wallet flow.
+- **Chain-aware address validation**: Dispatch to Bitcoin, EVM, Solana, Spark, or Tron validation with a CAIP-2 chain ID, including network checks for Bitcoin and Spark.
+- **Address validation helpers**: Validate Bitcoin, EVM, Lightning invoice, LNURL, Lightning address, Solana, Spark, Tron, and UMA inputs before you hand them to a wallet flow.
+- **Mnemonic sharing helpers**: Split an English BIP-39 mnemonic into threshold-based Shamir shares and combine enough shares to recover it.
- **BIP-21 payment URI helpers**: Detect, parse, and encode `bitcoin:` payment URIs with optional amount, label, and message fields.
- **Seed encryption helpers**: Encrypt and decrypt seed phrases or other strings with AES-256-GCM and scrypt-derived keys.
+- **Seed key derivation**: Derive domain-separated byte keys or deterministic Ed25519 keypairs from high-entropy seed bytes.
- **Lightning payment parsing**: Decode LNURL strings and BOLT11 invoices before you display or route payment details.
- **BOLT11 invoice helpers**: Validate, decode, hash, sign, and encode BOLT11 invoices for Bitcoin, testnet, regtest, and signet flows.
- **EIP-681 request parsing**: Detect request-shaped EIP-681 strings and parse transfer payloads into `recipient`, `tokenAddress`, `chainId`, and `amountSmallest`.
@@ -19,8 +22,11 @@ WDK Utils provides validation helpers for Bitcoin, EVM, Lightning, Spark, Tron,
## Why this matters
-- Validate user input early and return machine-readable failure reasons before you attempt a transaction or resolution flow.
+- Validate user input early, optionally against its CAIP-2 chain, and return machine-readable failure reasons before you attempt a transaction or resolution flow.
+- Split a mnemonic across separate storage or recovery locations without making every share sufficient on its own.
+- Pair these helpers with the [P2P Address Book](/tools/p2p-address-book/) when a wallet stores user-selected destinations for later use.
- Protect seed phrases or other local strings with passphrase-based encryption before storing them in app-managed persistence.
+- Derive separate application keys from one high-entropy seed without reusing the same key material across purposes.
- Parse and encode BIP-21 Bitcoin payment links before pre-filling send forms.
- Inspect BOLT11 invoice metadata, fallback addresses, routing information, and feature bits before presenting payment details.
- Normalize EIP-681 payment links into structured transfer data that wallet UIs can inspect before execution.
diff --git a/content/docs/tools/worklet-bundler/api-reference.mdx b/content/docs/tools/worklet-bundler/api-reference.mdx
index 68524dc4..f3b69d68 100644
--- a/content/docs/tools/worklet-bundler/api-reference.mdx
+++ b/content/docs/tools/worklet-bundler/api-reference.mdx
@@ -18,20 +18,42 @@ schemaType: APIReference
```typescript title="WdkBundleConfig Shape"
interface WdkBundleConfig {
networks: Record
- protocols?: Record
+ protocols?: Record
+ modules?: Record
preloadModules?: string[]
+ transport?: 'hrpc' | 'jsonrpc'
output?: {
bundle?: string
types?: string
+ addons?: {
+ ios?: string
+ macos?: string
+ android?: string
+ }
+ addonsYml?: string
}
options?: {
minify?: boolean
sourceMaps?: boolean
targets?: string[]
+ linkAddons?: boolean
+ platforms?: Array<'ios' | 'macos' | 'android'>
+ swiftTarget?: string
+ convertEsmToCjs?: boolean
}
}
```
+`modules` is generated only for HRPC through beta.7. JSON-RPC entry generation ignores that map. JSON-RPC otherwise defaults addon linking and ESM-to-CJS conversion to `true`; HRPC defaults both to `false`.
+
+
+Beta.7 declares `output.types`, `options.minify`, and `options.sourceMaps`, but its bundle generator does not honor those fields. Declarations are always written to `./.wdk/index.d.ts`; `minify` and `sourceMaps` do not control `bare-pack` output. JSON-RPC ESM-to-CJS conversion minifies independently.
+
+
#### `ResolvedConfig`
`ResolvedConfig` extends `WdkBundleConfig` with absolute filesystem paths produced by `loadConfig()`.
@@ -43,6 +65,12 @@ interface ResolvedConfig extends WdkBundleConfig {
resolvedOutput: {
bundle: string
types: string
+ addons: {
+ ios: string
+ macos: string
+ android: string
+ }
+ addonsYml: string
}
}
```
@@ -119,7 +147,10 @@ Run the uninstall flow from code and receive a structured `UninstallResult`.
| `loadConfig(configPath?)` | Load, validate, and resolve a `wdk.config.js` file into absolute paths. | `Promise\` |
| `generateBundle(config, options?)` | Generate the entrypoint, imports, bundle, and optional type output. | `Promise\` |
| `generateSourceFiles(config, options?)` | Generate the source entrypoint and related artifacts without bundling. | `Promise\<{ entryPath: string }\>` |
-| `generateEntryPoint(config, outputDir)` | Generate only the Bare worklet entrypoint file. | `Promise\` |
+| `generateEntryPoint(config, outputDir)` | Generate an HRPC Bare worklet entrypoint file. | `Promise\` |
+| `generateJsonRpcEntryPoint(config, outputDir)` | Generate a JSON-RPC Bare worklet entrypoint file. | `Promise\` |
+| `linkAddons(config, options?)` | Link native addons for selected platforms with `bare-link`. | `Promise\` |
+| `generateAddonsYml(iosAddonsDir, swiftTarget, outputPath)` | Generate the BareKit Swift addon dependency file. | `void` |
| `generateWalletModulesCode(config)` | Generate the wallet-module section inserted into the entrypoint. | `string` |
#### `loadConfig(configPath?)`
@@ -143,6 +174,9 @@ Use `generateBundle()` when you want the same bundle workflow that powers the CL
- `silent`
- `skipTypes`
- `skipGeneration`
+- `deferOptionalPeers?` (`boolean`): When omitted or `true`, pass missing peers marked optional through `bare-pack --defer`. Beta.7 checks root, nested, scoped, and symlinked package trees before treating an optional peer as missing. Set this option to `false` to require absent optional imports at build time.
+
+`deferOptionalPeers` is a per-run API option. It is not a `WdkBundleConfig` field and cannot be set in `wdk.config.js`.
`GenerateBundleResult` contains:
@@ -154,6 +188,8 @@ Use `generateBundle()` when you want the same bundle workflow that powers the CL
- `error?`
- `missingModule?`
+In beta.7, `typesPath` can reflect a configured `output.types` path even though the declaration file is still written to `./.wdk/index.d.ts`.
+
```typescript title="Generate A Bundle Programmatically"
import { generateBundle, loadConfig } from '@tetherto/wdk-worklet-bundler'
@@ -167,10 +203,24 @@ Use `generateSourceFiles()` when you want the generated entrypoint without the f
#### `generateEntryPoint(config, outputDir)`
-Use `generateEntryPoint()` when you need the exact generated Bare entrypoint string written to a chosen output directory.
+Use `generateEntryPoint()` when you need the generated HRPC Bare entrypoint written to a chosen output directory. This path includes configured generic modules and their lifecycle/event wiring.
In `beta.3`, the generated entrypoint suspends and resumes both the `bare-http1` and `bare-https` global agents when the Bare runtime emits `suspend` and `resume`.
+#### `generateJsonRpcEntryPoint(config, outputDir)`
+
+Generate the framed JSON-RPC entrypoint used by native hosts. Through beta.7 this path includes wallet and protocol managers but not generic `modules`.
+
+#### `linkAddons(config, options?)`
+
+Link required Bare addons for iOS, macOS, Android, or a selected subset. `LinkAddonsOptions` supports `platforms`, `verbose`, and `silent`. The result contains `success`, `duration`, `platforms`, and optional `error`.
+
+When iOS is selected, addon linking also calls `generateAddonsYml()` with `config.options.swiftTarget` or the default target name `app`.
+
+#### `generateAddonsYml(iosAddonsDir, swiftTarget, outputPath)`
+
+Generate the `addons.yml` dependency list expected by BareKit Swift from linked iOS XCFrameworks.
+
#### `generateWalletModulesCode(config)`
Use `generateWalletModulesCode()` when you only need the generated wallet-module section for inspection or custom generator flows.
@@ -181,13 +231,17 @@ The published CLI exposes these commands through `wdk-worklet-bundler`:
| Command | Description | Key Options |
| --- | --- | --- |
-| `generate` | Generate a WDK bundle from configuration. | `--config`, `--install`, `--keep-artifacts`, `--dry-run`, `--no-types`, `--source-only`, `--skip-generation`, `--verbose` |
+| `generate` | Generate a WDK bundle from configuration. | `--config`, `--install`, `--keep-artifacts`, `--dry-run`, `--no-types`, `--source-only`, `--skip-generation`, `--transport`, `--link-addons`, `--skip-link-addons`, `--platforms`, `--no-esm-to-cjs`, `--no-defer-optional-peers`, `--verbose` |
| `init` | Create a new `wdk.config.js` file. | `--yes` |
| `validate` | Validate configuration without building. | `--config` |
| `list-modules` | List available WDK modules. | `--json` |
| `clean` | Remove the generated `.wdk` folder. | `--yes` |
-For the end-to-end config workflow, see the Worklet Bundler configuration guide when it is available.
+For the end-to-end config workflow and transport defaults, see the [Worklet Bundler configuration guide](/tools/worklet-bundler/configuration).
+
+
+Beta.7 applies `--transport` after `loadConfig()` resolves output paths. The flag changes entrypoint generation but does not recompute the default bundle filename. Set `transport` in `wdk.config.js`, or configure `output.bundle` explicitly when using the flag.
+
***
diff --git a/content/docs/tools/worklet-bundler/configuration.mdx b/content/docs/tools/worklet-bundler/configuration.mdx
index afa044c4..4f792186 100644
--- a/content/docs/tools/worklet-bundler/configuration.mdx
+++ b/content/docs/tools/worklet-bundler/configuration.mdx
@@ -3,16 +3,19 @@ title: Worklet Bundler Configuration
description: Configure wdk.config.js and generate Bare worklet bundles with @tetherto/wdk-worklet-bundler
---
-This page shows how to install `@tetherto/wdk-worklet-bundler`, shape `wdk.config.js`, and generate a Bare worklet bundle for your WDK modules.
+This page shows how to install `@tetherto/wdk-worklet-bundler` and its Pear runtime, shape `wdk.config.js`, and generate a Bare worklet bundle for your WDK modules.
-## Install the package
+## Install the packages
-Install the bundler as a development dependency in the host project:
+Install the bundler as a development dependency and Pear Worklet as a runtime dependency in the host project:
-```bash title="Install @tetherto/wdk-worklet-bundler"
-npm install --save-dev @tetherto/wdk-worklet-bundler
+```bash title="Install The Bundler And Runtime"
+npm install @tetherto/pear-wrk-wdk@1.0.0-beta.10
+npm install --save-dev @tetherto/wdk-worklet-bundler@1.0.0-beta.7
```
+Worklet Bundler beta.7 generates entrypoints that import Pear Worklet, but `generate --install` does not add that package. Pear Worklet beta.10 provides the JSON-RPC, HRPC generic-module, and Swidge protocol behavior documented here.
+
## Create `wdk.config.js`
Use `init` to create a starter config, or write the file yourself:
@@ -21,7 +24,7 @@ Use `init` to create a starter config, or write the file yourself:
npx wdk-worklet-bundler init
```
-The published config surface accepts `networks`, optional `protocols`, optional `preloadModules`, optional `output`, and optional `options`:
+The published config surface accepts `networks`, optional `protocols`, HRPC-only generic `modules`, `transport`, `preloadModules`, `output`, and `options`:
```javascript title="Example wdk.config.js"
module.exports = {
@@ -40,18 +43,25 @@ module.exports = {
}
},
+ modules: {
+ preferences: {
+ package: '@your-org/wdk-module-preferences',
+ factory: 'createModule',
+ events: ['changed']
+ }
+ },
+
+ transport: 'hrpc',
+
preloadModules: [
'spark-frost-bare-addon'
],
output: {
- bundle: './.wdk-bundle/wdk-worklet.bundle.js',
- types: './.wdk/index.d.ts'
+ bundle: './.wdk-bundle/wdk-worklet.bundle.js'
},
options: {
- minify: false,
- sourceMaps: false,
targets: ['ios-arm64', 'android-arm64']
}
}
@@ -81,23 +91,79 @@ module.exports = {
Use `protocols` when the worklet should preload WDK protocol packages alongside wallet modules.
+### `modules` (optional, HRPC only)
+
+Use `modules` for named generic packages that expose a module factory. Each entry supports:
+
+- `package` (`string`, required): Package name or local path.
+- `factory` (`string`, optional): Named factory export. When omitted, the package's callable default export is used.
+- `events` (`string[]`, optional): Event names forwarded from the worklet to the host.
+
+The factory receives `{ seed, config, capabilities, emit }` and can return the module instance or a promise for it. A module that needs the seed must consume it synchronously rather than retain it. The build-time name, such as `preferences`, must match the key in the host's runtime module config.
+
+
+Through beta.7, generic modules are included only in generated HRPC entrypoints. Do not configure `modules` with `transport: 'jsonrpc'`; the current schema accepts the field, but JSON-RPC generation ignores it.
+
+
+### `transport` (optional)
+
+Choose `hrpc` or `jsonrpc`. HRPC is the default and is the transport used by React Native Core. JSON-RPC produces a length-prefixed bundle for a native host and enables addon linking and ESM-to-CJS conversion by default.
+
### `preloadModules` (optional)
Use `preloadModules` for native addons or other modules that must be required before the generated worklet starts.
### `output` (optional)
-If you omit `output.bundle`, the loader resolves the bundle path to `./.wdk-bundle/wdk-worklet.bundle.js`. If you omit `output.types`, the loader resolves the type path to `./.wdk/index.d.ts`.
+Supported output fields are:
+
+- `bundle`: Bundle path. Defaults to `./.wdk-bundle/wdk-worklet.bundle.js` for HRPC and `./.wdk-bundle/wdk-worklet.bundle` for JSON-RPC.
+- `types`: Declared in the beta.7 config type, but the generator does not honor a custom value and always writes declarations to `./.wdk/index.d.ts`.
+- `addons.ios`, `addons.macos`, `addons.android`: Platform addon directories. Defaults are `./ios-addons`, `./mac-addons`, and `./android-addons`.
+- `addonsYml`: BareKit Swift dependency file. Defaults to `./ios-addons/addons.yml` and is generated when iOS addons are linked.
### `options` (optional)
The published config type supports these build options:
-- `minify` (`boolean`): Minify the generated bundle.
-- `sourceMaps` (`boolean`): Request source map output.
+- `minify` (`boolean`): Declared in the beta.7 config type but not read by the bundle path. JSON-RPC ESM-to-CJS conversion minifies independently of this field.
+- `sourceMaps` (`boolean`): Declared in the beta.7 config type but not read by the bundle path, so it does not produce source maps.
- `targets` (`string[]`): Override the default Bare build hosts. The shipped defaults cover iOS arm64 and simulator targets plus Android arm, arm64, ia32, and x64 hosts.
+- `linkAddons` (`boolean`): Link native addons with `bare-link`. Defaults to `true` for JSON-RPC and `false` for HRPC.
+- `platforms` (`('ios' | 'macos' | 'android')[]`): Addon platforms. Defaults to all three when addon linking is active.
+- `swiftTarget` (`string`): Xcode target written to `addons.yml`. Defaults to `app`.
+- `convertEsmToCjs` (`boolean`): Convert the bundle for JSC runtimes. Defaults to `true` for JSON-RPC and `false` for HRPC. Android uses V8, so Android-only JSON-RPC builds can set this to `false`.
+
+## Configure JSON-RPC and native addons
+
+Use JSON-RPC when a native host will implement Pear Worklet's length-prefixed JSON-RPC protocol:
+
+```javascript title="JSON-RPC wdk.config.js"
+module.exports = {
+ networks: {
+ ethereum: {
+ package: '@tetherto/wdk-wallet-evm'
+ }
+ },
+ transport: 'jsonrpc',
+ output: {
+ addons: {
+ ios: './ios-addons',
+ macos: './mac-addons',
+ android: './android-addons'
+ },
+ addonsYml: './ios-addons/addons.yml'
+ },
+ options: {
+ platforms: ['ios', 'android'],
+ swiftTarget: 'app'
+ }
+}
+```
-## Validate and generate the bundle
+JSON-RPC generation defaults to a bundle without a `.js` suffix, ESM-to-CJS conversion, and addon linking. It links `bare-posix` with the other required Bare modules automatically. See the [Pear Worklet API reference](/tools/pear-wrk-wdk/api-reference#json-rpc-transport) for framing and supported methods.
+
+## Validate dependencies
Use `validate` to check the config and dependency resolution before you generate the bundle:
@@ -105,19 +171,43 @@ Use `validate` to check the config and dependency resolution before you generate
npx wdk-worklet-bundler validate
```
+## Handle peer dependencies
+
+Worklet Bundler beta.7 separates required peers from peers explicitly marked optional through `peerDependenciesMeta`:
+
+- During bundle generation, missing required peers are offered for installation. In a non-interactive environment without `--install`, the CLI prints the exact manual install command and continues; `bare-pack` can still fail if the bundle needs that peer. The peer scan is skipped by `--source-only`.
+- Missing optional peers are not prompted for or installed. By default, the bundler passes each one to `bare-pack --defer`, so an unused optional feature does not block the build.
+- Optional peers detected anywhere in the scanned package tree are not deferred. Beta.7 matches package names across root, nested, scoped, and symlinked package trees; install a peer in an ancestor `node_modules` visible to the package that imports it, because the same package name elsewhere in the tree can suppress deferral without making that import resolvable. A peer that is required anywhere else in the scanned dependency tree is treated as required and is not deferred.
+
+Use `--no-defer-optional-peers` when the app uses an optional feature and you want a missing peer to fail during the build:
+
+```bash title="Require Optional Peers At Build Time"
+npx wdk-worklet-bundler generate --no-defer-optional-peers
+```
+
+For example, a bundle can build while a missing `@ledgerhq/ledger-bitcoin` peer is deferred, then fail at runtime if the app uses the corresponding Ledger feature. Install the peer explicitly, or use `--no-defer-optional-peers` to surface the missing import during bundling.
+
+This behavior is a per-run setting. `wdk.config.js` has no `deferOptionalPeers` field. Programmatic callers can set `GenerateBundleOptions.deferOptionalPeers` to `false`; omitting it or setting it to `true` keeps the default deferral behavior.
+
+If a core configured dependency is missing, a non-interactive `generate` run that will invoke `bare-pack` exits with status `1` and tells you to install it manually or rerun with `--install`. A `--source-only` run can still emit the generated source files.
+
+## Generate the bundle
+
Use `generate` to build the worklet artifact:
```bash title="Generate The Worklet Bundle"
npx wdk-worklet-bundler generate --install
```
-`generate --install` can auto-install missing configured modules after the package manager is detected from the project root. Use `--source-only` when you want the generated `.wdk/wdk-worklet.generated.js` entrypoint and related artifacts without running `bare-pack`.
+`generate --install` can auto-install missing configured wallet, protocol, generic, and preload modules after the package manager is detected from the project root. In beta.7 it does not install the Pear Worklet runtime imported by generated entrypoints; install Pear explicitly as shown above. Use `--source-only` when you want the generated `.wdk/wdk-worklet.generated.js` entrypoint and related artifacts without running `bare-pack`.
+
+The `generate` command also accepts `--transport hrpc|jsonrpc`, `--link-addons`, `--skip-link-addons`, `--platforms ios,macos,android`, `--no-esm-to-cjs`, and `--no-defer-optional-peers`. Set `transport` in `wdk.config.js` when you rely on its transport-specific default bundle path. In beta.7, `--transport` changes the generated transport after output paths have already been resolved, so it does not switch `.js` HRPC output to the extensionless JSON-RPC default, or vice versa. If you use the flag, set `output.bundle` explicitly to the intended path.
## Suspend and resume behavior
-Generated `beta.3` worklet entrypoints register Bare lifecycle handlers for `suspend` and `resume`, then apply those handlers to both the `bare-http1` and `bare-https` global agents. No config flag is required for this behavior.
+Generated HRPC worklet entrypoints register Bare lifecycle handlers for `suspend` and `resume`, then apply those handlers to both the `bare-http1` and `bare-https` global agents. No config flag is required for this behavior.
-This matters when a generated worklet performs HTTPS-backed fetches. In `beta.2`, only the `bare-http1` agent was suspended and resumed. In `beta.3`, generated worklets suspend and resume both agents together.
+This matters when a generated HRPC worklet performs HTTPS-backed fetches. Starting in beta.3, those generated entrypoints suspend and resume both agents together.
## Troubleshooting
diff --git a/content/docs/tools/worklet-bundler/index.mdx b/content/docs/tools/worklet-bundler/index.mdx
index 084863a1..8ada6df5 100644
--- a/content/docs/tools/worklet-bundler/index.mdx
+++ b/content/docs/tools/worklet-bundler/index.mdx
@@ -3,21 +3,32 @@ title: Worklet Bundler
description: CLI tool for generating WDK Bare worklet bundles
---
-Worklet Bundler packages selected WDK wallet and protocol modules into a Bare runtime bundle that runs outside your main React Native application thread. Powered by [`@tetherto/wdk-worklet-bundler`](https://github.com/tetherto/wdk-worklet-bundler).
+Worklet Bundler packages selected WDK wallet, protocol, and optional generic modules into a Bare runtime bundle that runs outside the host application thread. It can generate the default HRPC worklet used by React Native Core or a framed JSON-RPC bundle for a native host. Powered by [`@tetherto/wdk-worklet-bundler`](https://github.com/tetherto/wdk-worklet-bundler).
## Features
-- **Config-driven bundle generation**: Map logical network names to wallet packages and optional protocol names to protocol packages in `wdk.config.js`.
-- **Generated worklet artifact**: Produce a bundle for the Bare runtime plus generated TypeScript types for the host app.
+- **Config-driven bundle generation**: Map logical network names to wallet packages, protocol names to protocol packages, and HRPC-only generic-module names to package factories.
+- **HRPC and JSON-RPC transports**: Generate an HRPC JavaScript bundle by default or a length-prefixed JSON-RPC bundle for native hosts.
+- **Generated worklet artifacts**: Produce a Bare bundle, a stable `./.wdk` host import, and generated TypeScript types.
+- **Native addon outputs**: Link iOS, macOS, and Android addons and generate `addons.yml` for JSON-RPC/native integrations.
- **Dependency validation helpers**: Validate configured modules, detect the active package manager, and generate install or uninstall commands when dependencies are missing.
+- **Optional peer deferral**: Defer missing peers marked optional by their package metadata, or require them at build time with `--no-defer-optional-peers`.
- **CLI workflow**: Use `init`, `validate`, `generate`, `list-modules`, and `clean` without building your own wrapper.
-- **Bare suspend and resume handling**: `beta.3` generated entrypoints suspend and resume both the `bare-http1` and `bare-https` global agents when the Bare thread lifecycle changes.
+- **Bare suspend and resume handling**: Generated HRPC entrypoints suspend and resume both the `bare-http1` and `bare-https` global agents when the Bare thread lifecycle changes.
+
+
+`v1.0.0-beta.7` includes the built CLI and API files for the JSON-RPC, generic-module, and addon-linking surface, defers genuinely missing optional peers during bundling by default, and links `bare-posix` automatically for supported native platforms. It also detects optional peers installed anywhere in nested, scoped, or symlinked `node_modules` trees so present dependencies are not incorrectly deferred and omitted from the bundle.
+
## Why this matters
- Bundle generation keeps wallet and protocol code in a separate Bare worklet instead of the UI thread.
- The generated type output gives the host app a stable import for the bundle surface.
-- The `beta.3` runtime fix reduces the chance that HTTPS-backed worklet fetches keep running after a Bare thread suspension.
+- Transport-specific defaults produce a JavaScript HRPC bundle for React Native Core or native addon artifacts for JSON-RPC hosts.
+
+
+Generic `modules` are generated only for HRPC through beta.7. A JSON-RPC config can currently accept a `modules` field but silently omits those modules from the generated entrypoint.
+
diff --git a/package-lock.json b/package-lock.json
index 3958580d..4d17f2da 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,7 +32,7 @@
"katex": "^0.16.27",
"lucide-react": "^0.562.0",
"mermaid": "^11.15.0",
- "next": "^16.2.6",
+ "next": "^16.2.12",
"next-themes": "^0.4.6",
"react": "^19.2.3",
"react-dom": "^19.2.3",
@@ -52,7 +52,7 @@
"@types/react-dom": "^19.1.9",
"@vahor/next-broken-links": "^0.4.1",
"glob": "^11.0.3",
- "postcss": "^8.5.6",
+ "postcss": "^8.5.18",
"remark": "^15.0.1",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
@@ -2116,15 +2116,15 @@
}
},
"node_modules/@next/env": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz",
- "integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.12.tgz",
+ "integrity": "sha512-d0Z5Bc13Fa4nR8pFAKx2jay2yhJM16vlfHbTzYnUQAxlNb6B6lmn4hjt69lYNt4kRtyYP6gEM49lPRHNbIyneg==",
"license": "MIT"
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz",
- "integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.12.tgz",
+ "integrity": "sha512-0W1R0teHWJrqKX0FH20IzzIWAOuGtBxPGuObrxy1lE8hQvCFj49KE8a3WUg0D7sq6rn6zkM4c7YGUnhudBS6oA==",
"cpu": [
"arm64"
],
@@ -2138,9 +2138,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz",
- "integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.12.tgz",
+ "integrity": "sha512-Hy5Ls099+aFUmOLmIgPfLqNi6iCwhL3uQCssz5rWk+5Nkc6TUKCE83DY5BbNylfm3+mfwcSFnLRfrZDJhVxdtw==",
"cpu": [
"x64"
],
@@ -2154,12 +2154,15 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz",
- "integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.12.tgz",
+ "integrity": "sha512-+YqU2h1cQkHsGfvjAsrSmst8UIFBibBGm5x3Xgel8NLMiDQtNOM4sM2GOEMvG5YiOBNeN/Ykk8cQC2S0Xrqljg==",
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2170,12 +2173,15 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz",
- "integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.12.tgz",
+ "integrity": "sha512-0qjhiYBaKAqF63LA1ZWAAnKTzFUguAaZiRa5etMLGGPj/B6uEVjtIZldIzFEp3wHlB0koK6aTzqPtSdplTCjoA==",
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2186,12 +2192,15 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz",
- "integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.12.tgz",
+ "integrity": "sha512-7A3q26W+h7gnA15uqBToNuDqBEFZZcqh0mW2mn4AJh/G5pdg2RVE3n4slzLEliASZFG3NmsbEzng/x2Sh09mBg==",
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2202,12 +2211,15 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz",
- "integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.12.tgz",
+ "integrity": "sha512-qSjL/uppm+cbh21s72Ss8gkiOhQ4dExWHNGOWy6eZV7STj5WsKehgxT61beSsOj+YYQuTplL376lOCdMQU5T8w==",
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -2218,9 +2230,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz",
- "integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.12.tgz",
+ "integrity": "sha512-X6hzsOUJac/e7AWSbn9gQ9nzHld1xWP5iyjHpYWvud8pufB679O1xg4JDyKr8Xd69Jvd+kM2Der6uftiZCmjYA==",
"cpu": [
"arm64"
],
@@ -2234,9 +2246,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz",
- "integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.12.tgz",
+ "integrity": "sha512-F6fakeHuFTLOPt0bslQJdf+xtT+WIP9DVn/m4y1w1mRnVPyh3D/cNvzlRkxM444xfm+IvvYNSOrKiA2CDJ0Uxw==",
"cpu": [
"x64"
],
@@ -5718,9 +5730,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
- "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6852,9 +6864,9 @@
}
},
"node_modules/dompurify": {
- "version": "3.4.11",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz",
- "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==",
+ "version": "3.4.12",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz",
+ "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -7288,9 +7300,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
- "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
+ "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
"dev": true,
"funding": [
{
@@ -10209,15 +10221,15 @@
}
},
"node_modules/minimatch/node_modules/brace-expansion": {
- "version": "5.0.6",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
- "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
- "node": "18 || 20 || >=22"
+ "node": "20 || >=22"
}
},
"node_modules/minimist": {
@@ -10317,12 +10329,12 @@
}
},
"node_modules/next": {
- "version": "16.2.6",
- "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz",
- "integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==",
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.2.12.tgz",
+ "integrity": "sha512-iD59eYQWmbFcEbX7v/acG5DRym9iw1DdaPoD0WTA920naWsE25wShzJW4+UvAs8MK9EC2kBfIH6vtto1H1PHGw==",
"license": "MIT",
"dependencies": {
- "@next/env": "16.2.6",
+ "@next/env": "16.2.12",
"@swc/helpers": "0.5.15",
"baseline-browser-mapping": "^2.9.19",
"caniuse-lite": "^1.0.30001579",
@@ -10336,14 +10348,14 @@
"node": ">=20.9.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "16.2.6",
- "@next/swc-darwin-x64": "16.2.6",
- "@next/swc-linux-arm64-gnu": "16.2.6",
- "@next/swc-linux-arm64-musl": "16.2.6",
- "@next/swc-linux-x64-gnu": "16.2.6",
- "@next/swc-linux-x64-musl": "16.2.6",
- "@next/swc-win32-arm64-msvc": "16.2.6",
- "@next/swc-win32-x64-msvc": "16.2.6",
+ "@next/swc-darwin-arm64": "16.2.12",
+ "@next/swc-darwin-x64": "16.2.12",
+ "@next/swc-linux-arm64-gnu": "16.2.12",
+ "@next/swc-linux-arm64-musl": "16.2.12",
+ "@next/swc-linux-x64-gnu": "16.2.12",
+ "@next/swc-linux-x64-musl": "16.2.12",
+ "@next/swc-win32-arm64-msvc": "16.2.12",
+ "@next/swc-win32-x64-msvc": "16.2.12",
"sharp": "^0.34.5"
},
"peerDependencies": {
@@ -10807,9 +10819,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.15",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
- "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "version": "8.5.18",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.18.tgz",
+ "integrity": "sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==",
"funding": [
{
"type": "opencollective",
@@ -11654,9 +11666,9 @@
}
},
"node_modules/serve-handler/node_modules/brace-expansion": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
- "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index 5338f72e..86a5da12 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,8 @@
"check:meta": "node scripts/check-meta.mjs",
"check:redirects": "node scripts/check-redirects.mjs",
"test:llm-md": "node --test scripts/test/*.test.mjs",
- "quality": "npm run check:meta && npm run check:redirects && npm run check:links && npm run build"
+ "test:link-routing": "tsx --test scripts/test/link-routing.test.ts",
+ "quality": "npm run test:link-routing && npm run check:meta && npm run check:redirects && npm run check:links && npm run build"
},
"dependencies": {
"@ai-sdk/openai-compatible": "^1.0.29",
@@ -47,7 +48,7 @@
"katex": "^0.16.27",
"lucide-react": "^0.562.0",
"mermaid": "^11.15.0",
- "next": "^16.2.6",
+ "next": "^16.2.12",
"next-themes": "^0.4.6",
"react": "^19.2.3",
"react-dom": "^19.2.3",
@@ -67,7 +68,7 @@
"@types/react-dom": "^19.1.9",
"@vahor/next-broken-links": "^0.4.1",
"glob": "^11.0.3",
- "postcss": "^8.5.6",
+ "postcss": "^8.5.18",
"remark": "^15.0.1",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
diff --git a/public/llms-full.txt b/public/llms-full.txt
index eb1270f2..bfbcb6b8 100644
--- a/public/llms-full.txt
+++ b/public/llms-full.txt
@@ -55,8 +55,8 @@ Get started with WDK in a Node.js environment
Build mobile wallets with React Native Expo
-
-Deploy WDK in lightweight environments
+
+Connect AI assistants to WDK docs and tools
Explore our React Native UI Kit with pre-built components
@@ -78,7 +78,7 @@ Description: Give any AI agent self-custodial wallet capabilities with WDK agent
WDK provides agent skills: structured instruction sets that teach AI agents how to create wallets, send transactions, swap tokens, bridge assets, and interact with DeFi protocols across 20+ blockchains. All operations are self-custodial. Keys stay on your machine, with no third-party custody dependency.
-**Skill vs MCP Toolkit**: Use an **agent skill** when your agent platform works with file-based instructions (e.g., OpenClaw, Cursor). Use the [MCP Toolkit](mcp-toolkit/) when your agent supports the Model Context Protocol natively (e.g., Claude, Cursor). Use both for maximum coverage.
+**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?
@@ -123,7 +123,7 @@ WDK's agent skills use a self-custodial model where your agent controls its own
| Custody model | Self-custodial | Coinbase-hosted | Privy-hosted (server) |
| Multi-chain | Yes (EVM, Bitcoin, Solana, TON, Tron, Spark + more) | EVM + Solana | EVM + Solana + Bitcoin + more |
| Open source | Yes (SDK + skills) | CLI/skills open, infra closed | Skills open, API closed |
-| MCP support | Yes ([MCP Toolkit](mcp-toolkit/)) | Via skills | Via skills |
+| 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 |
@@ -132,10 +132,11 @@ WDK's agent skills use a self-custodial model where your agent controls its own
| Platform | How to Use |
| --- | --- |
-| **OpenClaw** | Install from [ClawHub](openclaw) or clone to workspace. See [OpenClaw Integration](openclaw) |
+| **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 the [MCP Toolkit](mcp-toolkit/) for structured tool calling |
+| **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
@@ -153,6 +154,7 @@ Community-built projects using WDK's agentic capabilities:
- [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
@@ -222,6 +224,9 @@ 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
+
***
@@ -1289,8 +1294,7 @@ server.registerProtocol('ethereum', 'velora', VeloraProtocolEvm)
server.registerProtocol('ethereum', 'usdt0', Usdt0ProtocolEvm)
server.registerProtocol('ethereum', 'aave', AaveProtocolEvm)
server.registerProtocol('ethereum', 'moonpay', MoonPayProtocol, {
- apiKey: process.env.MOONPAY_API_KEY,
- secretKey: process.env.MOONPAY_SECRET_KEY
+ apiKey: process.env.MOONPAY_API_KEY
})
```
@@ -1379,7 +1383,7 @@ server.registerTools([getAddress, getBalance, getCurrentPrice])
### Custom Tools
-Add your own MCP tools alongside the built-in ones using the standard `registerTool()` method (inherited from `McpServer`). See the [MCP SDK tools documentation](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/server.md#tools) for full details.
+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(
@@ -1418,7 +1422,6 @@ server.registerTool(
| `WDK_SEED` | Yes | BIP-39 mnemonic for wallet key derivation |
| `WDK_INDEXER_API_KEY` | No | API key for WDK Indexer |
| `MOONPAY_API_KEY` | No | API key for MoonPay fiat on/off-ramp |
-| `MOONPAY_SECRET_KEY` | No | Secret key for MoonPay |
***
@@ -1449,7 +1452,7 @@ URL: https://docs.wdk.tether.io/ai/mcp-toolkit/get-started
Description: Install the MCP Toolkit and run your first AI-powered wallet server
-**Building a LangChain agent?** The `serve` command provides zero-config MCP server startup -- no server script needed. See [LangChain Integration](langchain).
+**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
@@ -1706,7 +1709,6 @@ server.registerProtocol('ethereum', 'velora', VeloraProtocolEvm)
server.registerProtocol('ethereum', 'usdt0', Usdt0ProtocolEvm)
server.registerProtocol('ethereum', 'aave', AaveProtocolEvm)
server.registerProtocol('ethereum', 'moonpay', MoonPayProtocol, {
- secretKey: process.env.MOONPAY_SECRET_KEY,
apiKey: process.env.MOONPAY_API_KEY
})
@@ -1729,14 +1731,13 @@ server.registerTools([
| `WDK_SEED` | Yes | BIP-39 seed phrase for wallet derivation |
| `WDK_INDEXER_API_KEY` | No | Enables `INDEXER_TOOLS` - [get a key](/tools/indexer-api/get-started/#request-api-key) |
| `MOONPAY_API_KEY` | No | Enables `FIAT_TOOLS` - [MoonPay Dashboard](https://dashboard.moonpay.com/) |
-| `MOONPAY_SECRET_KEY` | No | Required with `MOONPAY_API_KEY` |
***
## Next Steps
-* [**Configuration**](configuration) - Wallets, tokens, protocols, custom tools, and security
-* [**API Reference**](api-reference) - All 35 built-in MCP tools with parameters and schemas
+* [**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
***
@@ -1759,7 +1760,7 @@ This approach uses LangChain's MCP adapters to connect to the WDK MCP server. WD
-**Want more control?** The `serve` command is the fastest way to get running, but you can also [write your own MCP server](get-started#manual-setup) with the programmatic API for full control over wallets, tools, and protocols. Then point LangChain's `MultiServerMCPClient` at it using `node your-server.js` instead of the `serve` command.
+**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.
***
@@ -1800,7 +1801,7 @@ The command has built-in definitions for 13 chains and 4 protocol modules. When
| [`@tetherto/wdk-protocol-swap-velora-evm`](/sdk/swap-modules/swap-velora-evm) | Swap tools (Ethereum, Arbitrum) | -- |
| [`@tetherto/wdk-protocol-bridge-usdt0-evm`](/sdk/bridge-modules/bridge-usdt0-evm) | Bridge tools (Ethereum, Arbitrum) | -- |
| [`@tetherto/wdk-protocol-lending-aave-evm`](/sdk/lending-modules/lending-aave-evm) | Lending tools (Ethereum) | -- |
-| [`@tetherto/wdk-protocol-fiat-moonpay`](/sdk/fiat-modules/fiat-moonpay) | Fiat tools (Ethereum) | Requires `MOONPAY_*` env vars |
+| [`@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).
@@ -1930,8 +1931,12 @@ Control `serve` behavior through environment variables:
| `WDK_RPC_\` | No | Built-in defaults | Override the RPC endpoint for a chain (e.g. `WDK_RPC_ETHEREUM=https://my-rpc.com`) |
| `WDK_CONFIG` | No | -- | Path to a `wdk.config.json` file for custom chains and protocols |
| `WDK_INDEXER_API_KEY` | No | -- | Enables indexer tools for balance and transfer history queries |
-| `MOONPAY_API_KEY` | No | -- | Enables fiat on/off-ramp tools |
-| `MOONPAY_SECRET_KEY` | No | -- | Required with `MOONPAY_API_KEY` |
+| `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
@@ -2013,6 +2018,10 @@ Tether and the WDK Team do not endorse or assume responsibility for its code, se
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):
@@ -2064,7 +2073,7 @@ Once the skill is loaded, your agent can:
- **Lend and borrow** through Aave V3
- **Buy and sell crypto** via MoonPay fiat on/off-ramps
-For the full list of capabilities and how skills work, see [Agent Skills](agent-skills).
+For the full list of capabilities and how skills work, see [Agent Skills](/ai/agent-skills/).
## Security Risks and Safety Precautions
@@ -2107,8 +2116,9 @@ OpenClaw makes use of artificial intelligence and machine learning technologies.
## Next Steps
-- [Agent Skills](agent-skills) - Full capabilities, how skills work, and a comparison with other agentic wallet solutions
-- [MCP Toolkit](mcp-toolkit/) - Programmatic wallet access for MCP-compatible agents
+- [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
***
@@ -2692,10985 +2702,25502 @@ app.use(
***
-## 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.
-
-
+## 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.
-**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.
+**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.
-### Quickstart
-
-Get your React Native wallet running in minutes with these simple steps:
+## Install
-
-
-#### Clone and Install
+WDK CLI requires Node.js 22.18.0 or later.
-```bash
-git clone https://github.com/tetherto/wdk-starter-react-native.git && cd wdk-starter-react-native && npm install
+```bash title="Terminal"
+npm install -g --allow-scripts=@tetherto/wdk-cli @tetherto/wdk-cli@1.0.0-beta.1
```
-
-
-#### Configure Environment
+The allowlist permits the CLI package's postinstall script, which installs the version-pinned wallet modules in its bundled network configuration.
-```bash
-cp .env.example .env
+Verify the installation:
+
+```bash title="Terminal"
+wdk --version
```
-Get your free WDK Indexer API key [here](/tools/indexer-api/get-started) and add it to your `.env` file:
+Install the scoped `@tetherto/wdk-cli` package. The unscoped `wdk-cli` package is not this CLI.
-```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
-```
+## 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 |
-
-#### Run Your App
+Most users invoke `wdk`. The CLI and MCP integrations manage the other two binaries.
-For first-time setup, generate native project files:
+## Command Map
-```bash
-npx expo prebuild
-```
+| 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 |
-Then run the app:
+Run `wdk --help` for the command map or append `--help` to a command:
-```bash
-npm run ios # iOS Simulator
-npm run android # Android Emulator
+```bash title="Terminal"
+wdk wallet create --help
+wdk get balance --help
```
-
-
-***
+## Choose an Interface
-
-**Need detailed instructions?** Check out the complete [React Native Quickstart](/start-building/react-native-quickstart) guide for step-by-step setup, configuration, and troubleshooting.
-
+| 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 |
-### Features
+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.
-**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
+## Next Steps
-**Wallet Management**
+
+
+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
+
+
-* **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**
+## Need Help?
-* **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
+## 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.
-### Project Structure
+## Root Options
-The starter includes a modular architecture designed for scalability and maintainability:
+| 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 |
-```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
-```
+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.
-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).
+## Shared Wallet Selection
-***
+Wallet-dependent read, send, buy, and sell commands use:
-### Available Scripts
+| Option | Behavior |
+| --- | --- |
+| `--wallet ` | Uses the named wallet; otherwise uses `defaultWallet` |
+| `--index ` | Uses a non-negative account index; otherwise uses `defaultIndex`, initially `0` |
-| 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 |
+The selected wallet must be unlocked before daemon-backed operations. See [Manage Wallets](/cli/guides/manage-wallets).
-***
+## Wallet Commands
-### Technology Stack
+### `wdk wallet create`
-#### Core Technologies
+Creates a named wallet from a newly generated BIP-39 seed phrase.
-* **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
+| Option | Required | Default | Description |
+| --- | --- | --- | --- |
+| `--name ` | Yes | — | Wallet name |
+| `--words ` | No | `12` | Seed length; accepts `12` or `24` |
-#### Build Configuration
+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`.
-* **Android**: minSdkVersion 29
-* **iOS**: Latest Xcode toolchain
-* **Build Properties**: Configured via `expo-build-properties`
+### `wdk wallet import`
-***
+Imports an existing 12-word or 24-word BIP-39 seed phrase.
-### Next Steps
+| Option | Required | Description |
+| --- | --- | --- |
+| `--name ` | Yes | Wallet name |
-**Customizing the UI**
+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.
-This starter uses components from the [WDK React Native UI Kit](/ui-kits/react-native-ui-kit/). To customize the look and feel:
+### `wdk wallet export`
-* [**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
+Decrypts and prints a wallet's seed phrase.
-**Add new functionality**
+| Option | Required | Description |
+| --- | --- | --- |
+| `--name ` | Yes | Wallet name |
-This starter provides a solid foundation that you can extend with additional functionality:
+With `--json`, the success object contains `seedPhrase`.
-* **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)
+
+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.
+
-**Or explore documentation**
+### `wdk wallet list`
-* [**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
+Lists local wallets with their default, lock, and TTL state. This command has no command-specific options.
-***
+### `wdk wallet delete`
-## Need Help?
+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.
-## About WDK
-URL: https://docs.wdk.tether.io/overview/about
-Description: Learn about the Wallet Development Kit and its capabilities
+### `wdk wallet unlock`
-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.
+Unlocks a wallet and starts the daemon when needed.
-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.
+| Option | Required | Default | Description |
+| --- | --- | --- | --- |
+| `--name ` | Yes | — | Wallet name |
+| `--ttl ` | No | `5` | Non-negative session duration in minutes; `0` disables automatic expiry |
-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.
+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`
-## What Problems Does WDK Solve?
+Locks one wallet or every wallet.
-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.
+| Option | Required | Description |
+| --- | --- | --- |
+| `--name ` | One selector required | Wallet to lock |
+| `--all` | One selector required | Lock every wallet |
-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.
+If both selectors are present, beta.1 applies `--all`.
-### **The Missing AI Foundation**
+### `wdk wallet default`
-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.
+Sets the default wallet after passphrase confirmation.
-***
+| Option | Required | Description |
+| --- | --- | --- |
+| `--name ` | Yes | Existing wallet name |
-## Why WDK is Different
+### `wdk wallet rename`
-
-
-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
-
-
+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 |
-## What WDK Provides
+## Read Commands
-WDK combines four core components to deliver a complete wallet development solution:
+### `wdk get address`
-
-
-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
-
-
+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 |
-## Supported Blockchains & Protocols
+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 natively supports a broad set of blockchains and standards out of the box:
+### `wdk get balance`
-
-
-| Blockchain/Module | Support |
-| --------------------------------------------------------------- | ------- |
-| [Bitcoin](/sdk/wallet-modules/wallet-btc/) | ✅ |
-| [Ethereum & EVM](/sdk/wallet-modules/wallet-evm/) | ✅ |
-| [Ethereum ERC-4337](/sdk/wallet-modules/wallet-evm-erc-4337/) | ✅ |
-| [TON](/sdk/wallet-modules/wallet-ton/) | ✅ |
-| [TON Gasless](/sdk/wallet-modules/wallet-ton-gasless/) | ✅ |
-| [TRON](/sdk/wallet-modules/wallet-tron/) | ✅ |
-| [TRON Gasfree](/sdk/wallet-modules/wallet-tron-gasfree/) | ✅ |
-| [Solana](/sdk/wallet-modules/wallet-solana/) | ✅ |
-| [Spark/Lightning](/sdk/wallet-modules/wallet-spark/) | ✅ |
-
-
-| Protocol/Module | Support |
-| -------------------------------------------------------------- | ------- |
-| [velora (EVM)](/sdk/swap-modules/swap-velora-evm/) | ✅ |
-| [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/) | ✅ |
-
-
+Reads one registered asset balance or native balances across a network group.
-The modular architecture allows new chains, tokens, or protocols to be added by implementing dedicated modules.
+| 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 |
-Ready to start building? Explore our [getting started guide](/start-building/nodejs-bare-quickstart) or dive into our [SDK documentation](/sdk/get-started).
+`--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`
-## Changelog
-URL: https://docs.wdk.tether.io/overview/changelog
-Description: Updates and improvements to the Wallet Development Kit (WDK) modules and tools.
+Reads token-transfer history through the configured indexer.
-Stay up to date with the latest improvements, new features, and bug fixes across all WDK modules.
+| 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`.
-### July 07, 2026
+## Send Command
-**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.
+### `wdk send`
-**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.
+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 |
-### July 04, 2026
+Use `--dry-run` before broadcasting:
-**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.
+```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.
-### July 03, 2026
+## Fiat Ramp Commands
-**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.
+`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).
-**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.
+### `wdk buy`
-**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.
+| 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.
-### July 01, 2026
+## Configuration Commands
-**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.
+See [Configuration](/cli/configuration) for keys, types, precedence, and storage considerations.
----
+### `wdk config get`
-### June 30, 2026
+| 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 |
-**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.
+`--all` cannot be combined with `--key` or `--network`. `--network` and `--key` can be combined.
----
+### `wdk config set`
-### June 29, 2026
+| 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 |
-**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.
+### `wdk config reset`
-**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.
+| 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`.
-### June 25, 2026
+### `wdk config path`
-**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.
+Prints the resolved `config.json` path. This command has no command-specific options.
----
+## Network Commands
-### June 23, 2026
+### `wdk network list`
-**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.
+| Option | Default | Description |
+| --- | --- | --- |
+| `--testnet` | Off | Show only testnets |
+| `--mainnet` | Off | Show only mainnets |
-**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.
+With neither option, the command shows every registered network. If both are provided, beta.1 applies `--testnet`.
----
+### `wdk network create `
-### June 20, 2026
+Creates a custom network from an inline JSON object or a JSON file path. The `` positional argument is required.
-**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.
+See [Custom Networks](/cli/guides/custom-networks) for the network schema and validation rules.
----
+### `wdk network delete`
-### June 19, 2026
+| Option | Required | Description |
+| --- | --- | --- |
+| `--name ` | Yes | Custom network to delete |
-**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.
+Built-in networks cannot be deleted. Deleting a custom network also removes its network configuration and custom token entries.
-**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.
+### `wdk network info`
----
+| Option | Required | Description |
+| --- | --- | --- |
+| `--network