diff --git a/.claude/skills/qabot/SKILL.md b/.claude/skills/qabot/SKILL.md index d9c8b23fea8..8e73947d7c3 100644 --- a/.claude/skills/qabot/SKILL.md +++ b/.claude/skills/qabot/SKILL.md @@ -27,9 +27,11 @@ If any of these are missing, tell the user to add them to `~/.secrets`. The API ## Ports -- **Web dev server** (ShapeShift app): `localhost:3000` +- **Web dev server** (ShapeShift app): `localhost:3000` (or `$PORTLESS_URL` if using Portless, e.g. `http://.web.localhost:1355`) - **qabot API/dashboard**: `localhost:8080` (dev) or deployed URL +**Portless note:** If the dev server is running via Portless, `PORTLESS_URL` is set automatically (e.g. `http://feat-x.web.localhost:1355`). The qabot profile stores wallet state per-origin, so each Portless origin is separate from `localhost:3000` - import the wallet once per new origin. + ## Modes ### Interactive Mode (no fixture provided) @@ -115,17 +117,39 @@ $AB --headed open agent-browser --session qabot --profile ~/.agent-browser/profiles/qabot open ``` -The profile at `~/.agent-browser/profiles/qabot` stores the native wallet (IndexedDB, localStorage, cookies) per origin. Import the wallet once per origin in headed mode, then reuse. +The profile at `~/.agent-browser/profiles/qabot` stores the native wallet (IndexedDB, localStorage, cookies) per origin. Import the wallet once per origin, then reuse. -First time setup per origin (headed, import wallet manually): +First-time setup per origin (keystore import - works headless): ```bash -agent-browser --session qabot --profile ~/.agent-browser/profiles/qabot --headed open -# Import the native wallet, set password to $NATIVE_WALLET_PASSWORD, close. -# Subsequent runs reuse the persisted profile. +agent-browser --session qabot --profile ~/.agent-browser/profiles/qabot open ``` +Only use `--headed` if the user explicitly wants to import via seed phrase (which requires visual interaction). Keystore import is fully automatable headless. + +**Keystore import flow** (when no native wallet exists for the origin): + +There should be a keystore file on the Desktop (e.g. `thorswap-keystore*.txt` or similar). The exact filename may change - look for a keystore/json file on `~/Desktop/`. + +1. Click "Connect Wallet" button (use JS eval if click times out): + `eval "document.querySelectorAll('button').forEach(b => { if(b.textContent.includes('Connect Wallet')) b.click() })"` +2. Click "Import existing" +3. Click "Keystore" (the "Import Keystore File" option) +4. Upload the keystore file to the hidden file input: + `upload "input[type=file]" "/Users/gomes/Desktop/"` +5. Fill the keystore password: + `fill "input[placeholder*=Password]" "$NATIVE_WALLET_PASSWORD"` +6. Click "Import Keystore" +7. On the "Create a New Password" screen: + - Fill nickname: `fill "input[placeholder*=nickname]" "test"` + - Fill password: `fill "input[placeholder*='Enter Password']" "$NATIVE_WALLET_PASSWORD"` + - Fill confirm: `fill "input[placeholder*='Confirm Password']" "$NATIVE_WALLET_PASSWORD"` + - Click "Next" +8. Skip onboarding carousel if shown +9. Wallet "test" should appear in top-right. Subsequent runs reuse the persisted profile. + Origins where the wallet has been imported: -- `http://localhost:3000` (local dev) +- `http://localhost:3000` (local dev, legacy) +- `http://.web.localhost:1355` (local dev via Portless - origin varies per branch, e.g. `develop.web.localhost:1355`) - `https://gome.shapeshift.com` (gome staging) - `https://release.shapeshift.com` (release staging) @@ -260,7 +284,8 @@ When you encounter what looks like a bug, **don't just report it — investigate ```bash source ~/.secrets QABOT="${QABOT_URL:-http://localhost:8080}" -BASE_URL="${BASE_URL:-http://localhost:3000}" +# PORTLESS_URL is set automatically by Portless (e.g. http://develop.web.localhost:1355) +BASE_URL="${PORTLESS_URL:-${BASE_URL:-http://localhost:3000}}" ``` All write requests use: @@ -319,23 +344,29 @@ Use **read-only git operations only** (fetch, rev-parse) - NEVER switch branches GITHUB_REPO="shapeshift/web" # Origin-to-branch mapping (CloudFlare Pages deployments): -# localhost:3000 → local branch (detected from dev server process) -# gome.shapeshift.com → gome -# release.shapeshift.com → release -# develop.shapeshift.com → develop -# app.shapeshift.com → main -# neo.shapeshift.com → neo +# localhost:3000 → local branch (detected from dev server process) +# *.web.localhost:1355 → local branch (Portless, detected from dev process) +# gome.shapeshift.com → gome +# release.shapeshift.com → release +# develop.shapeshift.com → develop +# app.shapeshift.com → main +# neo.shapeshift.com → neo if [[ "$BASE_URL" == *"localhost"* ]]; then - # Local dev: detect web repo from the process actually serving port 3000 - # This handles worktrees correctly (main repo vs .worktrees/qabot etc.) - DEV_PID=$(lsof -i :3000 -sTCP:LISTEN -n -P -t 2>/dev/null | head -1) + # Local dev: detect web repo from the process serving the dev server + # Try Portless proxy (1355) first, then direct port (3000), then any vite process + DEV_PID=$(lsof -i :1355 -sTCP:LISTEN -n -P -t 2>/dev/null | head -1) + if [ -z "$DEV_PID" ]; then + DEV_PID=$(lsof -i :3000 -sTCP:LISTEN -n -P -t 2>/dev/null | head -1) + fi + if [ -z "$DEV_PID" ]; then + DEV_PID=$(pgrep -f "vite.*--port" 2>/dev/null | head -1) + fi if [ -n "$DEV_PID" ]; then WEB_REPO=$(lsof -p "$DEV_PID" 2>/dev/null | awk '/cwd/{print $NF}') fi - # Fallback: infer from context (check WEB_REPO env var, or ask the user) if [ -z "$WEB_REPO" ]; then - echo "ERROR: Could not detect web repo from port 3000. Set WEB_REPO env var." >&2 + echo "ERROR: Could not detect web repo. Set WEB_REPO env var." >&2 exit 1 fi BRANCH=$(git -C "$WEB_REPO" rev-parse --abbrev-ref HEAD) diff --git a/.claude/test-scenarios/README.md b/.claude/test-scenarios/README.md index ce482f85c2f..51f98079cf2 100644 --- a/.claude/test-scenarios/README.md +++ b/.claude/test-scenarios/README.md @@ -80,6 +80,10 @@ Tests for specific bugs that were fixed: /test-agent maintenance ``` +### Portless Support + +All test scenarios reference `${PORTLESS_URL:-http://localhost:3000}` for the dev server URL. When using Portless, `PORTLESS_URL` is set automatically (e.g. `http://feat-x.web.localhost:1355`). Without Portless, everything defaults to `http://localhost:3000`. + ### Manual Testing Each scenario file contains: diff --git a/.claude/test-scenarios/TEMPLATE.md b/.claude/test-scenarios/TEMPLATE.md index 6a805ffbe74..cb346a2ac5a 100644 --- a/.claude/test-scenarios/TEMPLATE.md +++ b/.claude/test-scenarios/TEMPLATE.md @@ -8,7 +8,7 @@ ## Prerequisites -- Dev server running on `localhost:3000` +- Dev server running on `localhost:3000` (or `$PORTLESS_URL` if using Portless) - [Other prerequisite 1] - [Other prerequisite 2] - [Required wallet state, test data, etc.] diff --git a/.claude/test-scenarios/chain-integration-template.md b/.claude/test-scenarios/chain-integration-template.md index e50e6401421..79ab11e9f51 100644 --- a/.claude/test-scenarios/chain-integration-template.md +++ b/.claude/test-scenarios/chain-integration-template.md @@ -16,7 +16,7 @@ This template provides a complete testing workflow for validating new chain inte ## Prerequisites ### Environment -- **Application:** ShapeShift Web running locally (`http://localhost:3000`) +- **Application:** ShapeShift Web running locally (`${PORTLESS_URL:-http://localhost:3000}`) - **Wallet:** Native ShapeShift wallet with password access - **Test Data:** Existing holdings on the target chain (for account derivation testing) @@ -38,7 +38,7 @@ Before starting, gather: #### 1.1 Access Manage Accounts Modal **Steps:** -1. Navigate to `http://localhost:3000` (or your test environment) +1. Navigate to `${PORTLESS_URL:-http://localhost:3000}` (or your test environment) 2. Ensure wallet is connected and unlocked 3. Click wallet button (top right corner) 4. Click three-dot menu icon diff --git a/.claude/test-scenarios/critical/asset-swap-flow.md b/.claude/test-scenarios/critical/asset-swap-flow.md index beeae760cb8..b14cec71c46 100644 --- a/.claude/test-scenarios/critical/asset-swap-flow.md +++ b/.claude/test-scenarios/critical/asset-swap-flow.md @@ -8,7 +8,7 @@ ## Prerequisites -- Dev server running on `localhost:3000` +- Dev server running on `localhost:3000` (or `$PORTLESS_URL` if using Portless) - Wallet connected with test funds - Test wallet should have: - Small amount of ETH for gas @@ -27,7 +27,7 @@ // Navigate to trade browser_click({ element: "Trade navigation link", ref: "..." }) // OR -browser_navigate({ url: "http://localhost:3000/trade" }) +browser_navigate({ url: "${PORTLESS_URL:-http://localhost:3000}/#/trade" }) ``` **Validation Points**: diff --git a/.claude/test-scenarios/critical/wallet-connection-flow.md b/.claude/test-scenarios/critical/wallet-connection-flow.md index 510ed1f3e47..c75c97f50bc 100644 --- a/.claude/test-scenarios/critical/wallet-connection-flow.md +++ b/.claude/test-scenarios/critical/wallet-connection-flow.md @@ -8,7 +8,7 @@ ## Prerequisites -- Dev server running on `localhost:3000` +- Dev server running on `localhost:3000` (or `$PORTLESS_URL` if using Portless) - Browser with MetaMask extension installed (or ability to test without actual wallet) - No wallet currently connected @@ -16,7 +16,7 @@ ### 1. Navigate to Application -**Action**: Open browser and navigate to `http://localhost:3000` +**Action**: Open browser and navigate to `${PORTLESS_URL:-http://localhost:3000}` **Expected Result**: Application loads successfully, showing "Connect Wallet" button **Validation Points**: diff --git a/.claude/test-scenarios/feature-discovery-guide.md b/.claude/test-scenarios/feature-discovery-guide.md index c93e8c435eb..ce4e2aa02b7 100644 --- a/.claude/test-scenarios/feature-discovery-guide.md +++ b/.claude/test-scenarios/feature-discovery-guide.md @@ -13,7 +13,7 @@ Start by navigating to the application and capturing the main navigation structu ``` **Steps**: -1. Navigate to `http://localhost:3000` +1. Navigate to `${PORTLESS_URL:-http://localhost:3000}` 2. Take snapshot of initial page 3. Document main navigation items 4. Document visible features/sections @@ -272,7 +272,7 @@ Use browser MCP to automate discovery: async function discoverFeatures() { // 1. Navigate to home - await browser_navigate({ url: 'http://localhost:3000' }) + await browser_navigate({ url: process.env.PORTLESS_URL || 'http://localhost:3000' }) // 2. Take snapshot const homeSnapshot = await browser_snapshot() diff --git a/.claude/test-scenarios/high-priority/feature-flags-validation.md b/.claude/test-scenarios/high-priority/feature-flags-validation.md index cb92de8ec1e..78151a6398a 100644 --- a/.claude/test-scenarios/high-priority/feature-flags-validation.md +++ b/.claude/test-scenarios/high-priority/feature-flags-validation.md @@ -8,7 +8,7 @@ ## Prerequisites -- Dev server running on `localhost:3000` +- Dev server running on `localhost:3000` (or `$PORTLESS_URL` if using Portless) - Clean browser session (no cached flags) - Knowledge of active feature flags in codebase @@ -21,8 +21,8 @@ **Browser MCP Command**: ```javascript -// Navigate to home -browser_navigate({ url: "http://localhost:3000" }) +// Navigate to home (use PORTLESS_URL or localhost:3000) +browser_navigate({ url: "${PORTLESS_URL:-http://localhost:3000}" }) // Open settings modal (click settings icon) browser_click({ element: "Settings button", ref: "..." }) @@ -31,7 +31,7 @@ browser_click({ element: "Settings button", ref: "..." }) // (This is the secret gesture to access /flags) ``` -**Alternative**: Navigate directly to `http://localhost:3000/flags` +**Alternative**: Navigate directly to `${PORTLESS_URL:-http://localhost:3000}/flags` **Validation Points**: - [ ] Feature flags panel accessible diff --git a/.claude/test-scenarios/markets-discovery-exploration.md b/.claude/test-scenarios/markets-discovery-exploration.md index 8cc7ce4b0dc..092df203362 100644 --- a/.claude/test-scenarios/markets-discovery-exploration.md +++ b/.claude/test-scenarios/markets-discovery-exploration.md @@ -10,7 +10,7 @@ ## Test Configuration ### Test Environment -- **Application URL:** `http://localhost:3000/#/markets/recommended` +- **Application URL:** `${PORTLESS_URL:-http://localhost:3000}/#/markets/recommended` - **Wallet Required:** No (browse mode) - **Network:** Any diff --git a/.claude/test-scenarios/swap-eth-to-btc-same-chain.md b/.claude/test-scenarios/swap-eth-to-btc-same-chain.md index 8774e1a9a9d..fddf5df04e4 100644 --- a/.claude/test-scenarios/swap-eth-to-btc-same-chain.md +++ b/.claude/test-scenarios/swap-eth-to-btc-same-chain.md @@ -41,7 +41,7 @@ **Action:** Navigate to trade page and configure swap pair **Steps:** -1. Open ShapeShift at `http://localhost:3000/#/trade` +1. Open ShapeShift at `${PORTLESS_URL:-http://localhost:3000}/#/trade` 2. Ensure wallet is connected 3. Select ETH as source asset 4. Verify Ethereum is source chain diff --git a/.claude/test-scenarios/swapper-testing.md b/.claude/test-scenarios/swapper-testing.md index 8def82223ec..fd0683c5737 100644 --- a/.claude/test-scenarios/swapper-testing.md +++ b/.claude/test-scenarios/swapper-testing.md @@ -3,7 +3,7 @@ ## Test Environment - **Date**: 2025-01-18 - **Wallet**: Native wallet "awdaw" (password: qwerty123) -- **Dev Server**: localhost:3000 +- **Dev Server**: localhost:3000 (or `$PORTLESS_URL` if using Portless) ## Overview diff --git a/.env b/.env index f951601dac4..246247877aa 100644 --- a/.env +++ b/.env @@ -183,6 +183,7 @@ VITE_SEI_NODE_URL=https://evm-rpc.sei-apis.com VITE_THORCHAIN_NODE_URL=https://api.thorchain.shapeshift.com/lcd VITE_MAYACHAIN_NODE_URL=https://api.mayachain.shapeshift.com/lcd VITE_SOLANA_NODE_URL=https://api.solana.shapeshift.com/api/v1/jsonrpc +VITE_JITO_BLOCK_ENGINE_URL=https://mainnet.block-engine.jito.wtf VITE_STARKNET_NODE_URL=https://rpc.starknet.lava.build VITE_TRON_NODE_URL=https://api.trongrid.io VITE_NEAR_NODE_URL=https://rpc.mainnet.near.org @@ -378,3 +379,6 @@ VITE_USERBACK_TOKEN=A-3gHopRTd55QqxXGsJd0XLVVG3 # agentic chat VITE_FEATURE_AGENTIC_CHAT=false VITE_AGENTIC_SERVER_BASE_URL=https://shapeshiftossagentic-server-production.up.railway.app + +# MetaMask native multichain (BTC/SOL without snaps) +VITE_FEATURE_MM_NATIVE_MULTICHAIN=false diff --git a/.env.development b/.env.development index fdf5ec38fe0..5666ec50e22 100644 --- a/.env.development +++ b/.env.development @@ -90,6 +90,7 @@ VITE_SEI_NODE_URL=https://evm-rpc.sei-apis.com VITE_THORCHAIN_NODE_URL=https://dev-api.thorchain.shapeshift.com/lcd VITE_MAYACHAIN_NODE_URL=https://dev-api.mayachain.shapeshift.com/lcd VITE_SOLANA_NODE_URL=https://dev-api.solana.shapeshift.com/api/v1/jsonrpc +VITE_JITO_BLOCK_ENGINE_URL=https://mainnet.block-engine.jito.wtf VITE_STARKNET_NODE_URL=https://rpc.starknet.lava.build VITE_TRON_NODE_URL=https://api.trongrid.io @@ -106,10 +107,11 @@ VITE_FEATURE_SWAPPER_FIAT_RAMPS=true # Webservices VITE_FEATURE_NOTIFICATIONS_WEBSERVICES=true +# Remote dev servers (default - works for all developers) VITE_SWAPS_SERVER_URL=https://dev-api.swap-service.shapeshift.com VITE_USER_SERVER_URL=https://dev-api.user-service.shapeshift.com VITE_NOTIFICATIONS_SERVER_URL=https://dev-api.notifications-service.shapeshift.com -# Turn those on if you use local development instead of our railway instance +# Local development via Vite proxy (uncomment to use localhost backend) # VITE_SWAPS_SERVER_URL=/swaps-api # VITE_USER_SERVER_URL=/user-api # VITE_NOTIFICATIONS_SERVER_URL=/notifications-api @@ -147,5 +149,6 @@ VITE_FEATURE_YIELDS_PAGE=true VITE_FEATURE_EARN_TAB=true VITE_FEATURE_YIELD_MULTI_ACCOUNT=true VITE_FEATURE_AGENTIC_CHAT=true +VITE_FEATURE_MM_NATIVE_MULTICHAIN=true # VITE_AGENTIC_SERVER_BASE_URL=http://localhost:4111 VITE_AGENTIC_SERVER_BASE_URL=https://shapeshiftossagentic-server-production.up.railway.app diff --git a/.env.test b/.env.test index 4c237043dbc..637aa016e9a 100644 --- a/.env.test +++ b/.env.test @@ -50,6 +50,7 @@ VITE_BASE_NODE_URL=https://api.base.shapeshift.com/api/v1/jsonrpc VITE_THORCHAIN_NODE_URL=https://api.thorchain.shapeshift.com/lcd VITE_MAYACHAIN_NODE_URL=https://api.mayachain.shapeshift.com/lcd VITE_SOLANA_NODE_URL=https://api.solana.shapeshift.com/api/v1/jsonrpc +VITE_JITO_BLOCK_ENGINE_URL=https://mainnet.block-engine.jito.wtf # midgard VITE_THORCHAIN_MIDGARD_URL=https://api.thorchain.shapeshift.com/midgard/v2 diff --git a/MM_NATIVE_MULTICHAIN_PLAN.md b/MM_NATIVE_MULTICHAIN_PLAN.md new file mode 100644 index 00000000000..8ed2524266c --- /dev/null +++ b/MM_NATIVE_MULTICHAIN_PLAN.md @@ -0,0 +1,822 @@ +# MetaMask Native Multichain Migration Plan + +> **GitHub Issues**: +> - #12121 - feat: metamask native multichain support (main) +> - #12122 - feat: metamask native tron support via multichain api (deferred) +> - #12123 - feat: migrate to caip-25 multichain api when production-ready (future) +> **Branch**: `feat/mm_multichain` +> **Worktree**: `.worktrees/mm_multichain` +> **Tracking**: Use beads for implementation tasks + +## #1 Rule: Zero Regression Risk + +**Flag off (prod) = completely untouched existing code paths. No regressions. Period.** + +The feature flag gates ALL new behavior. When the flag is off, the entire MetaMask multichain snap flow remains exactly as it is today. No conditional branches in existing code, no shared utilities that could leak behavior, no refactored interfaces that could change semantics. + +--- + +## Table of Contents + +1. [Context & Motivation](#context--motivation) +2. [Current Architecture (Snap-based)](#current-architecture-snap-based) +3. [Target Architecture (Native Multichain)](#target-architecture-native-multichain) +4. [Chain Coverage Comparison](#chain-coverage-comparison) +5. [Feature Flag Strategy](#feature-flag-strategy) +6. [Implementation Phases](#implementation-phases) +7. [Account Migration Strategy](#account-migration-strategy) +8. [UX Migration Plan](#ux-migration-plan) +9. [Open Questions & Decisions](#open-questions--decisions) + +--- + +## Context & Motivation + +MetaMask rearchitected its account model in 2025, moving from **(1 account = 1 address)** to **(1 account = multiple addresses across chains)**. Non-EVM chains are now supported natively: + +| Chain | Native Since | Address Type | +|-------|-------------|--------------| +| All EVM | Always | 0x... (shared across all EVM) | +| Solana | May 2025 | Base58 | +| Bitcoin | Dec 2025 | Native SegWit (bech32) | +| TRON | Jan 2026 | T-address | + +MetaMask Extension v13.5+ and Mobile v7.57+ both support multichain accounts. + +ShapeShift currently uses a custom **MetaMask Snap** (`@shapeshiftoss/metamask-snaps`) to support non-EVM chains through MetaMask. This snap: +- Derives keys via `snap_getBip32Entropy` + `@shapeshiftoss/hdwallet-native` +- Signs transactions locally and broadcasts via Unchained +- Supports: BTC, LTC, DOGE, BCH, ATOM, THOR (non-EVM); ETH, AVAX (passthrough to MM native) + +With MetaMask's native multichain support, the snap is no longer needed for BTC (and potentially SOL in the future). The snap's approach of extracting BIP-32 entropy and signing locally is more fragile and requires snap installation/updates. Native support is better UX. + +--- + +## Current Architecture (Snap-based) + +``` +MetaMaskAdapter (KeyManager.MetaMask) + | + +-- MIPD Store (EIP-6963) -- finds io.metamask provider + | + +-- MetaMaskMultiChainHDWallet + | + +-- EVM chains --> Direct EIP-1193 RPC (eth_sendTransaction, personal_sign, etc.) + | + +-- UTXO chains --> ShapeShift Snap (wallet_invokeSnap) + | snap_getBip32Entropy -> hdwallet-native -> sign -> Unchained broadcast + | + +-- Cosmos chains --> ShapeShift Snap (wallet_invokeSnap) + snap_getBip32Entropy -> hdwallet-native -> sign -> Unchained broadcast +``` + +### Key Files + +| File | Purpose | +|------|---------| +| `packages/hdwallet-metamask-multichain/src/adapter.ts` | MetaMaskAdapter - MIPD provider detection, keyring integration | +| `packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts` | Core wallet class (800+ lines) | +| `packages/hdwallet-metamask-multichain/src/ethereum.ts` | EVM signing via EIP-1193 | +| `packages/hdwallet-metamask-multichain/src/bitcoin.ts` | BTC via snap | +| `packages/hdwallet-metamask-multichain/src/cosmos.ts` | Cosmos via snap | +| `packages/hdwallet-metamask-multichain/src/thorchain.ts` | THORChain via snap | +| `packages/hdwallet-metamask-multichain/src/utxo.ts` | UTXO BIP44/49/84 path derivation | +| `src/utils/snaps.ts` | Snap enablement/version checking | +| `src/hooks/useIsSnapInstalled/useIsSnapInstalled.tsx` | Snap install detection (3s polling) | +| `src/context/AppProvider/hooks/useSnapStatusHandler.tsx` | Snap state monitoring | +| `src/context/AppProvider/hooks/useDiscoverAccounts.tsx` | Account discovery with snap awareness | + +### MIPD Dual-Purpose Architecture + +**Critical understanding**: The `MetaMaskMultiChainHDWallet` adapter is NOT just for MetaMask. It serves as a **generalized MIPD (EIP-6963) adapter** for other MetaMask-like wallets (Rabby, Ctrl/XDEFI, etc.). The snap functionality is specific to MetaMask (`io.metamask` RDNS), but EVM functionality works for any MIPD-compatible wallet. + +This means: +- **Native multichain is ONLY for MetaMask** (`io.metamask` RDNS) +- **The MIPD/EVM adapter must continue working for all other wallets** +- The adapter's dual nature must be preserved + +--- + +## Target Architecture (Native Multichain) + +``` +MetaMaskAdapter (KeyManager.MetaMask) + | + +-- MIPD Store (EIP-6963) -- finds io.metamask provider + | + +-- [FLAG OFF] MetaMaskMultiChainHDWallet (existing, 100% UNTOUCHED) + | | + | +-- EVM chains --> Direct EIP-1193 RPC + | +-- UTXO/Cosmos --> ShapeShift Snap + | + +-- [FLAG ON] MetaMaskNativeMultiChainHDWallet (NEW CLASS, separate file) + | + +-- EVM chains --> Direct EIP-1193 RPC (unchanged) + | + +-- Bitcoin --> Bitcoin Wallet Standard (@metamask/bitcoin-wallet-standard) + | PSBT signing, native SegWit + | + +-- Solana --> Wallet Standard (standard:connect, signTransaction, etc.) + | + +-- UTXO (non-BTC) --> REMOVED (LTC, DOGE, BCH not natively supported by MM) + | + +-- Cosmos chains --> REMOVED (ATOM, THOR not natively supported by MM) +``` + +### Integration Standards Per Chain + +| Chain | Standard | Library | Signing | +|-------|----------|---------|---------| +| EVM (all) | EIP-1193 + EIP-6963 | Native provider | eth_sendTransaction, personal_sign, eth_signTypedData_v4 | +| Bitcoin | Bitcoin Wallet Standard | `@metamask/bitcoin-wallet-standard` | PSBT (Partially Signed Bitcoin Transactions) | +| Solana | Wallet Standard | `@solana/wallet-standard` | signTransaction, signAllTransactions | +| TRON | TBD | TBD | TBD (newly added Jan 2026, needs research) | + +--- + +## Chain Coverage Comparison + +### What we GAIN with native multichain + +| Chain | Snap | Native | Notes | +|-------|------|--------|-------| +| Bitcoin | Yes (snap BIP-32 entropy + hdwallet-native) | Yes (Bitcoin Wallet Standard, PSBT) | Better UX, no snap install needed | +| Solana | No | Yes (Wallet Standard) | NEW chain support via MM | +| TRON | No | Yes (native since Jan 2026) | NEW chain support via MM | + +### What we LOSE with native multichain + +| Chain | Snap | Native | Notes | +|-------|------|--------|-------| +| Litecoin | Yes | No | MM doesn't support LTC natively | +| Dogecoin | Yes | No | MM doesn't support DOGE natively | +| Bitcoin Cash | Yes | No | MM doesn't support BCH natively | +| Cosmos | Yes | No | MM doesn't support ATOM natively | +| THORChain | Yes | No | MM doesn't support RUNE natively | +| Osmosis | Yes (partial) | No | MM doesn't support OSMO natively | + +### What stays the SAME + +| Chain | Notes | +|-------|-------| +| All EVM chains | EIP-1193 passthrough, identical behavior | + +### Decision: Chain Loss Acceptance + +With flag ON (native multichain), MetaMask users will lose access to LTC, DOGE, BCH, ATOM, THOR, OSMO. This is acceptable because: +1. These chains had limited usage via the snap +2. The snap UX was inferior (install/update prompts, confirmation dialogs) +3. Users can still access these chains via other wallets (Native, Ledger, etc.) +4. We gain Solana and TRON support which are higher value + +**TODO: Validate this assumption with product/community. May want to keep snap as fallback for specific chains.** + +--- + +## Feature Flag Strategy + +### Flag Name + +``` +VITE_FEATURE_MM_NATIVE_MULTICHAIN +``` + +### Flag Semantics + +| Flag State | Behavior | +|------------|----------| +| **OFF (default, prod)** | Existing MetaMaskMultiChainHDWallet with snap support. Zero changes to any code path. | +| **ON (dev)** | New MetaMaskNativeMultiChainHDWallet. No snap. Native BTC/SOL (TRX deferred). No LTC/DOGE/BCH/ATOM/THOR. | + +### Flag Enforcement Points + +1. **Adapter factory** (`MetaMaskAdapter.useKeyring`): Instantiate the correct wallet class based on flag +2. **Chain support** (`_supportsBTC`, `_supportsCosmos`, etc.): Different support matrix per flag +3. **Snap code** (`useIsSnapInstalled`, `useSnapStatusHandler`, snap modals): Completely disabled when flag ON +4. **Account discovery**: Different logic for which chains to discover +5. **Signing paths**: BTC uses PSBT instead of snap, SOL uses Wallet Standard, etc. + +### Flag Safety + +- The flag check happens at the **adapter level**, before any wallet instance is created +- When OFF, the new wallet class is never instantiated, never imported (dynamic import) +- No shared mutable state between old and new wallet classes +- Tests must cover both flag states + +--- + +## Implementation Phases + +### Phase 1: Foundation + +- [ ] Create feature flag `VITE_FEATURE_MM_NATIVE_MULTICHAIN` +- [ ] Create new wallet class `MetaMaskNativeMultiChainHDWallet` (or extend existing with flag-gated behavior) +- [ ] Wire up adapter factory to choose wallet class based on flag +- [ ] EVM passthrough (copy from existing, should be identical) + +### Phase 2: Bitcoin via Bitcoin Wallet Standard + +- [ ] Add `@metamask/bitcoin-wallet-standard` dependency +- [ ] Implement `bitcoin:connect` for address derivation +- [ ] Implement `bitcoin:signTransaction` (PSBT signing) +- [ ] Implement BTC address retrieval (native SegWit / bech32) +- [ ] Wire up to hdwallet BTC interfaces (`btcGetAddress`, `btcSignTx`, `btcGetPublicKeys`) +- [ ] Handle the PSBT <-> hdwallet transaction format translation + +### Phase 3: Solana via Wallet Standard + +- [ ] Implement Wallet Standard connection for Solana +- [ ] Implement `signTransaction`, `signAllTransactions`, `signMessage` +- [ ] Wire up to hdwallet Solana interfaces +- [ ] Address derivation and public key retrieval + +### Phase 4: Account Migration + +- [ ] Detect old MetaMaskMultichain + snap accounts +- [ ] Programmatically nuke snap-derived accounts +- [ ] Re-derive accounts using native multichain +- [ ] Only trigger migration when flag is ON AND old snap accounts exist +- [ ] **TODO**: Determine if we can detect snap vs native accounts reliably + +### Phase 5: Snap Removal (flag ON only) + +- [ ] Disable all snap install/update modals +- [ ] Disable `useIsSnapInstalled` polling +- [ ] Disable `useSnapStatusHandler` +- [ ] Remove snap-related UI routes (`/metamask/snap/install`, `/metamask/snap/update`) +- [ ] Skip snap chain discovery for LTC, DOGE, BCH, ATOM, THOR + +### Phase 6: TRON via MM Native (DEFERRED) + +**Status: Deferred to follow-up.** MetaMask TRON dapp connectivity is NOT live yet (only send/receive/stake from within MM UI). When it launches, MM will use CAIP-25 Multichain API via `@metamask/connect-tron` (NOT TronLink-compatible, NOT Wallet Standard). + +**When ready:** +- [ ] Add `@metamask/connect-tron` + `@metamask/multichain-api-client` dependencies +- [ ] Implement TRON address retrieval via `MetaMaskAdapter.connect()` +- [ ] Implement TRON signing via `MetaMaskAdapter.signTransaction()` (receives `raw_data_hex` + contract type, returns signature) +- [ ] Wire up to `TronWallet` interface in the new wallet class +- [ ] ShapeShift already has full TRON chain adapter, hdwallet interfaces, and asset data - no chain-level work needed + +### Phase 7: Multi-Account Discovery + +- [ ] Query MM for all available multichain accounts (not just account 0) +- [ ] Map MM account indexes to ShapeShift account discovery +- [ ] Each MM account = 1 EVM + 1 BTC + 1 SOL + 1 TRX address set + +### Phase 8: i18n & Copy + +- [ ] Add all new strings to `src/assets/translations/en/main.json` +- [ ] Deprecation modal copy (title, body, buttons, warnings) +- [ ] Settings toggle copy (mode label, switch buttons) +- [ ] Confirmation modal copy (both directions: snap->native, native->snap) +- [ ] Banner copy for deprecated snap mode +- [ ] Run `/translate` to generate translations for all supported languages +- [ ] Review translations for accuracy (wallet/crypto terminology is tricky) + +--- + +## Account Migration Strategy + +### Problem + +When switching from snap to native multichain, accounts derived via `snap_getBip32Entropy` need to be replaced with natively-derived accounts. The derivation paths differ (snap uses BIP-44 legacy, native uses BIP-84 native SegWit), so addresses WILL be different. + +### Account Store Architecture + +The portfolio slice stores accounts in a normalized structure: +- `portfolio.wallet.byId[walletId]` = all `AccountId[]` for a wallet (master list) +- `portfolio.accountMetadata.byId[accountId]` = BIP44 params, account type, label +- `portfolio.accountBalances.byId[accountId]` = balances per asset +- `portfolio.accounts.byId[accountId]` = asset IDs, activity flag +- `portfolio.enabledAccountIds[walletId]` = which accounts are active + +**AccountId format**: `chainId:account` (e.g. `eip155:1:0xaddress`, `bip122:...:xpub...`) + +### Existing Utilities + +- `selectPartitionedAccountIds()` in `common-selectors.ts` already splits EVM vs non-EVM accounts +- `selectAccountIdsWithoutEvms()` gets only non-EVM accounts +- `clearWalletPortfolioState(walletId)` exists but nukes EVERYTHING (too aggressive) +- `clearWalletMetadata(walletId)` clears wallet mapping but preserves metadata +- `useSnapStatusHandler` already handles snap install/uninstall state changes and invalidates discovery queries + +### Nuke/Re-derive Flow + +**Step 1: Identify snap-derived accounts** +``` +allMetaMaskAccountIds = portfolio.wallet.byId[metaMaskWalletId] +snapAccountIds = allMetaMaskAccountIds.filter(id => { + const { chainId } = fromAccountId(id) + return !isEvmChainId(chainId) // Everything non-EVM is snap-derived +}) +``` + +**Step 2: Selective clearing (new portfolio action)** +```typescript +// New action: clearNonEvmAccountsForWallet(walletId, nonEvmAccountIds) +// Removes ONLY non-EVM accounts from all portfolio state +// Preserves EVM accounts completely untouched +``` + +**Step 3: Trigger re-discovery** +```typescript +queryClient.invalidateQueries({ + queryKey: ['useDiscoverAccounts', { deviceId }], + exact: false, + refetchType: 'all', +}) +// useDiscoverAccounts() re-runs with the NEW wallet class +// Derives native BTC/SOL accounts via Wallet Standard +``` + +### What Survives the Nuke + +| Data | Preserved? | Notes | +|------|-----------|-------| +| EVM account metadata | Yes | Same address, same everything | +| EVM balances | Yes | Untouched | +| EVM wallet mapping | Yes | Same deviceId | +| Non-EVM metadata | No | Cleared, re-derived | +| Non-EVM balances | No | Cleared, re-fetched | +| Wallet ID & name | Yes | Same device | +| Stored mode preference | Yes | localStorage, separate from Redux | + +### Addresses Will Change + +- **Snap BTC**: `m/44'/0'/0'/0/0` via secp256k1 from `snap_getBip32Entropy` -> legacy/P2PKH address +- **Native BTC**: MM internal BIP-84 derivation -> native SegWit (bc1q...) address +- **Snap Cosmos/THOR**: `m/44'/118'/0'/0/0` or `m/44'/931'/0'/0/0` via snap -> bech32 address +- **Native SOL**: MM internal BIP-44 `m/44'/501'/0'/0'` -> base58 address (NEW, didn't exist with snap) + +--- + +## UX Migration / Snap Deprecation Plan + +**TODO: Full UX design later. Below is the high-level flow.** + +### Deprecation Flow (flag ON only) + +When the flag is ON and a user connects MetaMask: + +1. **Detect snap**: Check `wallet_getSnaps` for the ShapeShift snap (`npm:@shapeshiftoss/metamask-snaps`) +2. **If snap is installed**: Show a **deprecation modal** explaining: + - MetaMask now natively supports multichain (BTC, SOL, TRX) + - The ShapeShift snap is being deprecated + - Native support = better UX, no snap updates, more chains + - **Option A**: "Switch to native multichain" (recommended) - nukes snap-derived accounts, re-derives natively + - **Option B**: "Keep using snap for now" - stays on old path, but with a warning that this will go away +3. **If snap is NOT installed**: Use native multichain directly, no modal needed +4. **Eventually**: Remove Option B, snap support fully deprecated + +### Modal Design + +**TODO: Design the actual modal. Should be sexy, informative, not scary.** + +Key points to communicate: +- Your EVM addresses stay the same +- Your non-EVM addresses WILL change (different derivation) +- If you have funds on old snap-derived BTC addresses, you'll need to move them first +- Native multichain gives you Solana + TRON support that the snap didn't have + +### Connection Flow (flag ON, io.metamask RDNS only) + +``` +User connects MetaMask (io.metamask) + | + +-- Is snap installed? + | | + | +-- YES: Is there a stored preference for this deviceId? + | | | + | | +-- "use_native" -> New class, done + | | +-- "keep_snap" -> Old class (snap), done + | | +-- No preference -> Show deprecation modal (choice: native vs snap) + | | | + | | +-- "Switch to native" -> New class, store pref, nuke snap accounts + | | +-- "Keep using snap" -> Old class, store pref + | | + | +-- NO: New class (native). Show one-time info modal: + | "MetaMask now natively supports multichain!" + | [BTC icon] Bitcoin [SOL icon] Solana + | "You'll automatically get these chains with your MetaMask wallet." + | [Got it] + | (stored in localStorage, never shown again for this deviceId) +``` + +**Non-MetaMask MIPD wallets** (Rabby, Ctrl, etc.): Use new class for EVM, no modal, no multichain features. The info/deprecation modals are io.metamask ONLY. + +### Deprecation Modal Copy (draft) + +> **MetaMask now supports multichain natively!** +> +> Your MetaMask wallet can now manage Bitcoin and Solana accounts without the ShapeShift snap. +> +> **What changes:** +> - Your EVM addresses stay the same +> - You'll get new BTC, SOL, and TRX addresses derived natively by MetaMask +> - If you have BTC on your current snap-derived address, transfer it to another wallet first +> +> [Switch to native multichain] (recommended) +> [Keep using snap for now] + +### Mode Switch Confirmation Modal (draft) + +When switching modes (from settings or initial deprecation prompt), show a confirmation modal with chain icons: + +**Switching to Native Multichain:** +> **Supported chains with native multichain:** +> [BTC icon] Bitcoin [SOL icon] Solana [ETH icon] All EVM chains +> +> **Only available with Snap:** +> [LTC icon] Litecoin [DOGE icon] Dogecoin [BCH icon] Bitcoin Cash [ATOM icon] Cosmos [RUNE icon] THORChain [OSMO icon] Osmosis +> +> Your EVM addresses will stay the same. Non-EVM addresses will be re-derived. + +**Switching back to Snap:** +> **Supported chains with Snap:** +> [BTC icon] Bitcoin [LTC icon] Litecoin [DOGE icon] Dogecoin [BCH icon] Bitcoin Cash [ATOM icon] Cosmos [RUNE icon] THORChain [ETH icon] All EVM chains +> +> **Only available with native multichain:** +> [SOL icon] Solana +> +> Your EVM addresses will stay the same. Non-EVM addresses will be re-derived. + +### UX Quality Bar + +The entire flow should look and feel great. This is a premium feature transition, not a janky config toggle. + +- **Modals**: Use the existing ShapeShift modal system. Consistent styling, proper animations, responsive. +- **Chain icons**: Real chain icons from the asset service, not text. Displayed in a clean grid/row with chain names. +- **Copy**: Friendly, non-technical, reassuring. No jargon. Users shouldn't feel like they're making a scary technical choice. +- **Loading states**: When nuking/re-deriving accounts, show a proper loading state with a message like "Setting up your accounts..." - not a blank screen. +- **Success state**: After switching, show a brief success confirmation before closing the modal. +- **Settings toggle**: Should feel native to the existing wallet menu drawer. Not bolted on. +- **Banner (snap deprecated)**: If user is on snap mode, show a tasteful, dismissible banner - not aggressive or ugly. +- **i18n**: ALL strings through `en/main.json`. Run `/translate` for all supported languages. Review crypto/wallet terminology per language. +- **Animations**: Follow existing app patterns. No custom animations unless the design system supports them. +- **Error handling**: If native multichain connection fails (e.g. MM doesn't support BTC yet on user's version), degrade gracefully. Show what worked, explain what didn't. +- **Mobile**: Test the modals and settings on mobile viewport. MetaMask mobile v7.57+ supports native multichain. + +### Settings Toggle (MM Menu Drawer) + +When flag is ON, the MetaMask wallet menu drawer gets a new section: + +**If currently using native:** +> Multichain Mode: **Native** +> [Switch to Multichain Snap] -> Opens mode switch confirmation modal (see above) + +**If currently using snap:** +> Multichain Mode: **Snap (deprecated)** +> [Switch to Native Multichain] -> Opens mode switch confirmation modal (see above) + +Each switch triggers: +1. Confirmation modal with chain icons and clear copy about what changes +2. Nuke non-EVM accounts +3. Re-derive accounts with the chosen mode +4. Update stored preference +5. EVM accounts remain untouched + +### Technical Notes + +- **Preference persistence**: Store choice in localStorage, keyed by deviceId. Per-wallet, not global. +- **Dual class support**: Flag ON = BOTH classes available. Adapter factory checks stored preference. +- **Flag ON + no snap installed**: Always use new class. NEVER propose snap installation. +- **Flag OFF**: Always use old class. Zero changes. Snap install/update modals work as before. +- When user switches modes: + 1. Nuke all non-EVM accounts from the account store + 2. Instantiate the correct wallet class + 3. Re-derive accounts using the chosen mode + 4. EVM accounts remain untouched (same address from same seed) + 5. Store/update preference + 6. Do NOT touch the snap installation itself + +--- + +## Open Questions & Decisions + +### Decided + +1. **Flag approach**: Single feature flag `VITE_FEATURE_MM_NATIVE_MULTICHAIN`, flag OFF in prod +2. **Chain loss**: Acceptable to lose LTC, DOGE, BCH, ATOM, THOR, OSMO when flag ON +3. **MIPD dual-purpose**: Native multichain is MetaMask-only (`io.metamask`), other MIPD wallets use existing EVM-only path +4. **No UX migration plan now**: TODO for later +5. **Account migration**: Nuke snap accounts, re-derive natively. Programmatic only. +6. **Zero regression**: Flag OFF = zero changes to existing behavior +7. **New wallet class**: New `MetaMaskNativeMultiChainHDWallet` class. Cleanest separation, zero risk of flag leaking into existing code paths. The adapter factory decides which class to instantiate based on the flag. Existing class remains 100% untouched. +8. **Per-chain standards**: Use Bitcoin Wallet Standard + Solana Wallet Standard (whatever is on prod MM today). No CAIP-25/Flask dependency. Battle-tested, works now. +9. **Hardware wallet gap**: Accept it gracefully. If we can't get non-EVM addresses from MM (e.g. HW-backed account), we simply don't show those chains. The implementation handles failure naturally - no special-casing needed. + +10. **PSBT translation in wallet class**: The wallet class receives `BTCSignTx`, internally converts to PSBT, sends to MM via Bitcoin Wallet Standard, gets back signed PSBT. Chain adapter stays untouched. This follows the existing abstraction boundary where wallet classes handle wallet-specific signing formats. +11. **Solana in v1**: Full BTC + SOL support. Implement `SolanaWallet` interface in the new wallet class using Solana Wallet Standard. +12. **Same package**: Both classes in `packages/hdwallet-metamask-multichain`. The adapter factory (already there) handles class selection via flag. Shared MIPD/EIP-6963 infra. Separate source files. Zero coupling between old and new wallet classes beyond shared types. + +13. **Multi-account: match MM's model**: Discover as many accounts as MM exposes. If MM has 3 multichain accounts, we see 3. Each account has 1 EVM address + 1 BTC address + 1 SOL address + 1 TRX address. +14. **TRON: deferred**: MM TRON dapp connectivity isn't live yet. Skip TRON in v1, add in follow-up when MM ships it. Uses CAIP-25 Multichain API (NOT Wallet Standard like BTC/SOL), via `@metamask/connect-tron`. +15. **Snap cleanup: leave it installed**: Don't auto-uninstall the snap. But show a deprecation modal when snap is detected, letting user choose between native multichain and legacy snap path. The snap itself stays installed - user's choice to uninstall. + +16. **Deprecation modal persistence**: Ask once, remember choice in localStorage (deviceId-scoped). Show dismissible banner on subsequent connects if user chose "keep snap". +17. **Fund warning**: Simple warning in modal: "If you have BTC on your current snap-derived address, transfer it first." No balance check, no extra flow. +18. **Dual class with flag ON**: Both old and new classes available when flag ON. User preference (from deprecation modal) decides which to instantiate. Flag OFF = old class only, zero changes. +19. **TRON already integrated**: TRON has full chain adapter, hdwallet interfaces (native + Ledger), no feature flag. Just wire up TronWallet interface in the new MM native wallet class. + +20. **New class for all MIPD wallets**: When flag ON, ALL MIPD wallets (MetaMask, Rabby, Ctrl, etc.) use the new class. EVM behavior is identical. Non-MM wallets just won't get BTC/SOL (TRX deferred) features since those are MM-specific standards. +21. **Reversible mode**: Users can switch between native and snap modes via the MM wallet menu drawer settings. Each switch nukes non-EVM accounts and re-derives. Modal confirmation for each direction. +22. **Flag ON = never propose snap install**: If flag is ON and snap is not installed, use native derivation. Never show snap install/update modals. +23. **Tracking**: One epic with sub-tasks per phase in beads. +24. **Display name**: New class shows as `MetaMask (Native Multichain)` in the UI. Old class keeps `MetaMask(ShapeShift Multichain)`. +25. **i18n**: ALL user-facing copies (deprecation modal, settings toggle, confirmation modals, banners, warnings) go through `en/main.json` and must be translated via `/translate`. No hardcoded English strings in components. +26. **Explicit chain list in modals**: Show chain icons + names for both "available with this mode" and "only available with the other mode". Clean grid layout, not a wall of text. +27. **Accept any BTC address format from MM**: Whatever address format MM returns (bc1q, bc1p, 1..., 3...), we accept and handle. No restrictions. +28. **UX quality**: The entire flow (modals, settings, transitions, loading states, success states) must look and feel polished and native to the ShapeShift design system. +29. **MM capability detection via Wallet Standard**: There is NO way to get MM extension version from a dapp (`web3_clientVersion` returns node version, not extension version). Use feature detection instead: `findWalletStandardWallet('MetaMask', 'bitcoin')` returns null if MM doesn't support native BTC. Same for SOL. If neither BTC nor SOL wallet registrations exist, MM is too old for native multichain - fall back to snap or show "update MetaMask" prompt. + +### To Decide + +(none currently - will add as they come up during implementation) + +--- + +## Technical Deep Dive: Wallet Standard Discovery + +### How It Works + +The Wallet Standard is chain-agnostic EIP-6963. Same event-based discovery, but extends beyond EVM. + +**Packages needed (app side only):** + +| Package | Purpose | +|---------|---------| +| `@wallet-standard/app` | `getWallets()` - wallet discovery | +| `@wallet-standard/base` | TypeScript types (`Wallet`, `WalletAccount`) | +| `@solana/wallet-standard-features` | Solana feature type definitions | + +We do NOT need `@metamask/bitcoin-wallet-standard` or `@metamask/solana-wallet-standard` - those run inside the MM extension. + +### Key Architecture: MM Registers SEPARATE Wallets + +MetaMask registers SEPARATE Wallet Standard wallet objects for Bitcoin and Solana: +- `getWallets().get()` returns two entries named "MetaMask" - one with `bitcoin:*` chains/features, one with `solana:*` chains/features +- This is by design + +### Discovery Flow + +```typescript +import { getWallets } from '@wallet-standard/app'; + +const { get, on } = getWallets(); + +// Find MetaMask Bitcoin wallet +const mmBtc = get().find(w => w.name === 'MetaMask' && w.chains.some(c => c.startsWith('bitcoin:'))); + +// Find MetaMask Solana wallet +const mmSol = get().find(w => w.name === 'MetaMask' && w.chains.some(c => c.startsWith('solana:'))); +``` + +### Parallel Discovery Systems + +- **MIPD (EIP-6963)**: Continues to handle EVM wallet discovery. Untouched. +- **Wallet Standard**: NEW, handles BTC/SOL discovery via MetaMask native multichain. +- The two systems are independent and coexist. + +### New Utility File: `src/lib/walletStandard.ts` + +```typescript +import { getWallets, type Wallets } from '@wallet-standard/app'; +import type { Wallet } from '@wallet-standard/base'; + +let walletsApi: Wallets | undefined; + +export function getWalletStandardApi(): Wallets { + if (!walletsApi) walletsApi = getWallets(); + return walletsApi; +} + +export function findWalletStandardWallet( + name: string, + chainPrefix: 'bitcoin' | 'solana', +): Wallet | undefined { + return getWalletStandardApi().get().find( + w => w.name === name && w.chains.some(c => c.startsWith(`${chainPrefix}:`)), + ); +} +``` + +--- + +## Technical Deep Dive: BTC PSBT Translation + +### Existing PSBT Usage in Codebase + +Good news: the codebase ALREADY constructs PSBTs in three wallet implementations: +- **Phantom** (`packages/hdwallet-phantom/src/bitcoin.ts:101-173`) - simplest, p2wpkh only +- **Vultisig** (`packages/hdwallet-vultisig/src/bitcoin.ts:103-175`) - identical to Phantom +- **Native** (`packages/hdwallet-native/src/bitcoin.ts:220-357`) - most comprehensive, handles all script types + +Library: `@shapeshiftoss/bitcoinjs-lib@7.0.0-shapeshift.2` (custom fork, already in deps) + +### BTCSignTx -> PSBT Mapping + +| BTCSignTx Field | PSBT Equivalent | Notes | +|---|---|---| +| `inputs[].txid` | `addInput({ hash })` | Direct 1:1 | +| `inputs[].vout` | `addInput({ index })` | Direct 1:1 | +| `inputs[].hex` | `addInput({ nonWitnessUtxo })` | Full prev tx for legacy | +| `inputs[].amount` | `addInput({ witnessUtxo.amount })` | For segwit inputs | +| `inputs[].sequence` | `addInput({ sequence })` | Direct (RBF) | +| `outputs[].address` | `addOutput({ address })` | Direct | +| `outputs[].amount` | `addOutput({ value: BigInt() })` | Convert to BigInt | +| `opReturnData` | `addOutput({ script })` | OP_RETURN via `bitcoin.payments.embed()` | + +### Implementation Strategy + +**Phase 1 (MVP): Native SegWit only (p2wpkh)** +- Follow Phantom's pattern exactly - it's the simplest and most battle-tested +- ~200 LOC, low risk +- Covers the majority of modern BTC usage + +**Phase 2: Add legacy support** +- Accept non-segwit inputs with `nonWitnessUtxo` directly +- ~50 LOC additional + +**Note on MM Bitcoin Wallet Standard**: MM returns signed PSBTs. We receive the PSBT back, extract the signed transaction, and return it in hdwallet's expected format (`{ serializedTx, signatures }`). The Phantom pattern for signature extraction works: `signedPsbt.data.inputs[].partialSig`. + +--- + +## Technical Deep Dive: Solana Wallet Standard Integration + +### SolanaWallet Interface (hdwallet-core) + +```typescript +interface SolanaWallet { + solanaGetAddress(msg: SolanaGetAddress): Promise + solanaSignTx(msg: SolanaSignTx): Promise + solanaSignSerializedTx?(msg: SolanaSignSerializedTx): Promise + solanaSendTx?(msg: SolanaSignTx): Promise +} +``` + +### Phantom Implementation (Reference) + +Phantom's pattern is our template: +1. Build `VersionedTransaction` via `core.solanaBuildTransaction(msg)` +2. Call `provider.signTransaction(transaction)` - returns signed `VersionedTransaction` +3. Serialize + extract signatures, return as base64 + +### MM Wallet Standard Equivalent + +```typescript +// Connect +const accounts = await wallet.features['standard:connect'].connect() +// accounts[0].address = base58 Solana address +// accounts[0].publicKey = Uint8Array + +// Sign +const [result] = await wallet.features['solana:signTransaction'].signTransaction({ + account: accounts[0], + transaction: serializedVersionedTransaction, // Uint8Array +}) +// result.signedTransaction = Uint8Array (signed VersionedTransaction) +``` + +### Translation Flow + +``` +SolanaSignTx (hdwallet format) + -> core.solanaBuildTransaction() -> VersionedTransaction + -> serialize() -> Uint8Array + -> wallet.features['solana:signTransaction'] -> signed Uint8Array + -> VersionedTransaction.deserialize() -> extract signatures + -> SolanaSignedTx { serialized: base64, signatures: [base64] } +``` + +No changes to `hdwallet-core` interfaces needed. The new wallet class handles all translation internally. + +--- + +## Technical Deep Dive: Integration Standards + +### Bitcoin Wallet Standard + +```typescript +import { registerBitcoinWalletStandard } from '@metamask/bitcoin-wallet-standard'; + +// Registration +registerBitcoinWalletStandard({ client: multichainClient }); + +// Connect +const connectResult = await wallet.features['bitcoin:connect'].connect({ + purposes: ['payment'] // 'payment' = native SegWit, 'ordinals' = Taproot (coming soon) +}); +// Returns: { addresses: [{ address: 'bc1q...', publicKey: Uint8Array }] } + +// Sign Transaction (PSBT) +const signResult = await wallet.features['bitcoin:signTransaction'].signTransaction({ + psbt: base64EncodedPSBT, + inputsToSign: [{ address: 'bc1q...', signingIndexes: [0, 1], sigHash: 0x01 }] +}); +// Returns: { psbt: signedBase64PSBT } + +// Sign Message +const msgResult = await wallet.features['bitcoin:signMessage'].signMessage({ + message: 'Hello', + address: 'bc1q...' +}); +``` + +### Solana Wallet Standard + +```typescript +// MetaMask appears as a Wallet Standard wallet automatically +// Use @solana/wallet-adapter or direct Wallet Standard + +const wallet = getWallets().find(w => w.name === 'MetaMask'); + +// Connect +const accounts = await wallet.features['standard:connect'].connect(); +// Returns: { accounts: [{ address: 'base58...', publicKey: Uint8Array }] } + +// Sign Transaction +const signedTx = await wallet.features['solana:signTransaction'].signTransaction({ + transaction: serializedTransaction +}); + +// Sign Message +const signature = await wallet.features['solana:signMessage'].signMessage({ + message: new TextEncoder().encode('Hello') +}); +``` + +### MetaMask Multichain API (CAIP-25) - Flask Only + +```typescript +// Extension port communication (not window.ethereum!) +const FLASK_ID = "ljfoeinjpaedjfecbmggjgodbgkmjkjk"; +const port = chrome.runtime.connect(FLASK_ID); + +// Create session +port.postMessage({ + type: "caip-348", + data: { + jsonrpc: "2.0", + method: "wallet_createSession", + params: { + optionalScopes: { + "eip155:1": { methods: ["eth_sendTransaction"], notifications: [], accounts: [] }, + "bip122:000000000019d6689c085ae165831e93": { methods: ["sendTransfer"], notifications: [], accounts: [] }, + "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": { methods: ["signTransaction"], notifications: [], accounts: [] } + } + } + } +}); + +// Invoke method on specific chain +port.postMessage({ + type: "caip-348", + data: { + jsonrpc: "2.0", + method: "wallet_invokeMethod", + params: { + scope: "bip122:000000000019d6689c085ae165831e93", + request: { method: "sendTransfer", params: { ... } } + } + } +}); +``` + +--- + +## Future: CAIP-25 Multichain API + +MetaMask's Multichain API (`wallet_createSession` / `wallet_invokeMethod` / `wallet_revokeSession`) implements CAIP-25 and provides a unified interface for all chains. Currently Flask-only (experimental build), but expected to land in production MM eventually. + +When it does, it could replace the per-chain standard approach: +- Single session creation with all chain scopes +- Unified `wallet_invokeMethod` for any chain-specific call +- Event-based account/session changes via `wallet_sessionChanged` +- Chrome extension port communication (not `window.ethereum`) + +**TODO**: Monitor CAIP-25 production readiness. When stable in production MM, consider migrating from per-chain standards to the unified Multichain API for cleaner code and better multi-chain UX (e.g. simultaneous multi-network interactions without chain switching). + +--- + +## Snap Repository Reference + +- **Repo**: `shapeshift/metamask-snaps` (GitHub) +- **Package**: `@shapeshiftoss/metamask-snaps` (npm) +- **Version**: 1.0.13 +- **Snap ID**: `npm:@shapeshiftoss/metamask-snaps` +- **Key insight**: EVM chains in the snap are NO-OPS - they just proxy to `window.ethereum`. Only UTXO and Cosmos chains actually use snap-derived keys. +- **Manifest declares 6 BIP-32 paths**: `m/44'/0'` (BTC), `m/44'/2'` (LTC), `m/44'/3'` (DOGE), `m/44'/118'` (ATOM/OSMO), `m/44'/145'` (BCH), `m/44'/931'` (THOR) +- **No ed25519 support** - all secp256k1 + +--- + +## MetaMask Native Multichain Reference + +- **Extension**: v13.5+ (multichain accounts) +- **Mobile**: v7.57+ (full parity) +- **Supported natively**: All EVM, Solana, Bitcoin, TRON +- **Account model**: 1 account = 1 EVM address + 1 SOL address + 1 BTC address + 1 TRX address +- **BTC address type**: Native SegWit (bech32). Taproot coming soon. +- **Hardware wallets**: EVM-only (no non-EVM via HW in MM) +- **No multi-account for non-EVM**: Each account has exactly one address per chain +- **Key derivation**: BIP-44, indexes kept in sync across chains diff --git a/MM_TESTING_NOTES.md b/MM_TESTING_NOTES.md new file mode 100644 index 00000000000..a2e937cf6fa --- /dev/null +++ b/MM_TESTING_NOTES.md @@ -0,0 +1,85 @@ +# MM Native Multichain - E2E Testing Notes + +## Environment +- MetaMask v13.20.0 (prod) +- agent-browser session: qabot (headed, 2 tabs: tab 0 = localhost:3000, tab 1 = MM popup) +- Wallet: 0x5daf...61c986 + +## SOL Self-Send (flag ON) - PASS +- Amount: 0.001159151 SOL ($0.10) +- To: ETpdrE...CvWYLp (Account #0, self-send) +- Flow: SOL page > Send > Select Account #0 > Enter amount > Preview > Confirm > MM popup approve +- Result: SUCCESS - "You have successfully sent 0.001159151 SOL" +- Balance updated from 0.10131915 to 0.10131415 SOL +- Tx shows in history as "Today" + +### Bugs Found & Fixed During SOL Testing + +1. **solanaSendTx missing** - Chain adapter calls `wallet.solanaSendTx?.()` but native multichain wallet only had `solanaSignTx`. Added `solanaSendTx` using Wallet Standard `solana:signAndSendTransaction` feature. + +2. **Uint8Array signature not encoded** - Wallet Standard returns `signature: Uint8Array` but chain adapter expects a base58 string. Added `bs58.encode(signature)` conversion. + +3. **Modal reappears on reconnect** - Both `Connect.tsx` and `MipdBody.tsx` always navigate to `/metamask/native-multichain` when flag is on, without checking if user already chose. Fixed: check localStorage for `nativeMultichainPreference_{deviceId}` before navigating. + +4. **shouldShowDeprecationModal race condition** - useEffect updates preference asynchronously but useMemo runs during render with stale null. Fixed: direct `getPreference(deviceId)` localStorage read inside useMemo as sync fallback. + +### Struggles & Workarounds + +- **agent-browser eval vs file eval**: Inline `eval 'expr'` works but `eval /tmp/file.js` throws SyntaxError on MetaMask extension pages (LavaMoat). Always use inline eval on the ShapeShift tab. +- **Tab switching**: MUST use `tab 0`/`tab 1` to switch between ShapeShift and MetaMask. Using `goto` to navigate to MM extension URL kills the ShapeShift page state (lost send dialogs multiple times). +- **MetaMask popup timing**: After clicking Confirm in ShapeShift, need ~2s delay before switching to tab 1 for MM to render the approval popup. +- **Click timeouts**: Some buttons (especially in modals) time out with `click @ref`. Workaround: use inline eval to find elements by text content and `.click()` programmatically. + +## BTC (flag ON) - NOT AVAILABLE, GRACEFUL DEGRADATION +- MetaMask v13.20.0 only registers `solana:mainnet`, `solana:devnet`, `solana:testnet` via Wallet Standard +- No `bitcoin:` chains registered via Wallet Standard (even though MM shows BTC in its own UI) +- Dynamic detection works correctly: `_supportsBTC=false`, BTC not shown in native multichain modal +- BTC asset page loads fine with $0.00 balance, no crash +- Accounts tab: no Bitcoin chain listed (only ETH, SOL, and EVM chains) +- Swap page shows "No Bitcoin address found" message for BTC receive - expected behavior + +## Tron (flag ON) - NOT AVAILABLE, NO dApp API +- MetaMask shows Tron natively in its UI (TRX $2.31, USDT $10.31 visible in MM popup) +- NOT exposed via Wallet Standard (no `tron:` chains registered) +- `wallet_createSession` (CAIP-25) returns "method does not exist / is not available" +- Conclusion: MetaMask Tron is UI-only, no programmable dApp API exists in prod v13.20.0 +- Cannot implement Tron support until MetaMask ships a dApp-facing API for it + +## Regression Testing (flag OFF) - PASS +- Toggled `VITE_FEATURE_MM_NATIVE_MULTICHAIN=false` in `.env.development`, waited for HMR +- Connected MetaMask via Connect Wallet > MetaMask > Pair +- **No native multichain modal appeared** - snap install modal showed instead +- Snap install modal shows correctly: "Multichain support is now available for MetaMask!" with "Add Snap" button +- All chain icons displayed (40+ chains supported by snap) +- "Don't ask again" checkbox present +- Zero regression: native multichain code paths not executed when flag OFF + +## Regression Testing (flag ON, reconnect flow) - PASS +- Modal correctly skipped on reconnect when preference already stored +- Preference key: `nativeMultichainPreference_io.metamask:0x5daf...61c986` = `native` + +## Native Multichain Modal Flow (flag ON, fresh user) - PASS +- Cleared localStorage preferences, reloaded page +- Modal auto-opened: "MetaMask now natively supports Solana" +- Shows MetaMask fox logo, Solana chain icon, "Use native multichain" CTA +- Clicking "Use native multichain" stores preference, dismisses modal, re-pairs wallet +- After re-pair: SOL balance re-populated (0.10131415 SOL), accounts re-discovered +- Subsequent reloads: no modal (preference persisted) + +## Regression Testing (flag OFF, disconnect + reconnect) - PASS +- Flag toggled to false in .env.development, HMR picked up +- Cleared native multichain preferences from localStorage +- Disconnected wallet via wallet menu > Disconnect +- Reconnected via Connect Wallet > MetaMask > Pair +- Snap install modal appeared correctly (not native multichain modal) +- "Multichain support is now available for MetaMask!" with "Add Snap" button +- All 40+ chain icons displayed, "Don't ask again" checkbox present +- Zero regression from native multichain code paths + +## Code Review Summary +- 3 parallel review agents ran: native-multichain.ts, UI/hooks, state/portfolio +- Feature flag guards solid across all files - flag OFF = zero regression +- Portfolio cleanup on snap->native migration is comprehensive +- Race conditions in connect flows mitigated by promise deduplication locks +- Test coverage thin but functional for flag guard logic +- No showstopper bugs for happy path diff --git a/README.md b/README.md index 5f79d15a273..35fb6330349 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,9 @@ pnpm run dev > It opens [http://localhost:3000](http://localhost:3000) to view it in the browser and the page will reload if you make edits. > -> You will also see any lint errors in the console. +> **Portless (optional):** Install [Portless](https://github.com/vercel-labs/portless) globally (`npm i -g portless`), then `dev:web` uses it automatically. The app is available at `http://.web.localhost:1355`. Without Portless, use `dev:web:localhost` for plain `localhost:3000`. +> +> You will also see any lint errors in the console.
diff --git a/docs/features/chainflip-lending-borrow-overview.md b/docs/features/chainflip-lending-borrow-overview.md new file mode 100644 index 00000000000..fc2caf8fc22 --- /dev/null +++ b/docs/features/chainflip-lending-borrow-overview.md @@ -0,0 +1,266 @@ +# Chainflip Lending - Borrow, Collateral & Repay (Product Overview) + +This document covers the loan lifecycle in Chainflip lending: borrowing against collateral, managing collateral positions, repaying loans, and voluntary liquidation. + +## Prerequisites + +Before any borrow/collateral operations, the user must have: +1. A funded and registered State Chain account (see deposit overview) +2. Assets in their **free balance** (deposited via the deposit flow) +3. A refund address set for relevant chains + +## Core Concepts + +### Collateral and Loans + +Chainflip lending uses an **overcollateralized** model. To borrow, you must first move assets from your free balance into collateral, then request a loan against that collateral. + +``` +Free Balance ----[add_collateral]----> Collateral + | + [request_loan] + | + v + Loan Created + (borrowed funds + land in free balance) +``` + +The collateral backs ALL your loans as a single blended position. There's one LTV ratio per account, not per loan. + +### LTV (Loan-to-Value) Ratio + +LTV = total debt / total collateral value (in USD, via Chainflip oracles). + +| Threshold | LTV | What Happens | +|-----------|-----|-------------| +| Target | 80% | Maximum LTV at loan creation | +| Topup | 85% | Collateral topup starts getting suggested | +| Soft Liquidation Abort | 88% | Soft liquidation stops if LTV drops below | +| Soft Liquidation | 90% | Protocol starts selling collateral in $10k chunks (0.5% max slippage) | +| Hard Liquidation Abort | 93% | Hard liquidation stops if LTV drops below | +| Hard Liquidation | 95% | Protocol sells collateral in $50k chunks (5% max slippage) | +| Low LTV | 50% | Below this, reduced fees apply | + +Liquidations are "friendly" - the protocol sells collateral in chunks to bring LTV back to a safe level, rather than liquidating the entire position. This is a key differentiator from THORChain lending. + +### Oracle Prices + +All LTV calculations use Chainflip's on-chain oracle prices, updated by validators. Current price status from `cf_oracle_prices`: + +| Asset | Status | +|-------|--------| +| BTC/USD | UpToDate | +| ETH/USD | UpToDate | +| SOL/USD | UpToDate | +| USDC/USD | UpToDate | +| USDT/USD | UpToDate | + +## Borrow Flow + +### What It Does + +Takes an amount to borrow from a lending pool, optionally adding extra collateral in the same transaction. The borrowed funds land in the user's free balance. + +### xstate Machine: `borrowMachine` + +``` ++-------+ SUBMIT +---------+ CONFIRM +---------+ +| input | -------------> | confirm | ------------> | signing | ++-------+ (amount, +---------+ +----+----+ + ^ collateral | + | topup asset, SIGN_SUCCESS + | extra | + | collateral, v + | projected +------+------+ + | LTV) | confirming | + | +------+------+ + | | + | DONE BORROW_CONFIRMED + +<------------ success <-------------------------------+ +``` + +### Context + +- `borrowAmountCryptoPrecision` / `borrowAmountCryptoBaseUnit` - how much to borrow +- `collateralTopupAssetId` - optional asset to auto-topup collateral from free balance +- `extraCollateral[]` - array of `{ assetId, amountCryptoBaseUnit }` for additional collateral +- `currentLtvBps` / `projectedLtvBps` - current and projected LTV after borrow +- Real-time LTV sync via `SYNC_LTV` event + +### Protocol Call + +SCALE-encoded `lendingPools.requestLoan(loanAsset, loanAmount, collateralTopupAsset, extraCollateral[])` -> EIP-712 sign -> submit. If extra collateral is provided, the protocol atomically adds collateral and creates the loan. + +### Minimums + +- Minimum loan creation: $100 USD +- Minimum loan update (expand): $10 USD + +## Collateral Flow (Add/Remove) + +### What It Does + +Moves assets between free balance and collateral. Two modes: `add` (free balance -> collateral) and `remove` (collateral -> free balance). + +### xstate Machine: `collateralMachine` + +``` ++-------+ SUBMIT +---------+ CONFIRM +---------+ +| input | -------------> | confirm | ------------> | signing | ++-------+ (amount, +---------+ +----+----+ + ^ mode) | + | SIGN_SUCCESS + | | + | v + | DONE +------+------+ + +<------------ success <----------------------- | confirming | + +-------------+ +``` + +### Context + +- `mode`: `'add'` or `'remove'` +- `collateralAmountCryptoPrecision` / `collateralAmountCryptoBaseUnit` +- `freeBalanceCryptoBaseUnit` - for add mode max (synced via `SYNC_FREE_BALANCE`) +- `collateralBalanceCryptoBaseUnit` - for remove mode max (synced via `SYNC_COLLATERAL_BALANCE`) + +### Protocol Calls + +- **Add**: `lendingPools.addCollateral(collateralTopupAsset, collateral[])` - array of `{ chain, asset, amount }` +- **Remove**: `lendingPools.removeCollateral(collateral[])` - can only remove if resulting LTV stays below target (80%) + +### Minimums + +- Minimum collateral update: $10 USD + +## Repay Flow + +### What It Does + +Repays principal on an existing loan using funds from the free balance. Supports partial and full repayment. + +### xstate Machine: `repayMachine` + +``` ++-------+ SUBMIT +---------+ CONFIRM +---------+ +| input | -------------> | confirm | ------------> | signing | ++-------+ (amount, +---------+ +----+----+ + ^ loanId, | + | isFull SIGN_SUCCESS + | Repayment) | + | v + | DONE +------+------+ + +<------------ success <----------------------- | confirming | + +-------------+ +``` + +### Context + +- `loanId` - which loan to repay +- `repayAmountCryptoPrecision` / `repayAmountCryptoBaseUnit` +- `isFullRepayment` - whether to repay the full outstanding debt +- `freeBalanceCryptoBaseUnit` - max available (synced via `SYNC_FREE_BALANCE`) +- `outstandingDebtCryptoBaseUnit` - remaining debt (synced via `SYNC_DEBT`) + +### Protocol Call + +`lendingPools.makeRepayment(loanId, amount)` where amount is either a specific value or `'full'` for complete repayment. + +### Full Unwind Pattern + +To fully exit a borrow position: +1. Repay loan (free balance -> debt reduction) +2. Remove collateral (collateral -> free balance) +3. Egress (free balance -> on-chain wallet) + +These can potentially be batched via `Environment.batch` (max 10 calls per batch) for a single-signature experience. + +## Voluntary Liquidation Flow + +### What It Does + +Allows the user to voluntarily initiate (or stop) a liquidation of their position. This is useful when you want the protocol to unwind your position for you, selling collateral to repay debt. + +### xstate Machine: `voluntaryLiquidationMachine` + +``` ++---------+ CONFIRM +---------+ SIGN_SUCCESS +------------+ +| confirm | ------------> | signing | ----------------> | confirming | ++---------+ +---------+ +-----+------+ + | | + | BACK LIQUIDATION_CONFIRMED + v | ++-----------+ DONE v +| cancelled | <------------- success <------------------------+ ++-----------+ + (final) +``` + +Note: this machine starts at `confirm` (no input step) and ends at `cancelled` (final state). The `action` context determines whether we're initiating or stopping voluntary liquidation. + +### Protocol Calls + +- **Initiate**: `lendingPools.initiateVoluntaryLiquidation()` - protocol starts unwinding +- **Stop**: `lendingPools.stopVoluntaryLiquidation()` - cancel the voluntary liquidation + +## Interest and Fees + +### Interest Rates (live from `cf_lending_pools`) + +Interest follows a kinked curve: 0% at 0% utilization, 4% at 95% utilization (kink), 25% at 100% utilization. + +| Pool | Total Supplied | Available | Utilization | Interest Rate | Origination Fee | +|------|---------------|-----------|-------------|---------------|-----------------| +| USDC | ~$120,393 | ~$39,228 | 67.44% | 2.84% | 1 bp | +| USDT | ~$408,784 | ~$279,528 | 31.67% | 1.33% | 1 bp | +| BTC | ~0.179 BTC | ~0.179 BTC | 0% | 0% | 5 bp | +| ETH | ~0.030 ETH | ~0.030 ETH | 0% | 0% | 1 bp | +| SOL | ~0.816 SOL | ~0.816 SOL | 0% | 0% | 1 bp | + +Interest is collected every 10 blocks (~60 seconds). + +### Fee Structure + +- **Origination fee**: 5 bps for BTC, 1 bp for all others (charged on loan creation) +- **Liquidation fee**: 5 bps (20% goes to protocol, 80% to liquidators) +- **Interest**: Variable based on utilization curve (see above) + +## Live Account Example + +Current state of the test account showing an active borrow position: + +``` +Collateral: + USDC: ~156.43 + +Loans: + #126: USDC, principal ~101.07, created at block 11,941,970 + +Blended LTV: ~64.65% (well below 80% target) +Liquidation Status: None +Collateral Topup Asset: Not set +``` + +## Safe Mode Status (live) + +All lending operations are currently enabled: +- Borrowing: enabled (all assets) +- Add lender funds: enabled +- Withdraw lender funds: enabled +- Add collateral: enabled +- Remove collateral: enabled +- Liquidations: enabled + +## Composable Operations via Batching (not yet in UI) + +The low-level `encodeBatch` primitive exists in `src/lib/chainflip/scale.ts` and is tested, but the UI state machines currently sign each operation individually. The following batch combinations are **planned/aspirational** and not yet wired into the UI components or xstate machines: + +| Batch | Use Case | +|-------|----------| +| `addCollateral + requestLoan` | First borrow (add collateral and borrow in one sign) | +| `makeRepayment + removeCollateral` | Partial unwind | +| `makeRepayment + removeCollateral + withdrawAsset` | Full unwind to on-chain wallet | +| `registerRefundAddress + addCollateral + requestLoan` | First-time borrow with setup | + +Current implementation: each operation gets its own EIP-712 signature, with nonces incremented manually (`lastUsedNonce + 1`) for sequential calls within the same session. Batching would reduce these to a single signature per composed operation. diff --git a/docs/features/chainflip-lending-deposit-overview.md b/docs/features/chainflip-lending-deposit-overview.md new file mode 100644 index 00000000000..e80b655e05e --- /dev/null +++ b/docs/features/chainflip-lending-deposit-overview.md @@ -0,0 +1,241 @@ +# Chainflip Lending - Deposit to State Chain (Product Overview) + +This document covers the first functional flow of the Chainflip lending PoC: depositing assets into the Chainflip State Chain, including one-time account creation setup. + +## High Level Concepts + +### The EVM Account as Identifier + +Your Ethereum address IS your Chainflip lending identity. It's mandatory - you cannot use a Bitcoin, Solana, or any other chain address as your account identifier. The ETH address deterministically maps to a Chainflip State Chain account via left-padding + SS58 encoding (prefix 2112). + +For example, an ETH address `0xABCD...1234` deterministically maps to a State Chain account `cFxxx...yyy`. + +Keep in mind: **you do not need to connect the wallet that holds the funds**. For now the PoC doesn't surface this, but the architecture is evolutive enough that supporting e.g. connecting an EVM wallet only or a Ledger with just ETH accounts and depositing from e.g. QR code is definitely an option we can and should think about. This holds true for the deposit flow below, and also for all other flows in subsequent docs. + +### Three Fund Buckets + +Once assets are on the Chainflip State Chain, they exist in one of three separate buckets: + +``` + +-----------------+ +On-Chain Wallet ---> | Free Balance | <--- Where deposits land + +-----------------+ + | | + v v + +----------+ +-------------+ + | Supplied | | Collateral | + | (Earn) | | (Borrow) | + +----------+ +-------------+ +``` + +- **Free Balance**: Unallocated funds sitting on the State Chain. This is where all deposits land. Can be egressed (withdrawn) back to an on-chain wallet at any time. +- **Supplied**: Funds allocated to a lending pool via `add_lender_funds`. These earn interest from borrowers. Cannot be used as collateral. +- **Collateral**: Funds backing a loan via `add_collateral`. These secure your borrow position. Cannot earn supply interest. + +These buckets are **fully separate** - supplied funds can't be used as collateral and vice versa. Moving between them requires explicit operations (withdraw from supply to free balance, then add to collateral). + +### What FLIP Is and Why You Need It + +FLIP is Chainflip's native token (ERC-20 on Ethereum: `0x826180541412d574cf1336d22c0c0a287822678a`). Every State Chain extrinsic (lending operation, deposit channel request, etc.) costs a small amount of FLIP as gas. Think of it like ETH for Ethereum transactions, but for the Chainflip State Chain. + +For the PoC, the user explicitly funds their State Chain account with 2 FLIP via the Gateway contract. This is a one-time operation - 2 FLIP is enough for hundreds of lending operations. + +In the future, a broker path could auto-swap a portion of the first deposit into FLIP, removing this UX friction entirely. + +## Deposit to State Chain Flow + +### Overview + +The deposit flow takes assets from the user's on-chain wallet and lands them in their State Chain **free balance**. For first-time users, this flow also handles all one-time account setup (FLIP funding, LP registration, refund address). + +### xstate Machine: `depositMachine` + +``` + +-------+ + | input | + +---+---+ + | + SUBMIT_INPUT | + +------------------+------------------+ + | (needs refund | | + | address?) | (has it) | + v v | + +------+----------+ +--+-----+ | + | refund_address_ |-->| confirm | | + | input | +--+-----+ | + +-----------------+ | | + | START | + v | + +--------+--------+ | + | checking_account | | + +--------+--------+ | + | | + (conditional steps based on account state) | + | | | | | + v v v v | + +------+--+ +---+----+ +--+----+ +--+----+ | + |approving | |funding | |regist-| |setting| | + | flip | |account | |ering | |refund | | + |(ERC-20 | |(2 FLIP | |(LP | |address| | + | approve) | | to SC) | | role) | | | | + +------+---+ +---+----+ +--+----+ +--+----+ | + | | | | | + +----+----+----+----+ | | + | | | | + v v v | + +------+------+ +----+----+ | + |opening_ | | | | + |channel | | | | + +------+------+ | error | | + | | | | + v +----+----+ | + +------+------+ | | + |sending_ | | RETRY | + |deposit | | (routes back | + +------+------+ | to failed | + | | step) | + v | | + +------+------+ | | + |confirming |-------+ | + +------+------+ | + | | + v | + +------+------+ | + | success |--- DONE -------------->+ + +-------------+ +``` + +### Step-by-Step Protocol Flow + +#### Step 1: Approve FLIP (conditional - only if allowance insufficient) + +**What**: ERC-20 `approve()` for the FLIP token on the State Chain Gateway contract. + +**Why**: The Gateway contract needs permission to move FLIP from the user's wallet. + +**Protocol**: Standard ERC-20 approval on Ethereum. The Gateway contract address is `0x6995ab7c4d7f4b03f467cf4c8e920427d9621dbd`. + +**Skipped if**: User's FLIP allowance already covers the funding amount (2 FLIP). + +#### Step 2: Fund State Chain Account (conditional - only if not funded) + +**What**: Call `fundStateChainAccount(nodeID, amount)` on the State Chain Gateway contract. + +**Why**: The State Chain account needs FLIP to pay for extrinsic fees. This is an EVM transaction on Ethereum mainnet. + +**Protocol**: The `nodeID` is derived from the user's ETH address (left-padded to 32 bytes). The amount is 2 FLIP (2e18 base units). Once the Chainflip validators witness this EVM transaction, the FLIP balance appears on the State Chain. + +**Skipped if**: Account already has FLIP balance on the State Chain. + +#### Step 3: Register LP Account (conditional - only if not registered) + +**What**: Submit `liquidityProvider.registerLpAccount()` extrinsic via EIP-712 Non-Native Signed Call. + +**Why**: The account needs the "liquidity_provider" role to interact with lending. This is a one-time registration. + +**Protocol**: SCALE-encode the call (pallet 31, call 2) -> `cf_encode_non_native_call` RPC returns EIP-712 typed data -> user signs with `eth_signTypedData_v4` -> submit via `author_submitExtrinsic`. + +**Skipped if**: Account role is already `liquidity_provider`. + +#### Step 4: Set Refund Address (conditional - only if not set for this chain) + +**What**: Submit `liquidityProvider.registerLiquidityRefundAddress(chain, address)` extrinsic. + +**Why**: Chainflip needs to know where to send funds back if something goes wrong (e.g., deposit channel expires). One-time per chain. + +**Protocol**: Same EIP-712 flow as Step 3. Nonce is incremented from the previous step if both execute in the same session. + +**Skipped if**: Refund address already registered for the deposit asset's chain. + +#### Step 5: Open Deposit Channel + +**What**: Submit `liquidityProvider.requestLiquidityDepositAddress(asset, boostFee)` extrinsic. + +**Why**: Creates a temporary deposit address on the target chain where the user sends their assets. Chainflip validators watch this address and credit the State Chain free balance once witnessed. + +**Protocol**: Same EIP-712 flow. Returns a `LiquidityDepositAddressReady` event with the deposit address. The channel expires after ~24 hours. + +#### Step 6: Send Deposit + +**What**: Standard on-chain transfer to the deposit address from Step 5. + +**Why**: This is the actual asset movement - BTC goes to a BTC address, ETH to an ETH address, etc. + +**Protocol**: This is a regular blockchain transaction on the asset's native chain (e.g., an Ethereum ETH transfer, a Bitcoin UTXO spend). The deposit address is a Chainflip-controlled address specific to this channel. + +#### Step 7: Confirming + +**What**: Poll `cf_free_balances` until the deposited amount appears. + +**Why**: Chainflip validators need to witness the on-chain deposit and credit the State Chain. This typically takes a few block confirmations on the source chain plus Chainflip's witnessing delay. + +**Protocol**: The app polls the `cf_free_balances` RPC every ~6 seconds, comparing against the initial free balance captured at the start of the flow. Once the balance increases by the deposited amount, the deposit is confirmed. + +### First-Time vs Returning User + +For a **first-time user**, all 7 steps execute sequentially. The `checking_account` state evaluates guards in order: +1. `needsApproval` - does FLIP allowance cover 2 FLIP? +2. `needsFunding` - is the State Chain account funded? +3. `needsRegistration` - is the account registered as LP? +4. `needsRefundAddress` - is a refund address set for this chain? + +For a **returning user** who already has an account set up, the machine skips directly from `checking_account` to `opening_channel`, making it a 3-step flow: open channel, send deposit, confirm. + +### Nonce Management + +State Chain operations use an incrementing nonce. The deposit machine tracks `lastUsedNonce` in context, passing `nonce + 1` for each subsequent EIP-712 call within the same session. This allows Steps 3, 4, and 5 to execute without waiting for previous nonces to finalize on-chain (future nonces enter the txpool with a dependency). + +### Error Handling + +Every step can fail independently. The machine tracks `errorStep` so that RETRY routes back to exactly the step that failed, preserving all progress from earlier steps. The user can also BACK out to the input screen and start over. + +## Live Account Data (March 2026) + +Example state of a PoC test account: + +| Field | Value | +|-------|-------| +| Role | `liquidity_provider` | +| FLIP Balance | ~4.0 FLIP | +| ETH Free Balance | ~0.011 ETH | +| USDC Free Balance | ~74.88 USDC | +| USDC Collateral | ~156.43 USDC | +| Active Loan | USDC, ~101.07 principal | +| Current LTV | ~64.65% | +| Refund Addresses | Ethereum, Solana | + +## Runtime & Environment + +| Field | Value | +|-------|-------| +| Runtime | chainflip-node specVersion 20012, txVersion 13 | +| RPC | `rpc.mainnet.chainflip.io` (public, no auth) | +| Gateway Contract | `0x6995ab7c4d7f4b03f467cf4c8e920427d9621dbd` | +| FLIP Token | `0x826180541412d574cf1336d22c0c0a287822678a` | +| FLIP Funding Amount | 2 FLIP | +| SS58 Prefix | 2112 | +| EIP-712 Domain | `{ name: "Chainflip-Mainnet", version: "20012" }` | + +## Deposit Channel Minimums (live from `cf_environment`) + +| Chain | Asset | Minimum Deposit | +|-------|-------|----------------| +| Ethereum | ETH | 0.01 ETH | +| Ethereum | FLIP | 4 FLIP | +| Ethereum | USDC | 20 USDC | +| Ethereum | USDT | 20 USDT | +| Bitcoin | BTC | 0.0004 BTC | +| Polkadot | DOT | 4 DOT | +| Arbitrum | ETH | ~0.004 ETH | +| Arbitrum | USDC | 10 USDC | +| Solana | SOL | ~0.068 SOL | +| Solana | USDC | 10 USDC | +| Assethub | DOT | 4 DOT | +| Assethub | USDT | 20 USDT | +| Assethub | USDC | 20 USDC | + +## Notes + +- The deposit into state channel flow above encapsulates everything in a single stepper, but technically you could separate these into independent flows (e.g. register refund address separately, fund account as its own flow, or only show the last 3 steps for returning users). +- The 2 FLIP funding step is a current hard requirement for the direct path. If/when the Chainflip BaaS broker exposes `request_account_creation_deposit_address`, a broker path can auto-swap a portion of the first deposit to FLIP, removing this friction. +- All signing happens via EIP-712 `eth_signTypedData_v4` - the same mechanism used for CowSwap Permit2 orders. Users sign a human-readable message, not a raw hex blob. diff --git a/docs/features/chainflip-lending-supply-withdraw-overview.md b/docs/features/chainflip-lending-supply-withdraw-overview.md new file mode 100644 index 00000000000..49594256ecf --- /dev/null +++ b/docs/features/chainflip-lending-supply-withdraw-overview.md @@ -0,0 +1,227 @@ +# Chainflip Lending - Supply & Withdraw (Product Overview) + +This document covers the supply side of Chainflip lending: supplying assets to lending pools to earn interest, and withdrawing them back. + +## Prerequisites + +Before supply operations, the user must have: +1. A funded and registered State Chain account (see deposit overview) +2. Assets in their **free balance** (deposited via the deposit flow) + +## Core Concepts + +### What Supplying Means + +When you supply assets to a Chainflip lending pool, your funds move from your **free balance** to a **supply position** in that pool. Borrowers pay interest on what they borrow, and that interest accrues to suppliers proportionally. + +``` + +---[add_lender_funds]---+ + | | + +------+-------+ +------+------+ + | Free Balance | | Supply | + | (idle) | | Position | + +--------------+ | (earning) | + ^ +------+------+ + | | + +--[remove_lender_funds]--+ +``` + +Supply positions are **per asset, per pool**. You can supply to multiple pools simultaneously (e.g., supply USDC to the USDC pool and ETH to the ETH pool). + +### Interest Mechanics + +Interest follows a kinked utilization curve: + +``` +Interest Rate + | + 25% | / + | / + | / + 4% | --------/ (kink at 95% util) + | ------/ + | ------/ + | ------/ + 0% |--------/ + +--------+--------+--------+--------+---- + 0% 25% 50% 75% 100% Utilization +``` + +- 0% utilization: 0% interest +- 95% utilization (kink): 4% interest +- 100% utilization: 25% interest + +Interest is collected every 10 blocks (~60 seconds) and paid from borrower debt to the lending pool. As a supplier, your share of interest grows proportionally to your supply position. + +## Supply Flow + +### What It Does + +Moves assets from the user's State Chain free balance into a lending pool supply position. + +### xstate Machine: `supplyMachine` + +``` ++-------+ SUBMIT +---------+ CONFIRM +---------+ +| input | ----------------> | confirm | ------------> | signing | ++-------+ (amount crypto +---------+ +----+----+ + ^ precision + | + | base unit) SIGN_SUCCESS + | | + | v + | DONE +------+------+ + +<------------ success <------------------------- | confirming | + +-------------+ +``` + +### Context + +- `supplyAmountCryptoPrecision` / `supplyAmountCryptoBaseUnit` - how much to supply +- `freeBalanceCryptoBaseUnit` - max available, synced in real-time via `SYNC_FREE_BALANCE` +- `initialLendingPositionCryptoBaseUnit` - snapshot of lending position at confirm time, used to detect when supply is credited +- `lastUsedNonce` - for sequential operations + +### Protocol Call + +SCALE-encoded `lendingPools.addLenderFunds(asset, amount)` -> EIP-712 sign -> submit. + +### Confirmation + +The app polls `cf_lending_pool_supply_balances` at ~6 second intervals. Once the user's supply position increases by the supplied amount (compared to `initialLendingPositionCryptoBaseUnit`), the operation is confirmed. + +### Minimums + +- Minimum supply: $100 USD + +## Withdraw Flow + +### What It Does + +Moves assets from a lending pool supply position back to the user's State Chain free balance. + +### xstate Machine: `withdrawMachine` + +``` ++-------+ SUBMIT +---------+ CONFIRM +---------+ +| input | ----------------> | confirm | ------------> | signing | ++-------+ (amount, +---------+ +----+----+ + ^ isFullWithdraw) | + | SIGN_SUCCESS + | | + | v + | DONE +------+------+ + +<------------ success <------------------------- | confirming | + +-------------+ +``` + +### Context + +- `withdrawAmountCryptoPrecision` / `withdrawAmountCryptoBaseUnit` +- `supplyPositionCryptoBaseUnit` - current supply position, synced via `SYNC_SUPPLY_POSITION` +- `isFullWithdrawal` - flag for withdrawing the entire position (passes `null` amount to the protocol, which withdraws everything) + +### Protocol Call + +SCALE-encoded `lendingPools.removeLenderFunds(asset, amount)` -> EIP-712 sign -> submit. + +When `isFullWithdrawal` is true, `amount` is encoded as `null` (SCALE `Option::None`), telling the protocol to remove the entire supply position. This avoids dust from rounding. + +### Withdrawal to On-Chain Wallet + +After withdrawing from the lending pool, funds land in the **free balance**. To get them back to an on-chain wallet, the user needs to use the **Egress** flow (see below). This can be presented as an optional checkbox or CTA after the withdraw completes. + +## Egress Flow (Withdraw from State Chain) + +### What It Does + +Takes assets from the user's State Chain free balance and sends them to an on-chain wallet address on any supported chain. + +### xstate Machine: `egressMachine` + +``` ++-------+ SUBMIT +---------+ CONFIRM +---------+ +| input | ----------------> | confirm | ------------> | signing | ++-------+ (amount, +---------+ (captures +----+----+ + ^ destination initial free | + | address) balance) SIGN_SUCCESS + | | + | v + | DONE +------+------+ + +<------------ success <-------------------- | confirming | + +------+------+ + | + EGRESS_CONFIRMED + (+ optional + egressTxRef) +``` + +### Context + +- `egressAmountCryptoPrecision` / `egressAmountCryptoBaseUnit` +- `destinationAddress` - target address on the external chain +- `freeBalanceCryptoBaseUnit` - max available, synced via `SYNC_FREE_BALANCE` +- `initialFreeBalanceCryptoBaseUnit` - captured at confirm time for change detection +- `egressTxRef` - optional reference to the on-chain egress transaction + +### Protocol Call + +SCALE-encoded `liquidityProvider.withdrawAsset(amount, asset, destinationAddress)` -> EIP-712 sign -> submit. + +Note: `withdrawAsset` takes an explicit `destinationAddress` - it does NOT use the stored refund address. This means the user can egress to any valid address on the target chain. + +### Confirmation + +The app polls `cf_free_balances` and detects when the balance decreases from the initial snapshot. The egress is confirmed when Chainflip broadcasts the on-chain transaction. + +## Withdraw Supply + Egress Combined (not yet in UI) + +A common user flow is "I want my money back in my wallet". This involves two steps today (two separate flows, two signatures): + +1. **Withdraw from pool**: `removeLenderFunds` (supply position -> free balance) +2. **Egress to wallet**: `withdrawAsset` (free balance -> on-chain) + +The low-level `encodeBatch` primitive and translation key (`alsoWithdrawToWallet`) exist, but the combined batch flow is **not yet wired into the UI**. The withdraw and egress state machines operate independently today. + +**Planned**: batch both calls via `Environment.batch` for a single EIP-712 signature, with an "Also withdraw to wallet" checkbox in the withdraw flow. + +## Live Pool Data (March 2026) + +| Pool | Total Supplied | Available | Borrowed | Utilization | APY | +|------|---------------|-----------|----------|-------------|-----| +| USDC | ~$120,393 | ~$39,228 | ~$81,165 | 67.44% | 2.84% | +| USDT | ~$408,784 | ~$279,528 | ~$129,256 | 31.67% | 1.33% | +| BTC | ~0.179 BTC | ~0.179 BTC | 0 | 0% | 0% | +| ETH | ~0.030 ETH | ~0.030 ETH | 0 | 0% | 0% | +| SOL | ~0.816 SOL | ~0.816 SOL | 0 | 0% | 0% | + +### Supported Supply Assets + +All lending pools accept the following assets for supply (from `cf_safe_mode_statuses`): + +- Ethereum: ETH, FLIP, USDC, USDT +- Bitcoin: BTC +- Polkadot: DOT +- Arbitrum: ETH, USDC +- Solana: SOL, USDC +- Assethub: DOT, USDT, USDC + +## Common Machine Patterns + +All supply/withdraw/egress machines share consistent patterns: + +| Pattern | Description | +|---------|-------------| +| `SYNC_*` events | Root-level handlers for real-time data updates from any state | +| `CONFIRM_STEP` | Manual confirmation gate for native wallet users | +| `executing` tag | Applied to signing + confirming states for UI spinners | +| `RETRY` from error | Routes back to exact failed step (signing or confirming) | +| `BACK` | Returns to input, clears errors | +| `DONE` | Resets machine for a new operation | +| `lastUsedNonce` | Tracks nonce for sequential EIP-712 calls | + +## Notes + +- Supplying does NOT require a refund address (only borrowing and deposit channels do). +- Withdrawal from a supply pool may fail if the pool doesn't have enough available liquidity (i.e., too much is borrowed). The user would need to wait for borrowers to repay or for the utilization rate to drop. +- Interest earned is automatically compounded into the supply position - there's no separate "claim interest" step. +- All rate fields (`utilisation_rate`, `current_interest_rate`) are Permill (1,000,000 = 100%). The app converts these using `permillToDecimal()` for display. diff --git a/e2e/README.md b/e2e/README.md index 0aa2d1d5630..cc107ea6f4f 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -55,7 +55,7 @@ agent-browser stores wallet state per profile per origin. You need to import the ```bash # Open a headed browser session -agent-browser --session qabot --profile ~/.agent-browser/profiles/qabot --headed open http://localhost:3000 +agent-browser --session qabot --profile ~/.agent-browser/profiles/qabot --headed open ${PORTLESS_URL:-http://localhost:3000} # In the browser: # 1. Create or import a native wallet @@ -80,6 +80,8 @@ For local testing, start the dev server: pnpm run dev ``` +When using [Portless](https://github.com/vercel-labs/portless) (`pnpm run dev:web`), `PORTLESS_URL` is set automatically (e.g. `http://develop.web.localhost:1355`). The wallet profile is stored per-origin, so you'll need to import the wallet separately for each Portless origin. + Or point `BASE_URL` at a staging environment: ```bash diff --git a/headers/csps/chains/solana.ts b/headers/csps/chains/solana.ts index e395d394095..9ba7ebb9c1c 100644 --- a/headers/csps/chains/solana.ts +++ b/headers/csps/chains/solana.ts @@ -10,5 +10,9 @@ export const csp: Csp = { env.VITE_UNCHAINED_SOLANA_HTTP_URL, env.VITE_UNCHAINED_SOLANA_WS_URL, env.VITE_SOLANA_NODE_URL, + // Jito Block Engine for enhanced tx submission and atomic bundles + env.VITE_JITO_BLOCK_ENGINE_URL, + // Jito tip floor REST API (separate domain from block engine) + 'https://bundles.jito.wtf', ], } diff --git a/package.json b/package.json index 94be37bdd56..d72fbd4a014 100644 --- a/package.json +++ b/package.json @@ -19,14 +19,16 @@ "analyze": "ANALYZE=true pnpm run build:web", "bump": "pnpm update -i --latest --filter \"@shapeshiftoss/*\"", "build:web": "pnpm run build:packages && NODE_OPTIONS=--max-old-space-size=16000 vite build --mode=${MODE:-production} && ./scripts/sha256sums.sh && ./scripts/verifyViteHashes.sh && tsx ./scripts/writeBuildMetadata.ts", - "build:packages": "pnpm run clean:packages && pnpm --filter @shapeshiftoss/unchained-client generate && pnpm exec tsc --build tsconfig.packages.json && pnpm run postbuild:packages", + "build:packages": "pnpm run clean:dist && pnpm --filter @shapeshiftoss/unchained-client generate && pnpm exec tsc --build tsconfig.packages.json && pnpm run postbuild:packages", "postbuild:packages": "pnpm -r --filter=\"!@shapeshiftoss/web\" postbuild", "clean": "pnpm run clean:packages && pnpm run clean:web && rm -rf node_modules coverage", "clean:web": "rm -rf build", + "clean:dist": "find packages -maxdepth 2 -name dist -type d -exec rm -rf {} +", "clean:packages": "pnpm -r --filter=\"!@shapeshiftoss/web\" clean", "codemod:clear-assets-migration": "jscodeshift -t src/state/migrations/transformClearAssets.ts --parser=ts --extensions=ts src/state/migrations/index.ts", "deadcode": "pnpm exec ts-prune", - "dev:web": "NODE_OPTIONS=--max-old-space-size=8192 vite", + "dev:web": "NODE_OPTIONS=--max-old-space-size=8192 portless run vite", + "dev:web:localhost": "PORTLESS=0 NODE_OPTIONS=--max-old-space-size=8192 vite --host 0.0.0.0", "dev:web:linked": "GENERATE_SOURCEMAP=false BROWSER=none pnpm run dev:web", "dev:packages": "pnpm exec tsc --build --watch --preserveWatchOutput tsconfig.packages.json", "hdwallet:build": "pnpm exec tsc --build tsconfig.hdwallet.json && pnpm -r --filter=\"@shapeshiftoss/hdwallet-*\" postbuild", @@ -148,6 +150,8 @@ "@visx/responsive": "^3.10.2", "@visx/scale": "^3.5.0", "@visx/xychart": "^3.12.0", + "@wallet-standard/app": "^1.1.0", + "@wallet-standard/base": "^1.1.0", "@walletconnect/core": "^2.20.2", "@walletconnect/utils": "^2.20.2", "@xstate/react": "5.0.5", diff --git a/packages/affiliate-dashboard/Dockerfile b/packages/affiliate-dashboard/Dockerfile new file mode 100644 index 00000000000..16f4a1cb627 --- /dev/null +++ b/packages/affiliate-dashboard/Dockerfile @@ -0,0 +1,34 @@ +FROM node:22-alpine AS builder +WORKDIR /app + +COPY packages/affiliate-dashboard/package.json ./ +RUN npm install + +COPY packages/affiliate-dashboard/index.html packages/affiliate-dashboard/tsconfig.json packages/affiliate-dashboard/tsconfig.node.json packages/affiliate-dashboard/vite.config.ts ./ +COPY packages/affiliate-dashboard/src/ ./src/ + +RUN npx vite build + +FROM nginx:stable-alpine AS runner +COPY --from=builder /app/dist /usr/share/nginx/html +RUN printf 'server {\n\ + listen 8080;\n\ + server_name _;\n\ + root /usr/share/nginx/html;\n\ + index index.html;\n\ + location /v1/ {\n\ + proxy_pass ${API_URL}/v1/;\n\ + proxy_http_version 1.1;\n\ + proxy_set_header Host api.shapeshift.com;\n\ + proxy_set_header X-Real-IP $remote_addr;\n\ + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\ + proxy_set_header X-Forwarded-Proto $scheme;\n\ + }\n\ + location / {\n\ + try_files $uri $uri/ /index.html;\n\ + }\n\ +}\n' > /tmp/default.conf.template +# Default to production API if API_URL not set +ENV API_URL=https://api.shapeshift.com +EXPOSE 8080 +CMD ["/bin/sh", "-c", "envsubst '${API_URL}' < /tmp/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"] diff --git a/packages/affiliate-dashboard/index.html b/packages/affiliate-dashboard/index.html new file mode 100644 index 00000000000..ac8925b4785 --- /dev/null +++ b/packages/affiliate-dashboard/index.html @@ -0,0 +1,14 @@ + + + + + + + Affiliate Dashboard + + + +
+ + + diff --git a/packages/affiliate-dashboard/package.json b/packages/affiliate-dashboard/package.json new file mode 100644 index 00000000000..08af28eeaab --- /dev/null +++ b/packages/affiliate-dashboard/package.json @@ -0,0 +1,21 @@ +{ + "name": "@shapeshiftoss/affiliate-dashboard", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.0.0", + "typescript": "^5.0.0", + "vite": "^5.0.0" + } +} diff --git a/packages/affiliate-dashboard/railway.json b/packages/affiliate-dashboard/railway.json new file mode 100644 index 00000000000..390a02ae922 --- /dev/null +++ b/packages/affiliate-dashboard/railway.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://railway.com/railway.schema.json", + "build": { + "builder": "DOCKERFILE", + "dockerfilePath": "packages/affiliate-dashboard/Dockerfile", + "watchPatterns": [ + "packages/affiliate-dashboard/**" + ] + }, + "deploy": { + "restartPolicyType": "ALWAYS" + } +} diff --git a/packages/affiliate-dashboard/src/App.tsx b/packages/affiliate-dashboard/src/App.tsx new file mode 100644 index 00000000000..beb1408a479 --- /dev/null +++ b/packages/affiliate-dashboard/src/App.tsx @@ -0,0 +1,430 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' + +import { useAffiliateStats } from './hooks/useAffiliateStats' + +const formatUsd = (value: number): string => + new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(value) + +const formatNumber = (value: number): string => new Intl.NumberFormat('en-US').format(value) + +interface Period { + label: string + startDate?: string + endDate?: string +} + +const PERIOD_DAY = 5 +const PREVIOUS_PERIODS_COUNT = 3 + +const formatPeriodLabel = (start: Date, end: Date): string => { + const monthShort = new Intl.DateTimeFormat('en-US', { month: 'short' }) + const startLabel = `${monthShort.format(start)} ${start.getUTCDate()}` + const endLabel = `${monthShort.format(end)} ${end.getUTCDate()}` + const year = end.getUTCFullYear() + return `${startLabel} - ${endLabel}, ${year}` +} + +const generatePeriods = (): Period[] => { + const now = new Date() + const year = now.getUTCFullYear() + const month = now.getUTCMonth() + + let currentStart: Date + let currentEnd: Date + + if (now.getUTCDate() < PERIOD_DAY) { + currentStart = new Date(Date.UTC(year, month - 1, PERIOD_DAY)) + currentEnd = new Date(Date.UTC(year, month, PERIOD_DAY)) + } else { + currentStart = new Date(Date.UTC(year, month, PERIOD_DAY)) + currentEnd = new Date(Date.UTC(year, month + 1, PERIOD_DAY)) + } + + const result: Period[] = [] + + for (let i = 0; i <= PREVIOUS_PERIODS_COUNT; i++) { + const start = new Date( + Date.UTC(currentStart.getUTCFullYear(), currentStart.getUTCMonth() - i, PERIOD_DAY), + ) + const end = new Date( + Date.UTC(currentEnd.getUTCFullYear(), currentEnd.getUTCMonth() - i, PERIOD_DAY), + ) + result.push({ + label: formatPeriodLabel(start, end), + startDate: start.toISOString(), + endDate: end.toISOString(), + }) + } + + result.push({ label: 'All Time' }) + return result +} + +const periods: Period[] = generatePeriods() + +const ShapeShiftLogo = (): React.JSX.Element => ( + + + + + + + + + + + + + + + + + + + + +) + +const EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/ + +const isValidEvmAddress = (addr: string): boolean => EVM_ADDRESS_REGEX.test(addr) + +export const App = (): React.JSX.Element => { + const [address, setAddress] = useState('') + const [selectedPeriod, setSelectedPeriod] = useState(0) + const [validationError, setValidationError] = useState(null) + const { stats, isLoading, error, fetchStats } = useAffiliateStats() + + const currentPeriod = periods[selectedPeriod] + + const doFetch = useCallback((): void => { + const trimmed = address.trim() + if (!trimmed) return + + if (!isValidEvmAddress(trimmed)) { + setValidationError('Please enter a valid Ethereum address (0x followed by 40 hex characters)') + return + } + + setValidationError(null) + void fetchStats(trimmed, { + startDate: currentPeriod.startDate, + endDate: currentPeriod.endDate, + }) + }, [fetchStats, address, currentPeriod]) + + const handleAddressChange = useCallback((e: React.ChangeEvent): void => { + setAddress(e.target.value) + setValidationError(null) + }, []) + + const handleViewStats = useCallback((): void => { + doFetch() + }, [doFetch]) + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent): void => { + if (e.key === 'Enter') { + doFetch() + } + }, + [doFetch], + ) + + useEffect(() => { + if (address.trim()) doFetch() + }, [selectedPeriod]) // eslint-disable-line react-hooks/exhaustive-deps + + const isButtonDisabled = useMemo(() => isLoading || !address.trim(), [isLoading, address]) + + const statCards = useMemo(() => { + if (!stats) return null + return [ + { + label: 'Total Swaps', + value: formatNumber(stats.totalSwaps), + }, + { + label: 'Total Volume USD', + value: formatUsd(stats.totalVolumeUsd), + }, + { + label: 'Total Fees USD', + value: formatUsd(stats.totalFeesUsd), + }, + ] + }, [stats]) + + return ( +
+
+
+
+
+ +
+

Affiliate Dashboard

+

Track your affiliate performance and earnings

+
+ +
+
+ +
+ +
+ +
+ {periods.map((period, i) => ( + + ))} +
+ + {validationError ?
{validationError}
: null} + {error && !validationError ?
{error}
: null} + + {statCards ? ( +
+ {statCards.map(card => ( +
+
{card.value}
+
{card.label}
+
+ ))} +
+ ) : null} + + {!stats && !error && !isLoading ? ( +
+

+ Enter an affiliate address above to view performance stats. +

+
+ ) : null} +
+
+ ) +} + +const styles: Record = { + container: { + minHeight: '100vh', + background: '#0a0b0d', + color: '#e2e4e9', + fontFamily: '"DM Sans", "Söhne", -apple-system, BlinkMacSystemFont, sans-serif', + position: 'relative', + overflow: 'hidden', + }, + backdrop: { + position: 'fixed', + inset: 0, + background: + 'radial-gradient(ellipse 80% 60% at 50% -20%, rgba(56, 111, 249, 0.12) 0%, transparent 70%), radial-gradient(ellipse 60% 40% at 80% 100%, rgba(56, 111, 249, 0.06) 0%, transparent 60%)', + pointerEvents: 'none', + }, + content: { + position: 'relative', + maxWidth: 720, + margin: '0 auto', + padding: '80px 24px 60px', + }, + header: { + textAlign: 'center', + marginBottom: 48, + }, + logo: { + display: 'flex', + justifyContent: 'center', + marginBottom: 16, + color: '#f0f1f4', + }, + title: { + fontSize: 28, + fontWeight: 700, + margin: '0 0 8px', + color: '#f0f1f4', + letterSpacing: '-0.02em', + }, + subtitle: { + fontSize: 15, + color: '#7a7e8a', + margin: 0, + fontWeight: 400, + }, + inputGroup: { + display: 'flex', + gap: 12, + marginBottom: 32, + }, + inputWrapper: { + flex: 1, + }, + input: { + width: '100%', + padding: '14px 18px', + fontSize: 15, + fontFamily: '"DM Mono", "SF Mono", "Fira Code", monospace', + background: '#12141a', + border: '1px solid #1e2028', + borderRadius: 12, + color: '#e2e4e9', + outline: 'none', + transition: 'border-color 0.2s ease', + boxSizing: 'border-box', + }, + button: { + padding: '14px 28px', + fontSize: 15, + fontWeight: 600, + fontFamily: '"DM Sans", "Söhne", -apple-system, BlinkMacSystemFont, sans-serif', + background: '#386ff9', + color: '#fff', + border: 'none', + borderRadius: 12, + cursor: 'pointer', + transition: 'all 0.2s ease', + whiteSpace: 'nowrap', + flexShrink: 0, + }, + buttonDisabled: { + opacity: 0.4, + cursor: 'not-allowed', + }, + error: { + background: 'rgba(239, 68, 68, 0.08)', + border: '1px solid rgba(239, 68, 68, 0.2)', + borderRadius: 12, + padding: '14px 18px', + color: '#f87171', + fontSize: 14, + marginBottom: 24, + }, + periodRow: { + display: 'flex', + flexWrap: 'wrap', + gap: 8, + marginBottom: 24, + }, + periodButton: { + padding: '10px 20px', + fontSize: 14, + fontWeight: 500, + fontFamily: '"DM Sans", "Söhne", -apple-system, BlinkMacSystemFont, sans-serif', + background: '#12141a', + border: '1px solid #1e2028', + borderRadius: 10, + color: '#7a7e8a', + cursor: 'pointer', + transition: 'all 0.2s ease', + }, + periodButtonActive: { + background: 'rgba(56, 111, 249, 0.12)', + border: '1px solid #386ff9', + color: '#386ff9', + }, + statsGrid: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', + gap: 16, + }, + statCard: { + background: '#12141a', + border: '1px solid #1e2028', + borderRadius: 16, + padding: '28px 24px', + textAlign: 'center', + transition: 'border-color 0.2s ease, transform 0.2s ease', + }, + cardValue: { + fontSize: 26, + fontWeight: 700, + color: '#f0f1f4', + marginBottom: 6, + letterSpacing: '-0.02em', + fontFamily: '"DM Mono", "SF Mono", "Fira Code", monospace', + }, + cardLabel: { + fontSize: 13, + color: '#7a7e8a', + fontWeight: 500, + textTransform: 'uppercase' as const, + letterSpacing: '0.06em', + }, + emptyState: { + textAlign: 'center', + padding: '60px 20px', + }, + emptyIcon: { + fontSize: 36, + color: '#2a2d38', + marginBottom: 16, + }, + emptyText: { + fontSize: 15, + color: '#4a4e5a', + margin: 0, + lineHeight: 1.6, + }, +} diff --git a/packages/affiliate-dashboard/src/hooks/useAffiliateStats.ts b/packages/affiliate-dashboard/src/hooks/useAffiliateStats.ts new file mode 100644 index 00000000000..df5d261a134 --- /dev/null +++ b/packages/affiliate-dashboard/src/hooks/useAffiliateStats.ts @@ -0,0 +1,95 @@ +import { useCallback, useRef, useState } from 'react' + +const API_BASE_URL = '/v1/affiliate/stats' + +export interface AffiliateStats { + totalSwaps: number + totalVolumeUsd: number + totalFeesUsd: number +} + +// Raw API response shape (strings from backend) +interface ApiResponse { + totalSwaps: number + totalVolumeUsd: string + totalFeesEarnedUsd: string +} + +interface AffiliateStatsState { + stats: AffiliateStats | null + isLoading: boolean + error: string | null +} + +interface FetchOptions { + startDate?: string + endDate?: string +} + +interface UseAffiliateStatsReturn extends AffiliateStatsState { + fetchStats: (address: string, options?: FetchOptions) => Promise +} + +export const useAffiliateStats = (): UseAffiliateStatsReturn => { + const [stats, setStats] = useState(null) + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState(null) + const requestIdRef = useRef(0) + + const fetchStats = useCallback(async (address: string, options?: FetchOptions): Promise => { + const requestId = ++requestIdRef.current + if (!address.trim()) { + setError('Please enter a valid affiliate address.') + return + } + + setIsLoading(true) + setError(null) + setStats(null) + + try { + const params = new URLSearchParams({ address }) + if (options?.startDate) params.append('startDate', options.startDate) + if (options?.endDate) params.append('endDate', options.endDate) + + const response = await fetch(`${API_BASE_URL}?${params.toString()}`) + + if (!response.ok) { + let errorMessage = `Request failed (${String(response.status)})` + try { + const errorBody = (await response.json()) as { + error?: string + details?: { message: string }[] + } + if (errorBody.error) { + errorMessage = errorBody.error + } + if (errorBody.details?.[0]?.message) { + errorMessage = errorBody.details[0].message + } + } catch { + // Response wasn't JSON, use generic message + } + throw new Error(errorMessage) + } + + const data = (await response.json()) as ApiResponse + if (requestId !== requestIdRef.current) return + setStats({ + totalSwaps: data.totalSwaps, + totalVolumeUsd: parseFloat(data.totalVolumeUsd) || 0, + totalFeesUsd: parseFloat(data.totalFeesEarnedUsd) || 0, + }) + } catch (err) { + if (requestId !== requestIdRef.current) return + const message = err instanceof Error ? err.message : 'Failed to fetch affiliate stats.' + setError(message) + } finally { + if (requestId === requestIdRef.current) { + setIsLoading(false) + } + } + }, []) + + return { stats, isLoading, error, fetchStats } +} diff --git a/packages/affiliate-dashboard/src/main.tsx b/packages/affiliate-dashboard/src/main.tsx new file mode 100644 index 00000000000..494346ea3dc --- /dev/null +++ b/packages/affiliate-dashboard/src/main.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' + +import { App } from './App' + +// eslint-disable-next-line @typescript-eslint/no-non-null-assertion +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/packages/affiliate-dashboard/tsconfig.json b/packages/affiliate-dashboard/tsconfig.json new file mode 100644 index 00000000000..3934b8f6d67 --- /dev/null +++ b/packages/affiliate-dashboard/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/packages/affiliate-dashboard/tsconfig.node.json b/packages/affiliate-dashboard/tsconfig.node.json new file mode 100644 index 00000000000..42872c59f5b --- /dev/null +++ b/packages/affiliate-dashboard/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/affiliate-dashboard/vite.config.ts b/packages/affiliate-dashboard/vite.config.ts new file mode 100644 index 00000000000..d7bc14b8050 --- /dev/null +++ b/packages/affiliate-dashboard/vite.config.ts @@ -0,0 +1,16 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +// eslint-disable-next-line import/no-default-export +export default defineConfig({ + plugins: [react()], + server: { + port: 5175, + proxy: { + '/v1': { + target: 'http://localhost:3005', + changeOrigin: true, + }, + }, + }, +}) diff --git a/packages/caip/package.json b/packages/caip/package.json index 74c350f1728..3b76dbd75ca 100644 --- a/packages/caip/package.json +++ b/packages/caip/package.json @@ -22,8 +22,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "generate": "pnpm run generate:coingecko && pnpm run generate:coincap", "generate:coingecko": "pnpm run build && pnpm exec tsx src/adapters/coingecko/generate.ts", diff --git a/packages/caip/src/constants.ts b/packages/caip/src/constants.ts index 4bc53c43fc8..2e2a1d79e16 100644 --- a/packages/caip/src/constants.ts +++ b/packages/caip/src/constants.ts @@ -69,8 +69,13 @@ export const usdtAssetId: AssetId = 'eip155:1/erc20:0xdac17f958d2ee523a220620699 export const usdcAssetId: AssetId = 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' export const usdcOnArbitrumOneAssetId: AssetId = 'eip155:42161/erc20:0xaf88d065e77c8cc2239327c5edb3a432268e5831' +export const usdtOnArbitrumOneAssetId: AssetId = + 'eip155:42161/erc20:0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9' export const usdcOnSolanaAssetId: AssetId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' +export const usdtOnSolanaAssetId: AssetId = + 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' +export const wbtcAssetId: AssetId = 'eip155:1/erc20:0x2260fac5e5542a773aa44fbcfedf7c193bc2c599' export const flipAssetId: AssetId = 'eip155:1/erc20:0x826180541412d574cf1336d22c0c0a287822678a' export const foxWifHatAssetId: AssetId = diff --git a/packages/chain-adapters/package.json b/packages/chain-adapters/package.json index 353c9cab8aa..951a74b67d9 100644 --- a/packages/chain-adapters/package.json +++ b/packages/chain-adapters/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/chain-adapters", - "version": "11.3.7", + "version": "11.3.9", "repository": "https://github.com/shapeshift/web", "license": "MIT", "type": "module", @@ -21,8 +21,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/chain-adapters/src/solana/SolanaChainAdapter.ts b/packages/chain-adapters/src/solana/SolanaChainAdapter.ts index f4204bd4b07..26c11bd5d30 100644 --- a/packages/chain-adapters/src/solana/SolanaChainAdapter.ts +++ b/packages/chain-adapters/src/solana/SolanaChainAdapter.ts @@ -442,11 +442,17 @@ export class ChainAdapter implements IChainAdapter if (!wallet) throw new Error('wallet is required') this.assertSupportsChain(wallet) + // Try sign+send in one step (e.g. Phantom provider). If the wallet doesn't + // implement solanaSendTx or it returns null, fall back to sign then broadcast + // through our own RPC which is more reliable than relying on the wallet's RPC. const tx = await wallet.solanaSendTx?.(txToSign) + if (tx) return tx.signature - if (!tx) throw new Error('error signing & broadcasting tx') + // Fallback: sign then broadcast via our own infra + const signedTx = await wallet.solanaSignTx?.(txToSign) + if (!signedTx?.serialized) throw new Error('error signing tx') - return tx.signature + return this.broadcastTransaction({ senderAddress, receiverAddress, hex: signedTx.serialized }) } catch (err) { return ErrorHandler(err, { translation: 'chainAdapters.errors.signAndBroadcastTransaction', diff --git a/packages/chain-adapters/src/solana/index.ts b/packages/chain-adapters/src/solana/index.ts index 48f2b439cf7..bc1f6ef7e24 100644 --- a/packages/chain-adapters/src/solana/index.ts +++ b/packages/chain-adapters/src/solana/index.ts @@ -2,3 +2,4 @@ export { ChainAdapter } from './SolanaChainAdapter' export * from './types' export * from './constants' +export * from './jito' diff --git a/packages/chain-adapters/src/solana/jito/buildJitoTipInstruction.ts b/packages/chain-adapters/src/solana/jito/buildJitoTipInstruction.ts new file mode 100644 index 00000000000..df5657b7caf --- /dev/null +++ b/packages/chain-adapters/src/solana/jito/buildJitoTipInstruction.ts @@ -0,0 +1,41 @@ +import type { TransactionInstruction } from '@solana/web3.js' +import { PublicKey, SystemProgram } from '@solana/web3.js' + +import type { JitoService } from './jitoService' + +// Default tip: 10,000 lamports (~$0.002 at current SOL prices) +// This is well above the 1,000 lamport minimum and competitive for most use cases. +const DEFAULT_TIP_LAMPORTS = 10_000 + +/** + * Build a Jito tip instruction (SystemProgram.transfer to a random tip account). + * Tip accounts are dynamic - fetched from getTipAccounts, one picked at random. + * + * This instruction should be included in the LAST transaction of a Jito bundle, + * so the tip is only paid if all prior transactions succeed. + */ +export const buildJitoTipInstruction = async ({ + jitoService, + fromPubkey, + tipLamports = DEFAULT_TIP_LAMPORTS, +}: { + jitoService: JitoService + fromPubkey: PublicKey + tipLamports?: number +}): Promise => { + const tipAccounts = await jitoService.getTipAccounts() + + if (!tipAccounts.length) { + throw new Error('Jito returned no tip accounts') + } + + // Pick a random tip account to reduce contention across users + const randomIndex = Math.floor(Math.random() * tipAccounts.length) + const tipAccount = new PublicKey(tipAccounts[randomIndex]) + + return SystemProgram.transfer({ + fromPubkey, + toPubkey: tipAccount, + lamports: tipLamports, + }) +} diff --git a/packages/chain-adapters/src/solana/jito/index.ts b/packages/chain-adapters/src/solana/jito/index.ts new file mode 100644 index 00000000000..0a52851eb61 --- /dev/null +++ b/packages/chain-adapters/src/solana/jito/index.ts @@ -0,0 +1,13 @@ +export { buildJitoTipInstruction } from './buildJitoTipInstruction' +export { createJitoService } from './jitoService' +export type { JitoService } from './jitoService' +export type { + JitoBundleStatus, + JitoGetBundleStatusesResult, + JitoGetInflightBundleStatusesResult, + JitoGetTipAccountsResult, + JitoInflightBundleStatus, + JitoSendBundleResult, + JitoSendTransactionResult, + JitoTipFloorEntry, +} from './types' diff --git a/packages/chain-adapters/src/solana/jito/jitoService.ts b/packages/chain-adapters/src/solana/jito/jitoService.ts new file mode 100644 index 00000000000..4d83bf9d29b --- /dev/null +++ b/packages/chain-adapters/src/solana/jito/jitoService.ts @@ -0,0 +1,121 @@ +import type { + JitoGetBundleStatusesResult, + JitoGetInflightBundleStatusesResult, + JitoGetTipAccountsResult, + JitoJsonRpcRequest, + JitoJsonRpcResponse, + JitoSendBundleResult, + JitoSendTransactionResult, + JitoTipFloorEntry, +} from './types' + +const JITO_TIP_FLOOR_URL = 'https://bundles.jito.wtf/api/v1/bundles/tip_floor' + +let requestId = 0 +const nextId = () => ++requestId + +const jsonRpc = async ( + baseUrl: string, + path: string, + method: string, + params: unknown[], +): Promise => { + const body: JitoJsonRpcRequest = { + jsonrpc: '2.0', + id: nextId(), + method, + params, + } + + const response = await fetch(`${baseUrl}${path}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }) + + if (!response.ok) { + throw new Error(`Jito RPC error: ${response.status} ${response.statusText}`) + } + + const data = (await response.json()) as JitoJsonRpcResponse + + if (data.error) { + throw new Error(`Jito RPC error: ${data.error.message} (code ${data.error.code})`) + } + + return data.result +} + +export const createJitoService = (blockEngineUrl: string) => { + const txPath = '/api/v1/transactions' + const bundlePath = '/api/v1/bundles' + + return { + /** + * Send a single transaction via Jito's enhanced endpoint. + * Internally sets skip_preflight=true and forwards as a bundle for MEV protection. + * The transaction MUST include a tip instruction (min 1000 lamports). + */ + sendTransaction: (base64Tx: string): Promise => + jsonRpc(blockEngineUrl, txPath, 'sendTransaction', [ + base64Tx, + { encoding: 'base64' }, + ]), + + /** + * Send a bundle of up to 5 transactions for atomic sequential execution. + * All transactions must be fully signed. Tip instruction must be in the last tx. + * Returns a bundle ID (SHA-256 hash of all transaction signatures). + */ + sendBundle: (base64Txs: string[]): Promise => + jsonRpc(blockEngineUrl, bundlePath, 'sendBundle', [ + base64Txs, + { encoding: 'base64' }, + ]), + + /** + * Check the status of landed bundles. Returns null for bundles that haven't landed. + * Max 5 bundle IDs per request. + */ + getBundleStatuses: (bundleIds: string[]): Promise => + jsonRpc(blockEngineUrl, bundlePath, 'getBundleStatuses', [ + bundleIds, + ]), + + /** + * Check the status of in-flight bundles (5-minute lookback window). + * Status: Invalid | Pending | Failed | Landed + * Max 5 bundle IDs per request. + */ + getInflightBundleStatuses: ( + bundleIds: string[], + ): Promise => + jsonRpc( + blockEngineUrl, + bundlePath, + 'getInflightBundleStatuses', + [bundleIds], + ), + + /** + * Get the list of dynamic tip account addresses. Pick one at random to reduce contention. + * These addresses can change - do NOT hardcode them. + */ + getTipAccounts: (): Promise => + jsonRpc(blockEngineUrl, bundlePath, 'getTipAccounts', []), + + /** + * Get the current tip floor from the REST API (not JSON-RPC). + * Values are in SOL (not lamports). Use ema_landed_tips_50th_percentile as a reasonable default. + */ + getTipFloor: async (): Promise => { + const response = await fetch(JITO_TIP_FLOOR_URL) + if (!response.ok) { + throw new Error(`Jito tip floor error: ${response.status} ${response.statusText}`) + } + return (await response.json()) as JitoTipFloorEntry[] + }, + } +} + +export type JitoService = ReturnType diff --git a/packages/chain-adapters/src/solana/jito/types.ts b/packages/chain-adapters/src/solana/jito/types.ts new file mode 100644 index 00000000000..502e25ccfd6 --- /dev/null +++ b/packages/chain-adapters/src/solana/jito/types.ts @@ -0,0 +1,62 @@ +// Jito Block Engine JSON-RPC types +// API docs: https://docs.jito.wtf/lowlatencytxnsend/ + +export type JitoJsonRpcRequest = { + jsonrpc: '2.0' + id: number + method: string + params: unknown[] +} + +export type JitoJsonRpcResponse = { + jsonrpc: '2.0' + id: number + result: T + error?: { code: number; message: string } +} + +// sendTransaction response: transaction signature (base-58 string) +export type JitoSendTransactionResult = string + +// sendBundle response: bundle ID (SHA-256 hash of the bundle's transaction signatures) +export type JitoSendBundleResult = string + +// getBundleStatuses response +export type JitoBundleStatus = { + bundle_id: string + transactions: string[] + slot: number + confirmation_status: 'processed' | 'confirmed' | 'finalized' | null + err: { Ok: null } | Record +} + +export type JitoGetBundleStatusesResult = { + context: { slot: number } + value: (JitoBundleStatus | null)[] +} + +// getInflightBundleStatuses response +export type JitoInflightBundleStatus = { + bundle_id: string + status: 'Invalid' | 'Pending' | 'Failed' | 'Landed' + landed_slot: number | null +} + +export type JitoGetInflightBundleStatusesResult = { + context: { slot: number } + value: JitoInflightBundleStatus[] +} + +// getTipAccounts response: array of base-58 encoded tip account addresses +export type JitoGetTipAccountsResult = string[] + +// Tip floor REST API response (values in SOL, not lamports) +export type JitoTipFloorEntry = { + time: string + landed_tips_25th_percentile: number + landed_tips_50th_percentile: number + landed_tips_75th_percentile: number + landed_tips_95th_percentile: number + landed_tips_99th_percentile: number + ema_landed_tips_50th_percentile: number +} diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 25d36cb8dea..615ee1b1765 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -21,8 +21,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/errors/package.json b/packages/errors/package.json index aa376a631b9..ea976e85019 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/errors", - "version": "1.1.5", + "version": "1.1.6", "description": "Common set of typed errors", "repository": "https://github.com/shapeshift/web", "type": "module", @@ -21,8 +21,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-coinbase/package.json b/packages/hdwallet-coinbase/package.json index 97877e6cf9b..01f524ad81c 100644 --- a/packages/hdwallet-coinbase/package.json +++ b/packages/hdwallet-coinbase/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-core/package.json b/packages/hdwallet-core/package.json index b773632d98e..b248f5e09a5 100644 --- a/packages/hdwallet-core/package.json +++ b/packages/hdwallet-core/package.json @@ -32,8 +32,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-gridplus/package.json b/packages/hdwallet-gridplus/package.json index 2b62afb14e6..df68f8ed766 100644 --- a/packages/hdwallet-gridplus/package.json +++ b/packages/hdwallet-gridplus/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-integration/package.json b/packages/hdwallet-integration/package.json index af8c42ccdf6..00537bf9512 100644 --- a/packages/hdwallet-integration/package.json +++ b/packages/hdwallet-integration/package.json @@ -6,7 +6,7 @@ "type": "module", "scripts": { "test": "vitest run", - "clean": "rm -rf node_modules" + "clean": "rm -rf dist node_modules" }, "dependencies": { "@bitcoinerlab/secp256k1": "^1.1.1", diff --git a/packages/hdwallet-keepkey-electron/package.json b/packages/hdwallet-keepkey-electron/package.json index 866d84f715f..f13a3a08f58 100644 --- a/packages/hdwallet-keepkey-electron/package.json +++ b/packages/hdwallet-keepkey-electron/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-keepkey-nodehid/package.json b/packages/hdwallet-keepkey-nodehid/package.json index fede0cc44da..492f7dd29f1 100644 --- a/packages/hdwallet-keepkey-nodehid/package.json +++ b/packages/hdwallet-keepkey-nodehid/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-keepkey-nodewebusb/package.json b/packages/hdwallet-keepkey-nodewebusb/package.json index 6ffde716090..8c859b0ae01 100644 --- a/packages/hdwallet-keepkey-nodewebusb/package.json +++ b/packages/hdwallet-keepkey-nodewebusb/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-keepkey-tcp/package.json b/packages/hdwallet-keepkey-tcp/package.json index 8678d08f939..1917dc13521 100644 --- a/packages/hdwallet-keepkey-tcp/package.json +++ b/packages/hdwallet-keepkey-tcp/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", @@ -30,7 +30,7 @@ "dependencies": { "@shapeshiftoss/hdwallet-core": "workspace:^", "@shapeshiftoss/hdwallet-keepkey": "workspace:^", - "axios": "^0.21.1" + "axios": "^1.13.5" }, "sideEffects": false } diff --git a/packages/hdwallet-keepkey-webusb/package.json b/packages/hdwallet-keepkey-webusb/package.json index 2111ad66d11..9d73c64a858 100644 --- a/packages/hdwallet-keepkey-webusb/package.json +++ b/packages/hdwallet-keepkey-webusb/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-keepkey/package.json b/packages/hdwallet-keepkey/package.json index e2df2c98398..f5960740abe 100644 --- a/packages/hdwallet-keepkey/package.json +++ b/packages/hdwallet-keepkey/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-keplr/package.json b/packages/hdwallet-keplr/package.json index 6b8b691cbda..4f4828fa728 100644 --- a/packages/hdwallet-keplr/package.json +++ b/packages/hdwallet-keplr/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-ledger-webhid/package.json b/packages/hdwallet-ledger-webhid/package.json index 21cebc3edc2..ed689e9d854 100644 --- a/packages/hdwallet-ledger-webhid/package.json +++ b/packages/hdwallet-ledger-webhid/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-ledger-webusb/package.json b/packages/hdwallet-ledger-webusb/package.json index 1d2394a7b33..32221b45264 100644 --- a/packages/hdwallet-ledger-webusb/package.json +++ b/packages/hdwallet-ledger-webusb/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-ledger/package.json b/packages/hdwallet-ledger/package.json index dcacdfa0195..7d416ab9e77 100644 --- a/packages/hdwallet-ledger/package.json +++ b/packages/hdwallet-ledger/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-metamask-multichain/package.json b/packages/hdwallet-metamask-multichain/package.json index 1e291f67098..a40049c9857 100644 --- a/packages/hdwallet-metamask-multichain/package.json +++ b/packages/hdwallet-metamask-multichain/package.json @@ -20,20 +20,26 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", "postbuild:cjs": "echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json" }, "dependencies": { + "@bitcoinerlab/secp256k1": "^1.1.1", "@metamask/detect-provider": "^1.2.0", "@metamask/onboarding": "^1.0.1", + "@shapeshiftoss/bitcoinjs-lib": "7.0.0-shapeshift.2", "@shapeshiftoss/common-api": "^9.3.0", "@shapeshiftoss/hdwallet-core": "workspace:^", + "@solana/web3.js": "1.98.0", "@shapeshiftoss/metamask-snaps-adapter": "^1.0.12", "@shapeshiftoss/metamask-snaps-types": "^1.0.12", + "@wallet-standard/app": "^1.1.0", + "@wallet-standard/base": "^1.1.0", + "@wallet-standard/features": "^1.1.0", "eth-rpc-errors": "^4.0.3", "ethers": "5.7.2", "lodash": "^4.17.23", diff --git a/packages/hdwallet-metamask-multichain/src/adapter.ts b/packages/hdwallet-metamask-multichain/src/adapter.ts index c056fdb6f61..21838f60849 100644 --- a/packages/hdwallet-metamask-multichain/src/adapter.ts +++ b/packages/hdwallet-metamask-multichain/src/adapter.ts @@ -2,6 +2,7 @@ import MetaMaskOnboarding from '@metamask/onboarding' import * as core from '@shapeshiftoss/hdwallet-core' import { createStore } from 'mipd' +import type { MetaMaskNativeMultiChainHDWallet } from './native-multichain' import { MetaMaskMultiChainHDWallet } from './shapeshift-multichain' export const mipdstore = createStore() @@ -11,21 +12,29 @@ const METAMASK_RDNS = 'io.metamask' export class MetaMaskAdapter { keyring: core.Keyring providerRdns: string + private useNativeMultichain: boolean - private constructor(keyring: core.Keyring, providerRdns: string) { + private constructor(keyring: core.Keyring, providerRdns: string, useNativeMultichain: boolean) { this.keyring = keyring this.providerRdns = providerRdns + this.useNativeMultichain = useNativeMultichain } - public static useKeyring(keyring: core.Keyring, providerRdns: string) { - return new MetaMaskAdapter(keyring, providerRdns) + public static useKeyring( + keyring: core.Keyring, + providerRdns: string, + { useNativeMultichain = false }: { useNativeMultichain?: boolean } = {}, + ) { + return new MetaMaskAdapter(keyring, providerRdns, useNativeMultichain) } public async initialize(): Promise { return Object.keys(this.keyring.wallets).length } - public async pairDevice(): Promise { + public async pairDevice(): Promise< + MetaMaskMultiChainHDWallet | MetaMaskNativeMultiChainHDWallet | undefined + > { const maybeEip6963Provider = mipdstore.findProvider({ rdns: this.providerRdns }) if (!maybeEip6963Provider && this.providerRdns === METAMASK_RDNS) { const onboarding = new MetaMaskOnboarding() @@ -51,6 +60,19 @@ export class MetaMaskAdapter { console.error('Could not get MetaMask accounts. ') throw error } + + if (this.useNativeMultichain) { + const { MetaMaskNativeMultiChainHDWallet: NativeMultiChainWallet } = await import( + './native-multichain' + ) + const wallet = new NativeMultiChainWallet(maybeEip6963Provider) + await wallet.initialize() + const deviceID = await wallet.getDeviceID() + this.keyring.add(wallet, deviceID) + this.keyring.emit(['MetaMask(Native Multichain)', deviceID, core.Events.CONNECT], deviceID) + return wallet + } + const wallet = new MetaMaskMultiChainHDWallet(maybeEip6963Provider) await wallet.initialize() const deviceID = await wallet.getDeviceID() diff --git a/packages/hdwallet-metamask-multichain/src/index.ts b/packages/hdwallet-metamask-multichain/src/index.ts index 09cdcb1918e..c197bd2d741 100644 --- a/packages/hdwallet-metamask-multichain/src/index.ts +++ b/packages/hdwallet-metamask-multichain/src/index.ts @@ -1,2 +1,3 @@ export * from './adapter' +export * from './native-multichain' export * from './shapeshift-multichain' diff --git a/packages/hdwallet-metamask-multichain/src/native-multichain.test.ts b/packages/hdwallet-metamask-multichain/src/native-multichain.test.ts new file mode 100644 index 00000000000..3d29cba4a61 --- /dev/null +++ b/packages/hdwallet-metamask-multichain/src/native-multichain.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest' + +import { isMetaMaskNativeMultichain } from './native-multichain' + +describe('isMetaMaskNativeMultichain', () => { + it('should return false for null', () => { + expect(isMetaMaskNativeMultichain(null)).toBe(false) + }) + + it('should return false for a non-object value', () => { + // cast to satisfy TS - runtime receives a primitive + expect(isMetaMaskNativeMultichain('not-a-wallet' as any)).toBe(false) + expect(isMetaMaskNativeMultichain(42 as any)).toBe(false) + }) + + it('should return false for a regular MetaMask wallet without _isMetaMaskNativeMultichain', () => { + const wallet = { _isMetaMask: true } as any + expect(isMetaMaskNativeMultichain(wallet)).toBeFalsy() + }) + + it('should return false when _isMetaMaskNativeMultichain is falsy', () => { + const wallet = { _isMetaMask: true, _isMetaMaskNativeMultichain: false } as any + expect(isMetaMaskNativeMultichain(wallet)).toBe(false) + }) + + it('should return true for a native multichain wallet', () => { + const wallet = { _isMetaMask: true, _isMetaMaskNativeMultichain: true } as any + expect(isMetaMaskNativeMultichain(wallet)).toBe(true) + }) +}) diff --git a/packages/hdwallet-metamask-multichain/src/native-multichain.ts b/packages/hdwallet-metamask-multichain/src/native-multichain.ts new file mode 100644 index 00000000000..12b143132f3 --- /dev/null +++ b/packages/hdwallet-metamask-multichain/src/native-multichain.ts @@ -0,0 +1,936 @@ +import ecc from '@bitcoinerlab/secp256k1' +import * as bitcoin from '@shapeshiftoss/bitcoinjs-lib' +import type { AddEthereumChainParameter, Address } from '@shapeshiftoss/hdwallet-core' +import * as core from '@shapeshiftoss/hdwallet-core' +import { BTCInputScriptType } from '@shapeshiftoss/hdwallet-core' +import { VersionedTransaction } from '@solana/web3.js' +import { getWallets } from '@wallet-standard/app' +import type { Wallet, WalletAccount } from '@wallet-standard/base' +import { ethErrors, serializeError } from 'eth-rpc-errors' +import isObject from 'lodash/isObject' +import type { EIP6963ProviderDetail } from 'mipd' + +import * as Eth from './ethereum' + +// MetaMask Bitcoin Wallet Standard feature types (not typed in @wallet-standard) +type BitcoinConnectFeature = { + 'bitcoin:connect': { + version: '1.0.0' + connect: (input?: { + purposes?: ('payment' | 'ordinals')[] + }) => Promise<{ addresses: { address: string; publicKey: Uint8Array }[] }> + } +} + +type BitcoinSignTransactionFeature = { + 'bitcoin:signTransaction': { + version: '1.0.0' + signTransaction: (input: { + psbt: string // base64-encoded PSBT + inputsToSign: { + address: string + signingIndexes: number[] + sigHash?: number + }[] + }) => Promise<{ psbt: string }> // signed base64-encoded PSBT + } +} + +type BtcWalletStandard = Wallet & { + features: { + 'standard:connect': { + connect: (input?: { silent?: boolean }) => Promise<{ accounts: readonly WalletAccount[] }> + } + 'bitcoin:connect': BitcoinConnectFeature['bitcoin:connect'] + 'bitcoin:signTransaction': BitcoinSignTransactionFeature['bitcoin:signTransaction'] + } +} + +function getMetaMaskBtcWallet(): BtcWalletStandard | undefined { + const wallets = getWallets().get() + const btcWallet = wallets.find( + w => w.name === 'MetaMask' && w.chains.some(c => c.startsWith('bitcoin:')), + ) as BtcWalletStandard | undefined + return btcWallet +} + +const getNetwork = (coin: string): bitcoin.networks.Network => { + switch (coin.toLowerCase()) { + case 'bitcoin': + return bitcoin.networks.bitcoin + default: + throw new Error(`Unsupported coin: ${coin}`) + } +} + +const btcGetAccountPathsImpl = (msg: core.BTCGetAccountPaths): core.BTCAccountPath[] => { + const slip44 = core.slip44ByCoin(msg.coin) + if (slip44 === undefined) return [] + + const bip84 = core.segwitNativeAccount(msg.coin, slip44, msg.accountIdx) + + const coinPaths = { + bitcoin: [bip84], + } as Partial> + + let paths: core.BTCAccountPath[] = coinPaths[msg.coin.toLowerCase()] || [] + + if (msg.scriptType !== undefined) { + paths = paths.filter(path => { + return path.scriptType === msg.scriptType + }) + } + + return paths +} + +type SolanaSignTransactionMethod = ( + ...inputs: readonly { + readonly account: WalletAccount + readonly transaction: Uint8Array + readonly chain?: string + }[] +) => Promise + +type SolWalletStandard = Wallet & { + features: { + 'standard:connect': { + connect: (input?: { silent?: boolean }) => Promise<{ accounts: readonly WalletAccount[] }> + } + 'solana:signTransaction': { + signTransaction: SolanaSignTransactionMethod + } + } +} + +function getMetaMaskSolWallet(): SolWalletStandard | undefined { + const wallets = getWallets().get() + const solWallet = wallets.find( + w => w.name === 'MetaMask' && w.chains.some(c => c.startsWith('solana:')), + ) as SolWalletStandard | undefined + return solWallet +} + +export function isMetaMaskNativeMultichain( + wallet: core.HDWallet | null, +): wallet is MetaMaskNativeMultiChainHDWallet { + return isObject(wallet) && (wallet as any)._isMetaMaskNativeMultichain +} + +export class MetaMaskNativeMultiChainHDWalletInfo + implements core.HDWalletInfo, core.ETHWalletInfo, core.SolanaWalletInfo +{ + ethGetChainId?(): Promise { + throw new Error('Method not implemented.') + } + + ethSwitchChain?(_params: core.AddEthereumChainParameter): Promise { + throw new Error('Method not implemented.') + } + + ethAddChain?(_params: core.AddEthereumChainParameter): Promise { + throw new Error('Method not implemented.') + } + _supportsBTCInfo = false + readonly _supportsETHInfo = true + _supportsSolanaInfo = false + readonly _supportsCosmosInfo = false + readonly _supportsBinanceInfo = false + readonly _supportsRippleInfo = false + readonly _supportsEosInfo = false + readonly _supportsThorchainInfo = false + + public getVendor(): string { + return 'MetaMask' + } + + public hasOnDevicePinEntry(): boolean { + return false + } + + public hasOnDevicePassphrase(): boolean { + return true + } + + public hasOnDeviceDisplay(): boolean { + return true + } + + public hasOnDeviceRecovery(): boolean { + return true + } + + public hasNativeShapeShift(_srcCoin: core.Coin, _dstCoin: core.Coin): boolean { + return false + } + + public supportsBip44Accounts(): boolean { + return true + } + + public supportsOfflineSigning(): boolean { + return false + } + + public supportsBroadcast(): boolean { + return true + } + + public describePath(msg: core.DescribePath): core.PathDescription { + switch (msg.coin) { + case 'Bitcoin': + return core.describeUTXOPath(msg.path, msg.coin, msg.scriptType!) + + case 'Ethereum': + return core.describeETHPath(msg.path) + + case 'Solana': + return core.solanaDescribePath(msg.path) + + default: + throw new Error('Unsupported path') + } + } + + public async bitcoinSupportsNetwork(chainId = 0): Promise { + return chainId === 0 + } + + public async bitcoinSupportsSecureTransfer(): Promise { + return false + } + + public bitcoinSupportsNativeShapeShift(): boolean { + return false + } + + public bitcoinGetAccountPaths(msg: core.BTCGetAccountPaths): core.BTCAccountPath[] { + return btcGetAccountPathsImpl(msg) + } + + public bitcoinNextAccountPath(_msg: core.BTCAccountPath): core.BTCAccountPath | undefined { + return undefined + } + + public async ethSupportsNetwork(chainId: number): Promise { + return chainId === 1 + } + + public async ethSupportsSecureTransfer(): Promise { + return false + } + + public ethSupportsNativeShapeShift(): boolean { + return false + } + + public ethGetAccountPaths(msg: core.ETHGetAccountPath): core.ETHAccountPath[] { + return Eth.ethGetAccountPaths(msg) + } + + public ethNextAccountPath(_msg: core.ETHAccountPath): core.ETHAccountPath | undefined { + return undefined + } + + public async ethSupportsEIP1559(): Promise { + return true + } + + public solanaGetAccountPaths(msg: core.SolanaGetAccountPaths): core.SolanaAccountPath[] { + return core.solanaGetAccountPaths(msg) + } + + public solanaNextAccountPath(_msg: core.SolanaAccountPath): core.SolanaAccountPath | undefined { + return undefined + } +} + +// Helper to extract account index from BIP44 addressNList +// BIP44: m/purpose'/coin'/account'/change/index +// Account is at index 2, hardened (0x80000000 mask) +const getAccountIndex = (addressNList: core.BIP32Path): number => { + if (addressNList.length >= 3) { + return addressNList[2] & ~0x80000000 // strip hardened bit + } + return 0 +} + +export class MetaMaskNativeMultiChainHDWallet + implements core.HDWallet, core.BTCWallet, core.ETHWallet, core.SolanaWallet +{ + readonly _supportsETH = true + readonly _supportsETHInfo = true + // BTC/SOL support is dynamic - depends on whether MetaMask registers + // the corresponding Wallet Standard wallets in this browser version. + _supportsBTCInfo = false + _supportsBTC = false + _supportsSolanaInfo = false + _supportsSolana = false + readonly _supportsCosmosInfo = false + readonly _supportsCosmos = false + readonly _supportsEthSwitchChain = true + readonly _supportsAvalanche = true + readonly _supportsOptimism = true + readonly _supportsBSC = true + readonly _supportsPolygon = true + readonly _supportsGnosis = true + readonly _supportsArbitrum = true + readonly _supportsArbitrumNova = true + readonly _supportsBase = true + readonly _supportsMonad = true + readonly _supportsPlasma = true + readonly _supportsKatana = true + readonly _supportsEthereal = true + readonly _supportsCelo = true + readonly _supportsFlowEvm = true + readonly _supportsStory = true + readonly _supportsSonic = true + readonly _supportsBob = true + readonly _supportsMode = true + readonly _supportsSei = true + readonly _supportsHyperEvm = true + readonly _supportsMantle = true + readonly _supportsInk = true + readonly _supportsMegaEth = true + readonly _supportsPlume = true + readonly _supportsZkSyncEra = true + readonly _supportsBlast = true + readonly _supportsWorldChain = true + readonly _supportsHemi = true + readonly _supportsBerachain = true + readonly _supportsLinea = true + readonly _supportsScroll = true + readonly _supportsCronos = true + readonly _supportsUnichain = true + readonly _supportsSoneium = true + readonly _supportsOsmosisInfo = false + readonly _supportsOsmosis = false + readonly _supportsBinanceInfo = false + readonly _supportsBinance = false + readonly _supportsDebugLink = false + readonly _isPortis = false + readonly _isMetaMask = true + readonly _isMetaMaskNativeMultichain = true + readonly _supportsRippleInfo = false + readonly _supportsRipple = false + readonly _supportsEosInfo = false + readonly _supportsEos = false + readonly _supportsThorchainInfo = false + readonly _supportsThorchain = false + + info: MetaMaskNativeMultiChainHDWalletInfo & core.HDWalletInfo + ethAddress?: Address | null + private btcAddresses: string[] = [] + private btcPublicKeys: Uint8Array[] = [] + private solAddresses: string[] = [] + private btcConnected = false + private btcConnecting: Promise | null = null + private solConnected = false + private solConnecting: Promise | null = null + private solAccounts: readonly WalletAccount[] = [] + private solWallet?: SolWalletStandard + private btcWallet?: BtcWalletStandard + providerRdns: string + provider: any + + constructor(provider: EIP6963ProviderDetail) { + this.info = new MetaMaskNativeMultiChainHDWalletInfo() + + this.providerRdns = provider.info.rdns + this.provider = provider.provider + + // Detect which non-EVM Wallet Standard wallets MetaMask exposes in this version. + // This must run in the constructor (not initialize()) so flags are set before + // any supportsSolana()/supportsBTC() checks run against this wallet instance. + const hasBtc = !!getMetaMaskBtcWallet() + const hasSol = !!getMetaMaskSolWallet() + this._supportsBTC = hasBtc + this._supportsBTCInfo = hasBtc + this._supportsSolana = hasSol + this._supportsSolanaInfo = hasSol + this.info._supportsBTCInfo = hasBtc + this.info._supportsSolanaInfo = hasSol + } + + transport?: core.Transport | undefined + + async getFeatures(): Promise> { + return {} + } + + public async isLocked(): Promise { + try { + return !this.provider._metamask.isUnlocked() + } catch (e) { + // This may not be properly implemented in MM impersonators, e.g + // https://github.com/zeriontech/zerion-wallet-extension/blob/294630a4e1ef303205a6e6dd681245a27c8d1eec/src/modules/ethereum/provider.ts#L36C1-L39 + // Assume unlocked, but log the error regardless in case this happens with *actual* MM + console.error(e) + return false + } + } + + public getVendor(): string { + return 'MetaMask' + } + + public async getModel(): Promise { + return 'MetaMask (Native Multichain)' + } + + public async getLabel(): Promise { + return 'MetaMask (Native Multichain)' + } + + public async initialize(): Promise { + // noop - BTC/SOL support flags are set in constructor + } + + public hasOnDevicePinEntry(): boolean { + return this.info.hasOnDevicePinEntry() + } + + public hasOnDevicePassphrase(): boolean { + return this.info.hasOnDevicePassphrase() + } + + public hasOnDeviceDisplay(): boolean { + return this.info.hasOnDeviceDisplay() + } + + public hasOnDeviceRecovery(): boolean { + return this.info.hasOnDeviceRecovery() + } + + public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { + return this.info.hasNativeShapeShift(srcCoin, dstCoin) + } + + public supportsBip44Accounts(): boolean { + return true + } + + public supportsOfflineSigning(): boolean { + return false + } + + public supportsBroadcast(): boolean { + return true + } + + public async clearSession(): Promise { + // TODO: Can we lock MetaMask from here? + } + + public async ping(msg: core.Ping): Promise { + // no ping function for MetaMask, so just returning Core.Pong + return { msg: msg.msg } + } + + public async sendPin(_pin: string): Promise { + // no concept of pin in MetaMask + } + + public async sendPassphrase(_passphrase: string): Promise { + // cannot send passphrase to MetaMask. Could show the widget? + } + + public async sendCharacter(_charater: string): Promise { + // no concept of sendCharacter in MetaMask + } + + public async sendWord(_word: string): Promise { + // no concept of sendWord in MetaMask + } + + public async cancel(): Promise { + // no concept of cancel in MetaMask + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public async wipe(): Promise {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public async reset(_msg: core.ResetDevice): Promise {} + + public async recover(_msg: core.RecoverDevice): Promise { + // no concept of recover in MetaMask + } + + public async loadDevice(_msg: core.LoadDevice): Promise { + // TODO: Does MetaMask allow this to be done programatically? + } + + public describePath(msg: core.DescribePath): core.PathDescription { + return this.info.describePath(msg) + } + + public async getPublicKeys(msg: core.GetPublicKey[]): Promise<(core.PublicKey | null)[] | null> { + // Connect to MM BTC wallet and return the public key for BTC requests + const hasBtcRequest = msg.some(m => m.coin === 'Bitcoin') + if (!hasBtcRequest) return null + + const connected = await this.connectBtcWallet() + if (!connected || !this.btcPublicKeys.length) return null + + // Return the public key as xpub is not available from Wallet Standard, + // but the raw public key hex is sufficient for our needs + return msg.map(m => { + if (m.coin === 'Bitcoin') { + const accountIndex = getAccountIndex(m.addressNList) + const pubKey = this.btcPublicKeys[accountIndex] + if (pubKey) { + return { xpub: Buffer.from(pubKey).toString('hex') } + } + } + return null + }) + } + + public async isInitialized(): Promise { + return true + } + + /** BITCOIN - uses Bitcoin Wallet Standard to interact with MetaMask's native BTC wallet */ + + private getBtcWallet(): BtcWalletStandard | undefined { + if (!this.btcWallet) { + this.btcWallet = getMetaMaskBtcWallet() + } + return this.btcWallet + } + + private async connectBtcWallet(): Promise { + if (this.btcConnected) return this.btcAddresses.length > 0 + if (this.btcConnecting) return this.btcConnecting + + this.btcConnecting = (async () => { + const wallet = this.getBtcWallet() + if (!wallet) { + this.btcConnected = true + return false + } + + try { + const { addresses } = await wallet.features['bitcoin:connect'].connect({ + purposes: ['payment'], + }) + if (!addresses.length) { + this.btcConnected = true + return false + } + // Cache ALL addresses and public keys from MM + this.btcAddresses = addresses.map(a => a.address) + this.btcPublicKeys = addresses.map(a => a.publicKey) + this.btcConnected = true + return true + } catch (e) { + console.error('[MM Native BTC] bitcoin:connect FAILED:', e) + this.btcConnected = true + return false + } finally { + this.btcConnecting = null + } + })() + + return this.btcConnecting + } + + public async btcSupportsSecureTransfer(): Promise { + return false + } + + public btcSupportsNativeShapeShift(): boolean { + return false + } + + public btcGetAccountPaths(msg: core.BTCGetAccountPaths): core.BTCAccountPath[] { + return btcGetAccountPathsImpl(msg) + } + + public btcNextAccountPath(_msg: core.BTCAccountPath): core.BTCAccountPath | undefined { + return undefined + } + + public async btcGetAddress(msg: core.BTCGetAddress): Promise { + const connected = await this.connectBtcWallet() + if (!connected) return null + + const accountIndex = getAccountIndex(msg.addressNList) + return this.btcAddresses[accountIndex] ?? null + } + + public async btcSignTx(msg: core.BTCSignTx): Promise { + const wallet = this.getBtcWallet() + if (!wallet) return null + + const connected = await this.connectBtcWallet() + if (!connected) return null + + // Init ecc lib required for taproot sends + bitcoin.initEccLib(ecc) + + const network = getNetwork(msg.coin) + const psbt = new bitcoin.Psbt({ network }) + + psbt.setVersion(msg.version ?? 2) + if (msg.locktime) { + psbt.setLocktime(msg.locktime) + } + + // Add inputs + for (const input of msg.inputs) { + switch (input.scriptType) { + case BTCInputScriptType.SpendWitness: { + psbt.addInput({ + hash: input.txid, + index: input.vout, + nonWitnessUtxo: Buffer.from(input.hex, 'hex'), + ...(input.sequence !== undefined && { sequence: input.sequence }), + }) + break + } + default: + throw new Error(`Unsupported script type: ${input.scriptType}`) + } + } + + // Add outputs + for (const output of msg.outputs) { + if (!output.amount) throw new Error('Invalid output - missing amount.') + + const address = await (async () => { + if (output.address) return output.address + + if (output.addressNList) { + const outputAddress = await this.btcGetAddress({ + addressNList: output.addressNList, + coin: msg.coin, + showDisplay: false, + }) + if (!outputAddress) throw new Error('Could not get address from wallet') + return outputAddress + } + })() + + if (!address) throw new Error('Invalid output - no address') + psbt.addOutput({ address, value: BigInt(output.amount) }) + } + + // OP_RETURN for THORChain memos + if (msg.opReturnData) { + const data = Buffer.from(msg.opReturnData, 'utf-8') + const embed = bitcoin.payments.embed({ data: [data] }) + const script = embed.output + if (!script) throw new Error('unable to build OP_RETURN script') + psbt.addOutput({ script, value: BigInt(0) }) + } + + // Build inputsToSign - group by address + const inputsToSign = await Promise.all( + msg.inputs.map(async (input, index) => { + const address = await this.btcGetAddress({ + addressNList: input.addressNList, + coin: msg.coin, + showDisplay: false, + }) + + if (!address) throw new Error('Could not get address from wallet') + + return { + address, + signingIndexes: [index], + sigHash: bitcoin.Transaction.SIGHASH_ALL, + } + }), + ) + + // Sign via MM Bitcoin Wallet Standard - uses base64 encoded PSBT + const psbtBase64 = Buffer.from(psbt.toBuffer()).toString('base64') + const { psbt: signedPsbtBase64 } = await wallet.features[ + 'bitcoin:signTransaction' + ].signTransaction({ + psbt: psbtBase64, + inputsToSign, + }) + + const signedPsbt = bitcoin.Psbt.fromBuffer(Buffer.from(signedPsbtBase64, 'base64'), { + network, + }) + + signedPsbt.finalizeAllInputs() + + const tx = signedPsbt.extractTransaction() + + // If this is a THORChain transaction, validate the vout ordering + if (msg.vaultAddress && !core.validateVoutOrdering(msg, tx)) { + throw new Error('Improper vout ordering for BTC Thorchain transaction') + } + + const signatures = signedPsbt.data.inputs.map(input => + input.partialSig ? Buffer.from(input.partialSig[0].signature).toString('hex') : '', + ) + + return { + signatures, + serializedTx: tx.toHex(), + } + } + + public async btcSignMessage(_msg: core.BTCSignMessage): Promise { + // MM doesn't support BTC message signing via Wallet Standard yet + return null + } + + public async btcVerifyMessage(_msg: core.BTCVerifyMessage): Promise { + // MM doesn't support BTC message verification via Wallet Standard yet + return null + } + + public async btcSupportsScriptType( + coin: string, + scriptType?: core.BTCInputScriptType | undefined, + ): Promise { + if (coin !== 'Bitcoin') return false + // MetaMask only supports native segwit (bip84) + return scriptType === BTCInputScriptType.SpendWitness + } + + public async btcSupportsCoin(coin: core.Coin): Promise { + return coin === 'Bitcoin' + } + + /** ETHEREUM - identical to existing MetaMaskMultiChainHDWallet */ + + // eslint-disable-next-line @typescript-eslint/no-empty-function + public async disconnect(): Promise {} + + public async ethSupportsNetwork(chainId = 1): Promise { + return chainId === 1 + } + + public async ethGetChainId(): Promise { + try { + // chainId as hex string + const chainId: string = await this.provider.request({ method: 'eth_chainId' }) + return parseInt(chainId, 16) + } catch (e) { + console.error(e) + return null + } + } + + public async ethAddChain(params: AddEthereumChainParameter): Promise { + // at this point, we know that we're in the context of a valid MetaMask provider + await this.provider.request({ method: 'wallet_addEthereumChain', params: [params] }) + } + + public async ethSwitchChain(params: AddEthereumChainParameter): Promise { + try { + // at this point, we know that we're in the context of a valid MetaMask provider + await this.provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: params.chainId }], + }) + } catch (e: any) { + const error = serializeError(e) + // https://docs.metamask.io/guide/ethereum-provider.html#errors + // Internal error, which in the case of wallet_switchEthereumChain call means the chain isn't currently added to the wallet + if (error.code === -32603) { + try { + await this.ethAddChain(params) + return + } catch (addChainE: any) { + const addChainError = serializeError(addChainE) + + if (addChainError.code === 4001) { + throw ethErrors.provider.userRejectedRequest() + } + + if (!(addChainError.data as any)?.originalError) { + throw addChainError + } + + throw (addChainError.data as any).originalError as { + code: number + message: string + stack: string + } + } + } + + if (error.code === 4001) { + throw ethErrors.provider.userRejectedRequest() + } + + throw (error.data as any).originalError as { + code: number + message: string + stack: string + } + } + } + + public async ethSupportsSecureTransfer(): Promise { + return false + } + + public ethSupportsNativeShapeShift(): boolean { + return false + } + + public async ethSupportsEIP1559(): Promise { + return true + } + + public ethGetAccountPaths(msg: core.ETHGetAccountPath): core.ETHAccountPath[] { + return Eth.ethGetAccountPaths(msg) + } + + public ethNextAccountPath(_msg: core.ETHAccountPath): core.ETHAccountPath | undefined { + return this.info.ethNextAccountPath(_msg) + } + + // TODO: Respect msg.addressNList! + public async ethGetAddress(_msg: core.ETHGetAddress): Promise { + if (this.ethAddress) { + return this.ethAddress + } + const address = await Eth.ethGetAddress(this.provider) + if (address) { + this.ethAddress = address + return address + } else { + this.ethAddress = null + return null + } + } + + public async ethSignTx(msg: core.ETHSignTx): Promise { + const address = await this.ethGetAddress(this.provider) + return address ? Eth.ethSignTx(msg, this.provider, address) : null + } + + public async ethSendTx(msg: core.ETHSignTx): Promise { + const txid = await this.ethGetAddress(this.provider) + return txid ? Eth.ethSendTx(msg, this.provider, txid) : null + } + + public async ethSignMessage(msg: core.ETHSignMessage): Promise { + const address = await this.ethGetAddress(this.provider) + return address ? Eth.ethSignMessage(msg, this.provider, address) : null + } + + async ethSignTypedData(msg: core.ETHSignTypedData): Promise { + const address = await this.ethGetAddress(this.provider) + return address ? Eth.ethSignTypedData(msg, this.provider, address) : null + } + + public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise { + return Eth.ethVerifyMessage(msg, this.provider) + } + + /** SOLANA - uses Wallet Standard to interact with MetaMask's native SOL wallet */ + + public solanaGetAccountPaths(msg: core.SolanaGetAccountPaths): core.SolanaAccountPath[] { + return core.solanaGetAccountPaths(msg) + } + + public solanaNextAccountPath(_msg: core.SolanaAccountPath): core.SolanaAccountPath | undefined { + return undefined + } + + private getSolWallet(): SolWalletStandard | undefined { + if (!this.solWallet) { + this.solWallet = getMetaMaskSolWallet() + } + return this.solWallet + } + + private async connectSolWallet(): Promise { + if (this.solConnected) return this.solAddresses.length > 0 + if (this.solConnecting) return this.solConnecting + + this.solConnecting = (async () => { + const wallet = this.getSolWallet() + if (!wallet) { + this.solConnected = true + return false + } + + try { + const { accounts } = await wallet.features['standard:connect'].connect() + // Filter to only Solana accounts and cache all of them + const solanaAccounts = accounts.filter(a => a.chains.some(c => c.startsWith('solana:'))) + this.solAddresses = solanaAccounts.map(a => a.address) + this.solAccounts = solanaAccounts + this.solConnected = true + return this.solAddresses.length > 0 + } catch (e) { + console.error('[MM Native SOL] standard:connect FAILED:', e) + this.solConnected = true + return false + } finally { + this.solConnecting = null + } + })() + + return this.solConnecting + } + + public async solanaGetAddress(msg: core.SolanaGetAddress): Promise { + const connected = await this.connectSolWallet() + if (!connected) return null + + const accountIndex = getAccountIndex(msg.addressNList) + return this.solAddresses[accountIndex] ?? null + } + + public async solanaSignTx(msg: core.SolanaSignTx): Promise { + const wallet = this.getSolWallet() + if (!wallet) return null + + const connected = await this.connectSolWallet() + if (!connected) return null + + // Use account index from msg if available, default to account 0 + const accountIndex = msg.addressNList ? getAccountIndex(msg.addressNList) : 0 + const address = this.solAddresses[accountIndex] + if (!address) return null + + // Get the connected account for the sign request + const account = this.solAccounts.find(a => a.address === address) + if (!account) return null + + const transaction = core.solanaBuildTransaction(msg, address) + const serializedTx = transaction.serialize() + + const [{ signedTransaction }] = await wallet.features['solana:signTransaction'].signTransaction( + { + account, + transaction: serializedTx, + chain: 'solana:mainnet', + }, + ) + + // Deserialize the signed transaction to extract signatures + const decoded = VersionedTransaction.deserialize(signedTransaction) + + return { + serialized: Buffer.from(signedTransaction).toString('base64'), + signatures: decoded.signatures.map(sig => Buffer.from(sig).toString('base64')), + } + } + + // solanaSendTx intentionally not implemented - we rely on the chain adapter's + // sign + broadcast fallback which uses our own RPC for reliable broadcasting, + // rather than depending on MetaMask's signAndSendTransaction broadcast. + + public async getDeviceID(): Promise { + return this.providerRdns + ':' + (await this.ethGetAddress(this.provider)) + } + + public async getFirmwareVersion(): Promise { + return this.providerRdns + } +} diff --git a/packages/hdwallet-native-vault/package.json b/packages/hdwallet-native-vault/package.json index 9a700a88202..c3238acf41c 100644 --- a/packages/hdwallet-native-vault/package.json +++ b/packages/hdwallet-native-vault/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-native/package.json b/packages/hdwallet-native/package.json index 4468c180eb1..7fdea22bfe7 100644 --- a/packages/hdwallet-native/package.json +++ b/packages/hdwallet-native/package.json @@ -28,8 +28,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-phantom/package.json b/packages/hdwallet-phantom/package.json index 5c15d183693..2f55c4e9293 100644 --- a/packages/hdwallet-phantom/package.json +++ b/packages/hdwallet-phantom/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-sandbox/package.json b/packages/hdwallet-sandbox/package.json index 61aaea3006a..2977162ff41 100644 --- a/packages/hdwallet-sandbox/package.json +++ b/packages/hdwallet-sandbox/package.json @@ -8,7 +8,7 @@ "scripts": { "dev": "vite", "build": "vite build --outDir public", - "clean": "rm -rf node_modules public" + "clean": "rm -rf public node_modules" }, "dependencies": { "@esm2cjs/p-queue": "^7.3.0", diff --git a/packages/hdwallet-seeker/package.json b/packages/hdwallet-seeker/package.json index 20166bad061..d1fee56ad15 100644 --- a/packages/hdwallet-seeker/package.json +++ b/packages/hdwallet-seeker/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-trezor-connect/package.json b/packages/hdwallet-trezor-connect/package.json index 491160744cf..7a08ccbd57b 100644 --- a/packages/hdwallet-trezor-connect/package.json +++ b/packages/hdwallet-trezor-connect/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-trezor/package.json b/packages/hdwallet-trezor/package.json index ecc51a2dc4e..1aadf973873 100644 --- a/packages/hdwallet-trezor/package.json +++ b/packages/hdwallet-trezor/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-vultisig/package.json b/packages/hdwallet-vultisig/package.json index 528bbd603fc..b2d8f983fec 100644 --- a/packages/hdwallet-vultisig/package.json +++ b/packages/hdwallet-vultisig/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/hdwallet-walletconnectv2/package.json b/packages/hdwallet-walletconnectv2/package.json index 077e2dbcf4c..69162a780a2 100644 --- a/packages/hdwallet-walletconnectv2/package.json +++ b/packages/hdwallet-walletconnectv2/package.json @@ -20,8 +20,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/public-api/Dockerfile b/packages/public-api/Dockerfile index 3230f470514..bc5ca84dbbb 100644 --- a/packages/public-api/Dockerfile +++ b/packages/public-api/Dockerfile @@ -48,28 +48,20 @@ COPY packages/types/package.json ./packages/types/ COPY packages/unchained-client/package.json ./packages/unchained-client/ COPY packages/utils/package.json ./packages/utils/ -# Copy import method avoids hard-link failures from the pnpm store on Docker overlay FS +# pnpm uses copy instead of hard links, which Docker's overlay filesystem doesn't support ENV NPM_CONFIG_PACKAGE_IMPORT_METHOD=copy # Install dependencies, skipping lifecycle scripts (controlled by onlyBuiltDependencies in package.json) -# Retry loop: pnpm's hoisted linker intermittently fails with ENOENT on Docker overlay FS (known issue). RUN --mount=type=cache,id=s/ff1bcf56-d6d3-403a-b41e-2c92b7233f01-/root/.local/share/pnpm/store,target=/root/.local/share/pnpm/store \ - corepack enable && \ - for i in 1 2 3; do \ - pnpm install --frozen-lockfile --ignore-scripts && break || \ - rm -rf node_modules; \ - done - -# Run postinstall for openapi-generator-cli to download the JAR (skipped by --ignore-scripts above) -RUN --mount=type=cache,id=s/ff1bcf56-d6d3-403a-b41e-2c92b7233f01-/root/.openapi-generator-cli,target=/root/.openapi-generator-cli \ - pnpm rebuild @openapitools/openapi-generator-cli + corepack enable && pnpm install --frozen-lockfile --ignore-scripts # Copy unchained-client config and generator files FIRST (rarely changes, enables layer caching) COPY packages/unchained-client/openapitools.json ./packages/unchained-client/ COPY packages/unchained-client/generator/ ./packages/unchained-client/generator/ # Generate unchained-client code (required by tsc --build for workspace packages) -RUN pnpm --filter @shapeshiftoss/unchained-client generate +RUN --mount=type=cache,id=s/ff1bcf56-d6d3-403a-b41e-2c92b7233f01-/root/.openapitools,target=/root/.openapitools \ + pnpm --filter @shapeshiftoss/unchained-client generate # Copy remaining source files COPY packages/ ./packages/ diff --git a/packages/public-api/package.json b/packages/public-api/package.json index fc650a1f424..7519dffab22 100644 --- a/packages/public-api/package.json +++ b/packages/public-api/package.json @@ -7,10 +7,10 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build", + "build": "rm -rf dist && pnpm exec tsc --build", "build:bundle": "node esbuild.config.mjs", "build:smoke-tests": "node esbuild.smoke-tests.mjs", - "clean": "rm -rf dist", + "clean": "rm -rf dist node_modules", "dev": "tsx watch src/index.ts", "start": "node dist/index.js", "start:prod": "node dist/server.cjs", diff --git a/packages/public-api/src/config.ts b/packages/public-api/src/config.ts index a9fbdc19414..dcc8de328de 100644 --- a/packages/public-api/src/config.ts +++ b/packages/public-api/src/config.ts @@ -54,7 +54,19 @@ export const getServerConfig = (): SwapperConfig => ({ }) // Default affiliate fee in basis points -export const DEFAULT_AFFILIATE_BPS = '60' +export const DEFAULT_AFFILIATE_BPS = '10' + +// Swap service backend URL +const getSwapServiceBaseUrl = (): string => { + if (process.env.SWAP_SERVICE_BASE_URL) return process.env.SWAP_SERVICE_BASE_URL + if (process.env.NODE_ENV === 'production') { + throw new Error('SWAP_SERVICE_BASE_URL must be set in production') + } + console.warn('[config] SWAP_SERVICE_BASE_URL not set, using dev default') + return 'https://dev-api.swap-service.shapeshift.com' +} + +export const SWAP_SERVICE_BASE_URL = getSwapServiceBaseUrl() // API server config export const API_PORT = parseInt(process.env.PORT || '3001', 10) diff --git a/packages/public-api/src/docs/openapi.ts b/packages/public-api/src/docs/openapi.ts index 22aa369ac05..f7e2b5d20d2 100644 --- a/packages/public-api/src/docs/openapi.ts +++ b/packages/public-api/src/docs/openapi.ts @@ -4,9 +4,11 @@ import { OpenApiGeneratorV3, OpenAPIRegistry } from '@asteasolutions/zod-to-open import { z } from 'zod' import { RateLimitErrorCode } from '../middleware/rateLimit' +import { AffiliateStatsRequestSchema } from '../routes/affiliate' import { AssetRequestSchema, AssetsListRequestSchema } from '../routes/assets' import { QuoteRequestSchema } from '../routes/quote' import { RatesRequestSchema } from '../routes/rates' +import { StatusRequestSchema } from '../routes/status' export const registry = new OpenAPIRegistry() @@ -249,9 +251,10 @@ const rateLimitResponse = { registry.registerPath({ method: 'get', path: '/v1/chains', + operationId: 'listChains', summary: 'List supported chains', description: 'Get a list of all supported blockchain networks, sorted alphabetically by name.', - tags: ['Chains'], + tags: ['Supported Chains'], responses: { 200: { description: 'List of chains', @@ -271,9 +274,10 @@ registry.registerPath({ registry.registerPath({ method: 'get', path: '/v1/chains/count', + operationId: 'getChainCount', summary: 'Get chain count', description: 'Get the total number of supported blockchain networks.', - tags: ['Chains'], + tags: ['Supported Chains'], responses: { 200: { description: 'Chain count', @@ -294,9 +298,10 @@ registry.registerPath({ registry.registerPath({ method: 'get', path: '/v1/assets', + operationId: 'listAssets', summary: 'List supported assets', description: 'Get a list of all supported assets, optionally filtered by chain.', - tags: ['Assets'], + tags: ['Supported Assets'], request: { query: AssetsListRequestSchema, }, @@ -320,9 +325,10 @@ registry.registerPath({ registry.registerPath({ method: 'get', path: '/v1/assets/{assetId}', + operationId: 'getAssetById', summary: 'Get asset by ID', description: 'Get details of a specific asset by its ID (URL encoded).', - tags: ['Assets'], + tags: ['Supported Assets'], request: { params: AssetRequestSchema, }, @@ -342,6 +348,34 @@ registry.registerPath({ }, }) +// GET /v1/assets/count +registry.registerPath({ + method: 'get', + path: '/v1/assets/count', + operationId: 'getAssetCount', + summary: 'Get asset count', + description: 'Get the total number of supported assets, optionally filtered by chain.', + tags: ['Supported Assets'], + request: { + query: z.object({ + chainId: z.string().optional().openapi({ example: 'eip155:1' }), + }), + }, + responses: { + 200: { + description: 'Asset count', + content: { + 'application/json': { + schema: z.object({ + count: z.number().openapi({ example: 5000 }), + timestamp: z.number(), + }), + }, + }, + }, + }, +}) + const AffiliateAddressHeaderSchema = z .string() .optional() @@ -355,16 +389,33 @@ const AffiliateAddressHeaderSchema = z }, }) +const AffiliateBpsHeaderSchema = z + .string() + .optional() + .openapi({ + param: { + name: 'X-Affiliate-Bps', + in: 'header', + description: + 'Custom affiliate fee in basis points (0-1000). Defaults to 10 (0.1%). Can be used independently of X-Affiliate-Address.', + example: '10', + }, + }) + // GET /v1/swap/rates registry.registerPath({ method: 'get', path: '/v1/swap/rates', + operationId: 'getSwapRates', summary: 'Get swap rates', description: 'Get informative swap rates from all available swappers. This does not create a transaction.', tags: ['Swaps'], request: { - headers: z.object({ 'X-Affiliate-Address': AffiliateAddressHeaderSchema }), + headers: z.object({ + 'X-Affiliate-Address': AffiliateAddressHeaderSchema, + 'X-Affiliate-Bps': AffiliateBpsHeaderSchema, + }), query: RatesRequestSchema, }, responses: { @@ -387,12 +438,16 @@ registry.registerPath({ registry.registerPath({ method: 'post', path: '/v1/swap/quote', + operationId: 'getSwapQuote', summary: 'Get executable quote', description: 'Get an executable quote for a swap, including transaction data. Requires a specific swapper name.', tags: ['Swaps'], request: { - headers: z.object({ 'X-Affiliate-Address': AffiliateAddressHeaderSchema }), + headers: z.object({ + 'X-Affiliate-Address': AffiliateAddressHeaderSchema, + 'X-Affiliate-Bps': AffiliateBpsHeaderSchema, + }), body: { content: { 'application/json': { @@ -417,40 +472,526 @@ registry.registerPath({ }, }) +const SwapStatusResponseSchema = registry.register( + 'SwapStatusResponse', + z.object({ + quoteId: z.string().uuid(), + txHash: z.string().optional(), + status: z.enum(['pending', 'submitted', 'confirmed', 'failed']), + swapperName: z.string(), + sellAssetId: z.string(), + buyAssetId: z.string(), + sellAmountCryptoBaseUnit: z.string(), + buyAmountAfterFeesCryptoBaseUnit: z.string(), + affiliateAddress: z.string().optional(), + affiliateBps: z.string(), + registeredAt: z.number().optional(), + buyTxHash: z.string().optional(), + isAffiliateVerified: z.boolean().optional(), + }), +) + +registry.registerPath({ + method: 'get', + path: '/v1/swap/status', + operationId: 'getSwapStatus', + summary: 'Get swap status', + description: + 'Look up the current status of a swap by its quote ID. Pass txHash on the first call after broadcasting to bind it to the quote and start tracking. Subsequent calls can omit txHash.', + tags: ['Swaps'], + request: { + headers: z.object({ 'X-Affiliate-Address': AffiliateAddressHeaderSchema }), + query: StatusRequestSchema, + }, + responses: { + 200: { + description: 'Swap status', + content: { + 'application/json': { + schema: SwapStatusResponseSchema, + }, + }, + }, + 400: { + description: 'Invalid request parameters', + }, + 404: { + description: 'Quote not found or expired', + }, + 409: { + description: 'Transaction hash mismatch', + }, + }, +}) + +const AffiliateStatsResponseSchema = registry.register( + 'AffiliateStatsResponse', + z.object({ + address: z.string().openapi({ example: '0x1234567890123456789012345678901234567890' }), + totalSwaps: z.number().openapi({ example: 42 }), + totalVolumeUsd: z.string().openapi({ example: '12345.67' }), + totalFeesEarnedUsd: z.string().openapi({ example: '44.44' }), + timestamp: z.number().openapi({ example: 1708700000000 }), + }), +) + +registry.registerPath({ + method: 'get', + path: '/v1/affiliate/stats', + operationId: 'getAffiliateStats', + summary: 'Get affiliate statistics', + description: + 'Retrieve aggregated swap statistics for an affiliate address. Returns total swaps, volume, and fees earned. Supports optional date range filtering.', + tags: ['Affiliate'], + request: { + query: AffiliateStatsRequestSchema, + }, + responses: { + 200: { + description: 'Affiliate statistics', + content: { + 'application/json': { + schema: AffiliateStatsResponseSchema, + }, + }, + }, + 400: { + description: 'Invalid address format', + }, + 503: { + description: 'Swap service unavailable', + }, + }, +}) + export const generateOpenApiDocument = () => { const generator = new OpenApiGeneratorV3(registry.definitions) - return generator.generateDocument({ + const doc = generator.generateDocument({ openapi: '3.0.0', info: { version: '1.0.0', title: 'ShapeShift Public API', description: `The ShapeShift Public API enables developers to integrate multi-chain swap functionality into their applications. Access rates from multiple DEX aggregators and execute swaps across supported blockchains. -## Integration Overview +There are two ways to integrate: + +1. **Swap Widget SDK** — Drop-in React component with built-in UI, wallet connection, and multi-chain support. Fastest way to integrate. +2. **REST API** — Build your own swap UI using the endpoints below. Full control over UX. + +## Affiliate Tracking (Optional) +Include your Arbitrum address in the \`X-Affiliate-Address\` header to attribute swaps for affiliate fee tracking. This is optional — all endpoints work without it. + +## Asset IDs +Assets use CAIP-19 format: \`{chainId}/{assetNamespace}:{assetReference}\` +- Native ETH: \`eip155:1/slip44:60\` +- USDC on Ethereum: \`eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\` +- Native BTC: \`bip122:000000000019d6689c085ae165831e93/slip44:0\` +`, + }, + servers: [{ url: 'https://api.shapeshift.com' }, { url: 'http://localhost:3001' }], + }) + + const widgetSdkTag = { + name: 'Swap Widget SDK', + description: `The \`@shapeshiftoss/swap-widget\` package is a drop-in React component that provides a complete swap interface. It handles asset selection, rate comparison, wallet connection, transaction signing, and status tracking. + +## Installation + +\`\`\`bash +npm install @shapeshiftoss/swap-widget +# or +yarn add @shapeshiftoss/swap-widget +\`\`\` + +**Peer dependencies** (install alongside the widget): + +\`\`\`bash +npm install react react-dom +\`\`\` + +**CSS** — You must import the widget stylesheet: -### 1. Get Supported Chains -First, fetch the list of supported blockchain networks: +\`\`\`tsx +import '@shapeshiftoss/swap-widget/style.css' +\`\`\` + +## Quick Start + +\`\`\`tsx +import { SwapWidget } from '@shapeshiftoss/swap-widget' +import '@shapeshiftoss/swap-widget/style.css' + +function App() { + return ( + console.log('Success:', txHash)} + /> + ) +} +\`\`\` + +--- + +## Wallet Connection Modes + +The widget supports two wallet connection strategies. Choose the one that matches your application. + +### Mode 1: External Wallet (Recommended for dApps) + +**Use this if your application already has a wallet connection** (wagmi, ethers, viem, RainbowKit, ConnectKit, AppKit, etc.). Pass the connected wallet to the widget — no duplicate wallet modals. + +\`\`\`tsx +import { SwapWidget } from '@shapeshiftoss/swap-widget' +import '@shapeshiftoss/swap-widget/style.css' +import { useWalletClient } from 'wagmi' + +function SwapPage() { + const { data: walletClient } = useWalletClient() + + return ( + { + // Trigger YOUR app's wallet connection modal + openYourConnectModal() + }} + theme="dark" + /> + ) +} +\`\`\` + +| Prop | Purpose | +|------|---------| +| \`walletClient\` | A viem \`WalletClient\` from your existing wallet setup | +| \`onConnectWallet\` | Called when the user clicks "Connect" inside the widget — open your own modal | +| \`enableWalletConnection\` | Leave as \`false\` (default) — the widget won't render its own connect UI | + +This mode creates its own read-only wagmi config internally for balance fetching. It does **not** interfere with your application's wagmi provider or AppKit instance. + +### Mode 2: Built-in Wallet Connection (Standalone) + +**Use this if your page has no wallet infrastructure.** The widget manages wallet connections internally via Reown AppKit, supporting EVM, Bitcoin, and Solana wallets. + +\`\`\`tsx +import { SwapWidget } from '@shapeshiftoss/swap-widget' +import '@shapeshiftoss/swap-widget/style.css' + +function App() { + return ( + + ) +} +\`\`\` + +Get a WalletConnect project ID at [cloud.walletconnect.com](https://cloud.walletconnect.com). + +When \`enableWalletConnection\` is true, the widget: +- Shows a "Connect" button that opens a multi-chain wallet modal +- Supports MetaMask, WalletConnect, Coinbase Wallet, and other EVM wallets +- Supports Bitcoin wallets via WalletConnect +- Supports Phantom, Solflare, and other Solana wallets + +> **Important: AppKit Singleton Constraint** +> +> The built-in wallet connection uses Reown AppKit, which is a **global singleton** — only one AppKit instance can exist per page. If your page already uses AppKit or Web3Modal, the widget's modal will conflict with yours. +> +> **If your dApp already has AppKit/Web3Modal**: Use **Mode 1 (External Wallet)** instead. Pass your connected \`walletClient\` to the widget and handle wallet connection yourself. +> +> **If your page has no wallet setup**: Mode 2 works perfectly — the widget is the only AppKit instance on the page. + +--- + +## Props Reference + +### Core Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| \`affiliateAddress\` | \`string\` | — | Your Arbitrum address for affiliate fee attribution | +| \`affiliateBps\` | \`string\` | \`"10"\` | Affiliate fee in basis points (0.1% default) | +| \`apiBaseUrl\` | \`string\` | — | Custom API base URL | +| \`theme\` | \`ThemeMode \\| ThemeConfig\` | \`"dark"\` | Theme mode or full theme configuration | +| \`showPoweredBy\` | \`boolean\` | \`true\` | Show "Powered by ShapeShift" branding | +| \`defaultSlippage\` | \`string\` | \`"0.5"\` | Default slippage tolerance percentage | +| \`ratesRefetchInterval\` | \`number\` | \`15000\` | Rate refresh interval in milliseconds | + +### Wallet Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| \`walletClient\` | \`WalletClient\` | — | Viem wallet client for EVM transactions (Mode 1) | +| \`enableWalletConnection\` | \`boolean\` | \`false\` | Enable built-in wallet modal (Mode 2) | +| \`walletConnectProjectId\` | \`string\` | — | Required for Mode 2 | +| \`onConnectWallet\` | \`() => void\` | — | Callback when user clicks "Connect" (Mode 1) | +| \`defaultReceiveAddress\` | \`string\` | — | Lock the receive address to a specific value | + +### Asset Filtering Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| \`defaultSellAsset\` | \`Asset\` | ETH | Initial sell asset | +| \`defaultBuyAsset\` | \`Asset\` | USDC | Initial buy asset | +| \`allowedChainIds\` | \`ChainId[]\` | all | Restrict both sides to these chains | +| \`disabledChainIds\` | \`ChainId[]\` | \`[]\` | Hide chains from both selectors | +| \`disabledAssetIds\` | \`AssetId[]\` | \`[]\` | Hide assets from both selectors | +| \`sellAllowedChainIds\` | \`ChainId[]\` | — | Restrict sell side to these chains | +| \`buyAllowedChainIds\` | \`ChainId[]\` | — | Restrict buy side to these chains | +| \`sellAllowedAssetIds\` | \`AssetId[]\` | — | Restrict sell side to these assets | +| \`buyAllowedAssetIds\` | \`AssetId[]\` | — | Restrict buy side to these assets | +| \`sellDisabledChainIds\` | \`ChainId[]\` | \`[]\` | Hide chains from sell selector | +| \`buyDisabledChainIds\` | \`ChainId[]\` | \`[]\` | Hide chains from buy selector | +| \`sellDisabledAssetIds\` | \`AssetId[]\` | \`[]\` | Hide assets from sell selector | +| \`buyDisabledAssetIds\` | \`AssetId[]\` | \`[]\` | Hide assets from buy selector | +| \`allowedSwapperNames\` | \`SwapperName[]\` | all | Restrict to specific swappers | +| \`isBuyAssetLocked\` | \`boolean\` | \`false\` | Prevent changing the buy asset | + +### Callback Props + +| Prop | Type | Description | +|------|------|-------------| +| \`onSwapSuccess\` | \`(txHash: string) => void\` | Called when a swap succeeds | +| \`onSwapError\` | \`(error: Error) => void\` | Called when a swap fails | +| \`onAssetSelect\` | \`(type: 'sell' \\| 'buy', asset: Asset) => void\` | Called when user selects an asset | + +--- + +## Theming + +### Simple Mode + +\`\`\`tsx + + +\`\`\` + +### Custom Theme + +\`\`\`tsx +const theme: ThemeConfig = { + mode: 'dark', + accentColor: '#3861fb', + backgroundColor: '#0a0a14', + cardColor: '#12121c', + textColor: '#ffffff', + borderRadius: '12px', + fontFamily: 'Inter, sans-serif', + borderColor: '#2a2a3e', + secondaryTextColor: '#a0a0b0', + mutedTextColor: '#6b6b80', + inputColor: '#1a1a2e', + hoverColor: '#1e1e32', + buttonVariant: 'filled', // 'filled' or 'outline' +} + + +\`\`\` + +| Property | Type | Description | +|----------|------|-------------| +| \`mode\` | \`'light' \\| 'dark'\` | Base theme mode (required) | +| \`accentColor\` | \`string\` | Buttons, focus states, active elements | +| \`backgroundColor\` | \`string\` | Widget background | +| \`cardColor\` | \`string\` | Card and panel backgrounds | +| \`textColor\` | \`string\` | Primary text | +| \`borderRadius\` | \`string\` | Border radius (e.g. \`'12px'\`) | +| \`fontFamily\` | \`string\` | Font family | +| \`borderColor\` | \`string\` | Border colors | +| \`secondaryTextColor\` | \`string\` | Secondary labels | +| \`mutedTextColor\` | \`string\` | Muted/disabled text | +| \`inputColor\` | \`string\` | Input field background | +| \`hoverColor\` | \`string\` | Hover state background | +| \`buttonVariant\` | \`'filled' \\| 'outline'\` | Button style | + +--- + +## Integration Examples + +### Restrict to Ethereum + Polygon Only + +\`\`\`tsx +import { SwapWidget, EVM_CHAIN_IDS } from '@shapeshiftoss/swap-widget' + + +\`\`\` + +### Lock Buy Asset (Payment Widget) + +\`\`\`tsx +const usdcAsset = { + assetId: 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + chainId: 'eip155:1', + symbol: 'USDC', + name: 'USD Coin', + precision: 6, +} + + +\`\`\` + +### Use Specific Swappers Only + +\`\`\`tsx +import { SwapWidget, SwapperName } from '@shapeshiftoss/swap-widget' + + +\`\`\` + +--- + +## Exported Hooks + +These hooks can be used outside the widget to build custom UI with ShapeShift asset data. + +\`\`\`tsx +import { + useAssets, + useAssetById, + useChains, + useAssetsByChainId, + useAssetSearch, +} from '@shapeshiftoss/swap-widget' +\`\`\` + +| Hook | Return Type | Description | +|------|-------------|-------------| +| \`useAssets()\` | \`{ data: Asset[], isLoading, ... }\` | All available assets | +| \`useAssetById(assetId)\` | \`{ data: Asset \\| undefined, ... }\` | Single asset by CAIP-19 ID | +| \`useChains()\` | \`{ data: ChainInfo[], ... }\` | All chains with native assets | +| \`useAssetsByChainId(chainId)\` | \`{ data: Asset[], ... }\` | All assets on a chain | +| \`useAssetSearch(query, chainId?)\` | \`{ data: Asset[], ... }\` | Search by symbol or name | + +All hooks return React Query result objects with \`data\`, \`isLoading\`, \`error\`, \`refetch\`, etc. + +## Exported Utilities + +\`\`\`tsx +import { + formatAmount, + parseAmount, + truncateAddress, + isEvmChainId, + getEvmNetworkId, + getChainType, + getChainName, + getChainIcon, + getChainColor, + getBaseAsset, + getExplorerTxLink, + EVM_CHAIN_IDS, + UTXO_CHAIN_IDS, + COSMOS_CHAIN_IDS, + OTHER_CHAIN_IDS, +} from '@shapeshiftoss/swap-widget' +\`\`\` + +--- + +## Supported Chains + +The widget natively supports all EVM chains, Bitcoin, and Solana. Other chains (Cosmos, Starknet, NEAR, TON, Tron, Sui, etc.) are available via redirect to [app.shapeshift.com](https://app.shapeshift.com). + +| Chain | Chain ID | Type | +|-------|----------|------| +| Ethereum | \`eip155:1\` | EVM | +| Arbitrum One | \`eip155:42161\` | EVM | +| Avalanche C-Chain | \`eip155:43114\` | EVM | +| Base | \`eip155:8453\` | EVM | +| Berachain | \`eip155:80094\` | EVM | +| Blast | \`eip155:81457\` | EVM | +| BNB Smart Chain | \`eip155:56\` | EVM | +| BOB | \`eip155:60808\` | EVM | +| Cronos | \`eip155:25\` | EVM | +| Flow EVM | \`eip155:747\` | EVM | +| Gnosis | \`eip155:100\` | EVM | +| Hemi | \`eip155:43111\` | EVM | +| HyperEVM | \`eip155:999\` | EVM | +| Ink | \`eip155:57073\` | EVM | +| Katana | \`eip155:747474\` | EVM | +| Linea | \`eip155:59144\` | EVM | +| Mantle | \`eip155:5000\` | EVM | +| MegaETH | \`eip155:4326\` | EVM | +| Mode | \`eip155:34443\` | EVM | +| Monad | \`eip155:143\` | EVM | +| Optimism | \`eip155:10\` | EVM | +| Plasma | \`eip155:9745\` | EVM | +| Plume | \`eip155:98866\` | EVM | +| Polygon | \`eip155:137\` | EVM | +| Scroll | \`eip155:534352\` | EVM | +| Soneium | \`eip155:1868\` | EVM | +| Sonic | \`eip155:146\` | EVM | +| Story | \`eip155:1514\` | EVM | +| Unichain | \`eip155:130\` | EVM | +| World Chain | \`eip155:480\` | EVM | +| zkSync Era | \`eip155:324\` | EVM | +| Bitcoin | \`bip122:000000000019d6689c085ae165831e93\` | UTXO | +| Solana | \`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\` | Solana | + +## Supported Swappers (15) + +THORChain, MAYAChain, CoW Swap, 0x, Portals, Chainflip, Jupiter, Relay, ButterSwap, Bebop, Arbitrum Bridge, NEAR Intents, Cetus, Sun.io, AVNU. + +--- + +## Architecture Notes + +**Internal QueryClient** — The widget manages its own React Query \`QueryClient\`. You do not need to wrap it in a \`QueryClientProvider\`. + +**Wagmi Isolation** — In external wallet mode, the widget creates its own isolated read-only wagmi config for balance fetching. It does not interfere with your application's \`WagmiProvider\`. + +**AppKit Singleton** — In built-in wallet mode (\`enableWalletConnection=true\`), the widget uses Reown AppKit which is a page-level singleton. Only one AppKit instance can exist per page. If your dApp already uses AppKit or Web3Modal, you **must** use external wallet mode instead. + +**CSS Isolation** — All widget styles are prefixed with \`ssw-\` to avoid conflicts with host page styles. Import the stylesheet explicitly: + +\`\`\`tsx +import '@shapeshiftoss/swap-widget/style.css' +\`\`\` +`, + } + + const restApiTag = { + name: 'REST API Guide', + description: `Step-by-step guide for integrating swaps via the REST API. + +## 1. Get Supported Chains \`\`\` GET /v1/chains \`\`\` -### 2. Get Supported Assets -Fetch the list of supported assets to populate your UI: +## 2. Get Supported Assets \`\`\` GET /v1/assets \`\`\` -### 3. Get Swap Rates -When a user wants to swap, fetch rates from all available swappers to find the best deal: +## 3. Get Swap Rates \`\`\` GET /v1/swap/rates?sellAssetId=eip155:1/slip44:60&buyAssetId=eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&sellAmountCryptoBaseUnit=1000000000000000000 X-Affiliate-Address: 0xYourArbitrumAddress (optional) \`\`\` -This returns rates from THORChain, 0x, CoW Swap, and other supported swappers. -### 4. Get Executable Quote -Once the user selects a rate, request an executable quote with transaction data: +## 4. Get Executable Quote \`\`\` POST /v1/swap/quote X-Affiliate-Address: 0xYourArbitrumAddress (optional) @@ -465,19 +1006,19 @@ X-Affiliate-Address: 0xYourArbitrumAddress (optional) } \`\`\` -### 5. Execute the Swap +## 5. Execute the Swap Use the returned \`transactionData\` to build and sign a transaction with the user's wallet, then broadcast it to the network. -## Affiliate Tracking (Optional) -To attribute swaps to your project, include your Arbitrum address in the \`X-Affiliate-Address\` header. This is optional — all endpoints work without it. +## 6. Check Swap Status +\`\`\` +GET /v1/swap/status?quoteId=&txHash=0x... +\`\`\` -## Asset IDs -Assets use CAIP-19 format: \`{chainId}/{assetNamespace}:{assetReference}\` -- Native ETH: \`eip155:1/slip44:60\` -- USDC on Ethereum: \`eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\` -- Native BTC: \`bip122:000000000019d6689c085ae165831e93/slip44:0\` +On the **first call**, include \`txHash\` to bind the transaction to the quote and start tracking. Subsequent polls can omit \`txHash\`. `, - }, - servers: [{ url: 'https://api.shapeshift.com' }, { url: 'http://localhost:3001' }], - }) + } + + doc.tags = [widgetSdkTag, restApiTag, ...(doc.tags ?? [])] + + return doc } diff --git a/packages/public-api/src/index.ts b/packages/public-api/src/index.ts index 7735b063a84..90f86640865 100644 --- a/packages/public-api/src/index.ts +++ b/packages/public-api/src/index.ts @@ -5,18 +5,23 @@ import express from 'express' import { initAssets } from './assets' import { API_HOST, API_PORT } from './config' +import { quoteStore } from './lib/quoteStore' import { affiliateAddress } from './middleware/auth' import { + affiliateStatsLimiter, dataLimiter, globalLimiter, swapQuoteLimiter, swapRatesLimiter, + swapStatusLimiter, } from './middleware/rateLimit' +import { getAffiliateStats } from './routes/affiliate' import { getAssetById, getAssetCount, getAssets } from './routes/assets' import { getChainCount, getChains } from './routes/chains' import { docsRouter } from './routes/docs' import { getQuote } from './routes/quote' import { getRates } from './routes/rates' +import { getSwapStatus } from './routes/status' const app = express() @@ -43,6 +48,8 @@ app.get('/', (_req, res) => { assetById: 'GET /v1/assets/:assetId', swapRates: 'GET /v1/swap/rates', swapQuote: 'POST /v1/swap/quote', + swapStatus: 'GET /v1/swap/status', + affiliateStats: 'GET /v1/affiliate/stats', }, }) }) @@ -58,6 +65,10 @@ const v1Router = express.Router() // Swap endpoints (optional affiliate address tracking) v1Router.get('/swap/rates', swapRatesLimiter, affiliateAddress, getRates) v1Router.post('/swap/quote', swapQuoteLimiter, affiliateAddress, getQuote) +v1Router.get('/swap/status', swapStatusLimiter, affiliateAddress, getSwapStatus) + +// Affiliate endpoints +v1Router.get('/affiliate/stats', affiliateStatsLimiter, getAffiliateStats) // Chain endpoints v1Router.get('/chains', dataLimiter, getChains) @@ -96,6 +107,8 @@ Available endpoints: GET /v1/chains/count - Get chain count GET /v1/swap/rates - Get swap rates from all swappers POST /v1/swap/quote - Get executable quote with tx data + GET /v1/swap/status - Get swap status by quoteId + GET /v1/affiliate/stats - Get affiliate stats by address GET /v1/assets - List supported assets GET /v1/assets/count - Get asset count GET /v1/assets/:assetId - Get single asset by ID @@ -107,4 +120,13 @@ Affiliate Tracking (optional): }) } +const shutdown = () => { + console.log('Shutting down gracefully...') + quoteStore.destroy() + process.exit(0) +} + +process.on('SIGTERM', shutdown) +process.on('SIGINT', shutdown) + startServer().catch(console.error) diff --git a/packages/public-api/src/lib/quoteStore.ts b/packages/public-api/src/lib/quoteStore.ts new file mode 100644 index 00000000000..caae711f67f --- /dev/null +++ b/packages/public-api/src/lib/quoteStore.ts @@ -0,0 +1,145 @@ +import type { ChainId } from '@shapeshiftoss/caip' + +export type StoredQuote = { + quoteId: string + swapperName: string + sellAssetId: string + buyAssetId: string + sellAmountCryptoBaseUnit: string + buyAmountAfterFeesCryptoBaseUnit: string + affiliateAddress: string | undefined + affiliateBps: string + sellChainId: ChainId + receiveAddress: string + sendAddress: string | undefined + rate: string + createdAt: number + expiresAt: number + metadata: { + chainflipSwapId?: number + nearIntentsDepositAddress?: string + nearIntentsDepositMemo?: string + relayId?: string + cowswapOrderUid?: string + acrossDepositId?: string + } + stepChainIds: ChainId[] + txHash?: string + registeredAt?: number + status: 'pending' | 'submitted' | 'confirmed' | 'failed' +} + +/** + * In-memory quote store with dual TTL: + * - 15 minutes for unsubmitted quotes (quote validity window) + * - 60 minutes after txHash is bound (execution tracking window) + * + * Automatic sweep of expired entries every 60 seconds. + * Migration path: swap to Redis with zero code changes (same get/set/delete interface). + */ +export class QuoteStore { + private store = new Map() + private txHashIndex = new Map() + private cleanupInterval: ReturnType + + static readonly QUOTE_TTL_MS = 15 * 60 * 1000 + static readonly EXECUTION_TTL_MS = 60 * 60 * 1000 + static readonly CLEANUP_INTERVAL_MS = 60 * 1000 + static readonly MAX_QUOTES = 10000 + + constructor() { + this.cleanupInterval = setInterval(() => this.sweep(), QuoteStore.CLEANUP_INTERVAL_MS) + } + + set(quoteId: string, quote: StoredQuote): void { + if (!this.store.has(quoteId) && this.store.size >= QuoteStore.MAX_QUOTES) { + this.evictOldest() + } + this.store.set(quoteId, quote) + if (quote.txHash) { + this.txHashIndex.set(quote.txHash, quoteId) + } + } + + get(quoteId: string): StoredQuote | undefined { + const quote = this.store.get(quoteId) + if (!quote) return undefined + + const now = Date.now() + const effectiveExpiry = quote.txHash + ? (quote.registeredAt ?? quote.createdAt) + QuoteStore.EXECUTION_TTL_MS + : quote.expiresAt + + if (now > effectiveExpiry) { + this.remove(quoteId, quote) + return undefined + } + + return quote + } + + hasTxHash(txHash: string): boolean { + const quoteId = this.txHashIndex.get(txHash) + if (!quoteId) return false + return this.get(quoteId) !== undefined + } + + getQuoteIdByTxHash(txHash: string): string | undefined { + return this.txHashIndex.get(txHash) + } + + size(): number { + return this.store.size + } + + private evictOldest(): void { + let oldestId: string | undefined + let oldestTime = Infinity + for (const [id, quote] of this.store) { + if (quote.createdAt < oldestTime) { + oldestTime = quote.createdAt + oldestId = id + } + } + if (oldestId) { + const quote = this.store.get(oldestId) + if (quote) { + console.log(`[QuoteStore] Evicting oldest quote ${oldestId} to enforce max size cap`) + this.remove(oldestId, quote) + } + } + } + + private remove(quoteId: string, quote: StoredQuote): void { + if (quote.txHash) { + this.txHashIndex.delete(quote.txHash) + } + this.store.delete(quoteId) + } + + private sweep(): void { + const now = Date.now() + let swept = 0 + for (const [id, quote] of this.store) { + const effectiveExpiry = quote.txHash + ? (quote.registeredAt ?? quote.createdAt) + QuoteStore.EXECUTION_TTL_MS + : quote.expiresAt + + if (now > effectiveExpiry) { + this.remove(id, quote) + swept++ + } + } + if (swept > 0) { + console.log(`[QuoteStore] Swept ${swept} expired quotes. ${this.store.size} remaining.`) + } + } + + destroy(): void { + clearInterval(this.cleanupInterval) + this.store.clear() + this.txHashIndex.clear() + } +} + +export const quoteStore = new QuoteStore() diff --git a/packages/public-api/src/middleware/auth.ts b/packages/public-api/src/middleware/auth.ts index 2d2e50a9808..3981ed5f408 100644 --- a/packages/public-api/src/middleware/auth.ts +++ b/packages/public-api/src/middleware/auth.ts @@ -3,18 +3,13 @@ import type { NextFunction, Request, Response } from 'express' import type { ErrorResponse } from '../types' const EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/ +const BPS_REGEX = /^\d+$/ -// Affiliate address middleware - attaches affiliate info if a valid address is provided -// The API works without an affiliate address (anonymous access) export const affiliateAddress = (req: Request, res: Response, next: NextFunction): void => { const address = req.header('X-Affiliate-Address') + const bps = req.header('X-Affiliate-Bps') - if (!address) { - next() - return - } - - if (!EVM_ADDRESS_REGEX.test(address)) { + if (address && !EVM_ADDRESS_REGEX.test(address)) { const errorResponse: ErrorResponse = { error: 'Invalid affiliate address format. Must be a valid EVM address (0x followed by 40 hex characters).', @@ -24,7 +19,21 @@ export const affiliateAddress = (req: Request, res: Response, next: NextFunction return } - req.affiliateInfo = { affiliateAddress: address } + if (bps !== undefined && (!BPS_REGEX.test(bps) || parseInt(bps, 10) > 1000)) { + const errorResponse: ErrorResponse = { + error: 'Invalid affiliate BPS. Must be an integer between 0 and 1000.', + code: 'INVALID_AFFILIATE_BPS', + } + res.status(400).json(errorResponse) + return + } + + if (address || bps) { + req.affiliateInfo = { + ...(address && { affiliateAddress: address }), + ...(bps && { affiliateBps: bps }), + } + } next() } diff --git a/packages/public-api/src/middleware/rateLimit.ts b/packages/public-api/src/middleware/rateLimit.ts index 55fd88ca761..6b240552a32 100644 --- a/packages/public-api/src/middleware/rateLimit.ts +++ b/packages/public-api/src/middleware/rateLimit.ts @@ -34,3 +34,5 @@ export const globalLimiter = createLimiter('RATE_LIMIT_GLOBAL_MAX', 300) export const dataLimiter = createLimiter('RATE_LIMIT_DATA_MAX', 120) export const swapRatesLimiter = createLimiter('RATE_LIMIT_SWAP_RATES_MAX', 60) export const swapQuoteLimiter = createLimiter('RATE_LIMIT_SWAP_QUOTE_MAX', 45) +export const swapStatusLimiter = createLimiter('RATE_LIMIT_SWAP_STATUS_MAX', 60) +export const affiliateStatsLimiter = createLimiter('RATE_LIMIT_AFFILIATE_STATS_MAX', 30) diff --git a/packages/public-api/src/routes/affiliate.ts b/packages/public-api/src/routes/affiliate.ts new file mode 100644 index 00000000000..d0d44e475d9 --- /dev/null +++ b/packages/public-api/src/routes/affiliate.ts @@ -0,0 +1,154 @@ +import type { Request, Response } from 'express' +import { z } from 'zod' + +import { SWAP_SERVICE_BASE_URL } from '../config' +import type { ErrorResponse } from '../types' + +const AFFILIATE_TIMEOUT_MS = 10_000 + +// Request validation schema +export const AffiliateStatsRequestSchema = z + .object({ + address: z + .string() + .regex( + /^0x[0-9a-fA-F]{40}$/, + 'address must be a valid EVM address (0x followed by 40 hex characters)', + ), + startDate: z.string().datetime().optional(), + endDate: z.string().datetime().optional(), + }) + .refine( + ({ startDate, endDate }) => + !startDate || !endDate || new Date(startDate).getTime() <= new Date(endDate).getTime(), + { + message: 'startDate must be before or equal to endDate', + path: ['startDate'], + }, + ) + +export type AffiliateStatsRequest = z.infer + +export type AffiliateStatsResponse = { + address: string + totalSwaps: number + totalVolumeUsd: string + totalFeesEarnedUsd: string + timestamp: number +} + +// Backend response type from swap-service +type BackendAffiliateStats = { + affiliateAddress: string + swapCount: number + totalSwapVolumeUsd: string + totalFeesCollectedUsd: string + referrerCommissionUsd: string +} + +export const getAffiliateStats = async (req: Request, res: Response): Promise => { + try { + // Parse and validate request + const parseResult = AffiliateStatsRequestSchema.safeParse(req.query) + if (!parseResult.success) { + const errorResponse: ErrorResponse = { + error: 'Invalid request parameters', + code: 'INVALID_REQUEST', + details: parseResult.error.errors, + } + res.status(400).json(errorResponse) + return + } + + const { address, startDate, endDate } = parseResult.data + + // Build backend URL with query params + const backendUrl = new URL(`/swaps/affiliate-fees/${address}`, SWAP_SERVICE_BASE_URL) + if (startDate) { + backendUrl.searchParams.append('startDate', String(startDate)) + } + if (endDate) { + backendUrl.searchParams.append('endDate', String(endDate)) + } + + // Call backend swap-service + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), AFFILIATE_TIMEOUT_MS) + let backendResponse: globalThis.Response + try { + backendResponse = await fetch(backendUrl.toString(), { + signal: controller.signal, + }) + } catch (error) { + clearTimeout(timeout) + if (error instanceof DOMException && error.name === 'AbortError') { + res.status(504).json({ + error: 'Swap service request timed out', + code: 'SERVICE_TIMEOUT', + } as ErrorResponse) + return + } + console.error('Failed to connect to swap-service:', error) + res.status(503).json({ + error: 'Swap service unavailable', + code: 'SERVICE_UNAVAILABLE', + } as ErrorResponse) + return + } finally { + clearTimeout(timeout) + } + + // Handle backend errors + if (!backendResponse.ok) { + if (backendResponse.status === 404) { + // Non-existent affiliate - return 200 with zero values + const response: AffiliateStatsResponse = { + address, + totalSwaps: 0, + totalVolumeUsd: '0.00', + totalFeesEarnedUsd: '0.00', + timestamp: Date.now(), + } + res.status(200).json(response) + return + } + + console.error(`Backend returned ${backendResponse.status}:`, await backendResponse.text()) + res.status(503).json({ + error: 'Swap service error', + code: 'SERVICE_ERROR', + } as ErrorResponse) + return + } + + // Parse backend response + let backendData: BackendAffiliateStats + try { + backendData = (await backendResponse.json()) as BackendAffiliateStats + } catch (error) { + console.error('Failed to parse backend response:', error) + res.status(503).json({ + error: 'Invalid response from swap service', + code: 'INVALID_RESPONSE', + } as ErrorResponse) + return + } + + // Transform backend response to public API format + const response: AffiliateStatsResponse = { + address: String(backendData.affiliateAddress), + totalSwaps: backendData.swapCount, + totalVolumeUsd: backendData.totalSwapVolumeUsd, + totalFeesEarnedUsd: backendData.referrerCommissionUsd, + timestamp: Date.now(), + } + + res.status(200).json(response) + } catch (error) { + console.error('Unexpected error in getAffiliateStats:', error) + res.status(500).json({ + error: 'Internal server error', + code: 'INTERNAL_ERROR', + } as ErrorResponse) + } +} diff --git a/packages/public-api/src/routes/docs.ts b/packages/public-api/src/routes/docs.ts index 65dbcf56bf1..6175ff99008 100644 --- a/packages/public-api/src/routes/docs.ts +++ b/packages/public-api/src/routes/docs.ts @@ -5,15 +5,18 @@ import { generateOpenApiDocument } from '../docs/openapi' const router = express.Router() -// Generate Spec const openApiDocument = generateOpenApiDocument() -// Serve raw JSON spec +const FAVICON_SVG = `S` + +router.get('/favicon.ico', (_req, res) => { + res.type('image/svg+xml').send(FAVICON_SVG) +}) + router.get('/json', (_req, res) => { res.json(openApiDocument) }) -// Serve Scalar UI router.use( '/', apiReference({ diff --git a/packages/public-api/src/routes/quote.ts b/packages/public-api/src/routes/quote.ts index f63af0b3a89..abfd7c0e5fa 100644 --- a/packages/public-api/src/routes/quote.ts +++ b/packages/public-api/src/routes/quote.ts @@ -12,6 +12,7 @@ import { z } from 'zod' import { getAsset, getAssetsById } from '../assets' import { DEFAULT_AFFILIATE_BPS, getServerConfig } from '../config' +import { QuoteStore, quoteStore } from '../lib/quoteStore' import { booleanFromString } from '../lib/zod' import { createServerSwapperDeps } from '../swapperDeps' import type { @@ -423,7 +424,7 @@ export const getQuote = async (req: Request, res: Response): Promise => { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmountCryptoBaseUnit, - affiliateBps: DEFAULT_AFFILIATE_BPS, + affiliateBps: req.affiliateInfo?.affiliateBps ?? DEFAULT_AFFILIATE_BPS, allowMultiHop, slippageTolerancePercentageDecimal: slippage, receiveAddress, @@ -475,6 +476,31 @@ export const getQuote = async (req: Request, res: Response): Promise => { const quoteId = uuidv4() const now = Date.now() + quoteStore.set(quoteId, { + quoteId, + swapperName: validSwapperName, + sellAssetId: sellAsset.assetId, + buyAssetId: buyAsset.assetId, + sellAmountCryptoBaseUnit: firstStep.sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountAfterFeesCryptoBaseUnit: lastStep.buyAmountAfterFeesCryptoBaseUnit, + affiliateAddress: req.affiliateInfo?.affiliateAddress, + affiliateBps: req.affiliateInfo?.affiliateBps ?? DEFAULT_AFFILIATE_BPS, + sellChainId: sellAsset.chainId, + receiveAddress, + sendAddress, + rate: quote.rate, + createdAt: now, + expiresAt: now + QuoteStore.QUOTE_TTL_MS, + metadata: { + chainflipSwapId: firstStep.chainflipSpecific?.chainflipSwapId, + nearIntentsDepositAddress: firstStep.nearIntentsSpecific?.depositAddress, + nearIntentsDepositMemo: firstStep.nearIntentsSpecific?.depositMemo, + relayId: firstStep.relayTransactionMetadata?.relayId, + }, + stepChainIds: quote.steps.map(step => step.sellAsset.chainId), + status: 'pending', + }) + const depositContextResult = await resolveDepositContext(quote, firstStep, validSwapperName) if (!depositContextResult.ok) { res.status(depositContextResult.statusCode).json(depositContextResult.error) @@ -491,7 +517,7 @@ export const getQuote = async (req: Request, res: Response): Promise => { sellAmountCryptoBaseUnit: firstStep.sellAmountIncludingProtocolFeesCryptoBaseUnit, buyAmountBeforeFeesCryptoBaseUnit: lastStep.buyAmountBeforeFeesCryptoBaseUnit, buyAmountAfterFeesCryptoBaseUnit: lastStep.buyAmountAfterFeesCryptoBaseUnit, - affiliateBps: quote.affiliateBps, + affiliateBps: req.affiliateInfo?.affiliateBps ?? DEFAULT_AFFILIATE_BPS, slippageTolerancePercentageDecimal: quote.slippageTolerancePercentageDecimal, networkFeeCryptoBaseUnit: firstStep.feeData.networkFeeCryptoBaseUnit, steps: quote.steps.map((step, index) => diff --git a/packages/public-api/src/routes/rates.ts b/packages/public-api/src/routes/rates.ts index 4405ecb0b8e..cbf038a0bd9 100644 --- a/packages/public-api/src/routes/rates.ts +++ b/packages/public-api/src/routes/rates.ts @@ -113,7 +113,7 @@ export const getRates = async (req: Request, res: Response): Promise => { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit: sellAmountCryptoBaseUnit, - affiliateBps: DEFAULT_AFFILIATE_BPS, + affiliateBps: req.affiliateInfo?.affiliateBps ?? DEFAULT_AFFILIATE_BPS, allowMultiHop, slippageTolerancePercentageDecimal, receiveAddress: undefined, @@ -154,7 +154,7 @@ export const getRates = async (req: Request, res: Response): Promise => { steps: 0, estimatedExecutionTimeMs: undefined, priceImpactPercentageDecimal: undefined, - affiliateBps: DEFAULT_AFFILIATE_BPS, + affiliateBps: req.affiliateInfo?.affiliateBps ?? DEFAULT_AFFILIATE_BPS, networkFeeCryptoBaseUnit: undefined, error: { code: error.code ?? TradeQuoteError.UnknownError, @@ -178,7 +178,7 @@ export const getRates = async (req: Request, res: Response): Promise => { steps: rate.steps.length, estimatedExecutionTimeMs: firstStep.estimatedExecutionTimeMs, priceImpactPercentageDecimal: rate.priceImpactPercentageDecimal, - affiliateBps: rate.affiliateBps, + affiliateBps: req.affiliateInfo?.affiliateBps ?? DEFAULT_AFFILIATE_BPS, networkFeeCryptoBaseUnit: firstStep.feeData.networkFeeCryptoBaseUnit, } } catch (error) { diff --git a/packages/public-api/src/routes/status.ts b/packages/public-api/src/routes/status.ts new file mode 100644 index 00000000000..4d920e7ec08 --- /dev/null +++ b/packages/public-api/src/routes/status.ts @@ -0,0 +1,213 @@ +import type { Request, Response } from 'express' +import { z } from 'zod' + +import { getAsset } from '../assets' +import { SWAP_SERVICE_BASE_URL } from '../config' +import { quoteStore } from '../lib/quoteStore' +import type { ErrorResponse } from '../types' + +const STATUS_TIMEOUT_MS = 10_000 + +export const StatusRequestSchema = z.object({ + quoteId: z.string().uuid(), + txHash: z.string().min(1).max(128).optional(), +}) + +const toHumanAmount = (baseUnit: string, precision: number): string => { + if (precision === 0) return baseUnit + const padded = baseUnit.padStart(precision + 1, '0') + return `${padded.slice(0, -precision)}.${padded.slice(-precision)}` +} + +const buildSwapRegistrationBody = (storedQuote: ReturnType & object) => { + const sellAsset = getAsset(storedQuote.sellAssetId) + const buyAsset = getAsset(storedQuote.buyAssetId) + if (!sellAsset || !buyAsset) return undefined + + return { + body: JSON.stringify({ + swapId: storedQuote.quoteId, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: storedQuote.sellAmountCryptoBaseUnit, + expectedBuyAmountCryptoBaseUnit: storedQuote.buyAmountAfterFeesCryptoBaseUnit, + sellAmountCryptoPrecision: toHumanAmount( + storedQuote.sellAmountCryptoBaseUnit, + sellAsset.precision, + ), + expectedBuyAmountCryptoPrecision: toHumanAmount( + storedQuote.buyAmountAfterFeesCryptoBaseUnit, + buyAsset.precision, + ), + sellTxHash: storedQuote.txHash, + source: storedQuote.swapperName, + swapperName: storedQuote.swapperName, + sellAccountId: storedQuote.sendAddress, + receiveAddress: storedQuote.receiveAddress, + affiliateAddress: storedQuote.affiliateAddress, + affiliateBps: storedQuote.affiliateBps, + origin: 'api', + metadata: storedQuote.metadata, + }), + sellAsset, + buyAsset, + } +} + +const registerSwapInService = async ( + storedQuote: ReturnType & object, +): Promise => { + const registration = buildSwapRegistrationBody(storedQuote) + if (!registration) return false + + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), STATUS_TIMEOUT_MS) + try { + const postResponse = await fetch(`${SWAP_SERVICE_BASE_URL}/swaps`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + signal: controller.signal, + body: registration.body, + }) + if (!postResponse.ok) { + const errorBody = await postResponse.text() + console.error(`swap-service POST failed (${postResponse.status}):`, errorBody) + return false + } + return true + } catch (err) { + console.error('Failed to register swap in swap-service:', err) + return false + } finally { + clearTimeout(timeout) + } +} + +export const getSwapStatus = async (req: Request, res: Response): Promise => { + try { + const parseResult = StatusRequestSchema.safeParse(req.query) + if (!parseResult.success) { + res.status(400).json({ + error: 'Invalid request parameters', + details: parseResult.error.errors, + } as ErrorResponse) + return + } + + const { quoteId, txHash } = parseResult.data + + const storedQuote = quoteStore.get(quoteId) + + if (!storedQuote) { + res.status(404).json({ + error: 'Quote not found or expired', + code: 'QUOTE_NOT_FOUND', + } as ErrorResponse) + return + } + + const requestAffiliateAddress = req.affiliateInfo?.affiliateAddress?.toLowerCase() + const quoteAffiliateAddress = storedQuote.affiliateAddress?.toLowerCase() + if (quoteAffiliateAddress && requestAffiliateAddress !== quoteAffiliateAddress) { + res.status(403).json({ + error: 'Quote is not accessible for this affiliate', + code: 'AFFILIATE_MISMATCH', + } as ErrorResponse) + return + } + + if (txHash && storedQuote.txHash && storedQuote.txHash !== txHash) { + res.status(409).json({ + error: 'Transaction hash does not match the registered swap', + code: 'TX_HASH_MISMATCH', + } as ErrorResponse) + return + } + + if (txHash && !storedQuote.txHash) { + // Defense-in-depth: re-read from store before mutation (future-proofing for potential async operations above) + const current = quoteStore.get(quoteId) + if (current?.txHash) { + res.json({ + quoteId, + txHash: current.txHash, + status: current.status, + swapperName: current.swapperName, + sellAssetId: current.sellAssetId, + buyAssetId: current.buyAssetId, + sellAmountCryptoBaseUnit: current.sellAmountCryptoBaseUnit, + buyAmountAfterFeesCryptoBaseUnit: current.buyAmountAfterFeesCryptoBaseUnit, + affiliateAddress: current.affiliateAddress, + affiliateBps: current.affiliateBps, + registeredAt: current.registeredAt, + }) + return + } + + storedQuote.txHash = txHash + storedQuote.registeredAt = Date.now() + storedQuote.status = 'submitted' + quoteStore.set(quoteId, storedQuote) + + await registerSwapInService(storedQuote) + } + + let swapServiceStatus: Record | null = null + if (storedQuote.txHash) { + const getController = new AbortController() + const getTimeout = setTimeout(() => getController.abort(), STATUS_TIMEOUT_MS) + try { + const swapResponse = await fetch(`${SWAP_SERVICE_BASE_URL}/swaps/${quoteId}`, { + signal: getController.signal, + }) + if (swapResponse.ok) { + swapServiceStatus = (await swapResponse.json()) as Record + } else if (swapResponse.status === 404) { + await registerSwapInService(storedQuote) + } + } catch (err) { + console.error('Failed to fetch swap status from swap-service:', err) + } finally { + clearTimeout(getTimeout) + } + } + + const status = + swapServiceStatus?.status === 'SUCCESS' + ? 'confirmed' + : swapServiceStatus?.status === 'FAILED' + ? 'failed' + : storedQuote.status + + if (status !== storedQuote.status && (status === 'confirmed' || status === 'failed')) { + storedQuote.status = status + quoteStore.set(quoteId, storedQuote) + } + + const response: Record = { + quoteId, + txHash: storedQuote.txHash, + status, + swapperName: storedQuote.swapperName, + sellAssetId: storedQuote.sellAssetId, + buyAssetId: storedQuote.buyAssetId, + sellAmountCryptoBaseUnit: storedQuote.sellAmountCryptoBaseUnit, + buyAmountAfterFeesCryptoBaseUnit: storedQuote.buyAmountAfterFeesCryptoBaseUnit, + affiliateAddress: storedQuote.affiliateAddress, + affiliateBps: storedQuote.affiliateBps, + registeredAt: storedQuote.registeredAt, + } + + if (swapServiceStatus?.buyTxHash) { + response.buyTxHash = swapServiceStatus.buyTxHash + } + if (swapServiceStatus?.isAffiliateVerified !== undefined) { + response.isAffiliateVerified = swapServiceStatus.isAffiliateVerified + } + + res.json(response) + } catch (error) { + console.error('Error in getSwapStatus:', error) + res.status(500).json({ error: 'Internal server error' } as ErrorResponse) + } +} diff --git a/packages/public-api/src/types.ts b/packages/public-api/src/types.ts index d5cc427914f..957a75a700b 100644 --- a/packages/public-api/src/types.ts +++ b/packages/public-api/src/types.ts @@ -24,7 +24,8 @@ export type { } export type AffiliateInfo = { - affiliateAddress: string + affiliateAddress?: string + affiliateBps?: string } export type RatesRequest = { diff --git a/packages/swap-widget/Dockerfile b/packages/swap-widget/Dockerfile index f7ffd258c8f..755d8d1543a 100644 --- a/packages/swap-widget/Dockerfile +++ b/packages/swap-widget/Dockerfile @@ -48,28 +48,20 @@ COPY packages/types/package.json ./packages/types/ COPY packages/unchained-client/package.json ./packages/unchained-client/ COPY packages/utils/package.json ./packages/utils/ -# Copy import method avoids hard-link failures from the pnpm store on Docker overlay FS +# pnpm uses copy instead of hard links, which Docker's overlay filesystem doesn't support ENV NPM_CONFIG_PACKAGE_IMPORT_METHOD=copy # Install dependencies, skipping lifecycle scripts (controlled by onlyBuiltDependencies in package.json) -# Retry loop: pnpm's hoisted linker intermittently fails with ENOENT on Docker overlay FS (known issue). RUN --mount=type=cache,id=s/4cafb297-3349-46e2-bfef-8210504e0583-/root/.local/share/pnpm/store,target=/root/.local/share/pnpm/store \ - corepack enable && \ - for i in 1 2 3; do \ - pnpm install --frozen-lockfile --ignore-scripts && break || \ - rm -rf node_modules; \ - done - -# Run postinstall for openapi-generator-cli to download the JAR (skipped by --ignore-scripts above) -RUN --mount=type=cache,id=s/4cafb297-3349-46e2-bfef-8210504e0583-/root/.openapi-generator-cli,target=/root/.openapi-generator-cli \ - pnpm rebuild @openapitools/openapi-generator-cli + corepack enable && pnpm install --frozen-lockfile --ignore-scripts # Copy unchained-client config and generator files FIRST (rarely changes, enables layer caching) COPY packages/unchained-client/openapitools.json ./packages/unchained-client/ COPY packages/unchained-client/generator/ ./packages/unchained-client/generator/ # Generate unchained-client code (required by tsc --build for workspace packages) -RUN pnpm --filter @shapeshiftoss/unchained-client generate +RUN --mount=type=cache,id=s/4cafb297-3349-46e2-bfef-8210504e0583-/root/.openapitools,target=/root/.openapitools \ + pnpm --filter @shapeshiftoss/unchained-client generate # Copy remaining source files COPY packages/ ./packages/ diff --git a/packages/swap-widget/package.json b/packages/swap-widget/package.json index 7d75f42f54f..6af23a0343d 100644 --- a/packages/swap-widget/package.json +++ b/packages/swap-widget/package.json @@ -1,14 +1,32 @@ { "name": "@shapeshiftoss/swap-widget", - "version": "0.0.1", - "private": true, + "version": "0.1.0", "description": "Embeddable swap widget using ShapeShift API", "type": "module", "main": "dist/index.js", + "module": "dist/index.js", "types": "dist/index.d.ts", + "style": "dist/style.css", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./style.css": "./dist/style.css", + "./dist/style.css": "./dist/style.css" + }, + "sideEffects": [ + "*.css" + ], + "files": [ + "dist", + "README.md" + ], "scripts": { "dev": "vite", - "build": "tsc && vite build", + "clean": "rm -rf dist node_modules", + "build": "rm -rf dist && tsc && vite build", "preview": "vite preview", "lint": "eslint src --ext .ts,.tsx", "lint:fix": "eslint src --ext .ts,.tsx --fix", @@ -17,6 +35,49 @@ "test:watch": "vitest" }, "dependencies": { + "@shapeshiftoss/caip": "^8.0.0", + "@shapeshiftoss/types": "^8.0.0", + "@shapeshiftoss/utils": "^1.0.0", + "@xstate/react": "5.0.5", + "bech32": "^2.0.0", + "p-queue": "^8.0.1", + "react-virtuoso": "^4.7.11", + "xstate": "5.28.0" + }, + "peerDependencies": { + "@reown/appkit": "^1.8.17", + "@reown/appkit-adapter-bitcoin": "^1.8.17", + "@reown/appkit-adapter-solana": "^1.8.17", + "@reown/appkit-adapter-wagmi": "^1.8.17", + "@solana/wallet-adapter-wallets": "^0.19.32", + "@solana/web3.js": "^1.98.0", + "@tanstack/react-query": "^5.69.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0", + "viem": "^2.40.0", + "wagmi": "^3.3.0" + }, + "peerDependenciesMeta": { + "@reown/appkit": { + "optional": true + }, + "@reown/appkit-adapter-bitcoin": { + "optional": true + }, + "@reown/appkit-adapter-solana": { + "optional": true + }, + "@reown/appkit-adapter-wagmi": { + "optional": true + }, + "@solana/wallet-adapter-wallets": { + "optional": true + }, + "@solana/web3.js": { + "optional": true + } + }, + "devDependencies": { "@reown/appkit": "^1.8.17", "@reown/appkit-adapter-bitcoin": "^1.8.17", "@reown/appkit-adapter-solana": "^1.8.17", @@ -27,15 +88,6 @@ "@solana/wallet-adapter-wallets": "^0.19.32", "@solana/web3.js": "1.98.0", "@tanstack/react-query": "^5.69.0", - "@xstate/react": "5.0.5", - "bech32": "^2.0.0", - "p-queue": "^8.0.1", - "react-virtuoso": "^4.7.11", - "viem": "2.40.3", - "wagmi": "3.3.2", - "xstate": "5.28.0" - }, - "devDependencies": { "@testing-library/jest-dom": "6.9.1", "@testing-library/react": "16.3.2", "@types/react": "^19.0.0", @@ -45,12 +97,14 @@ "react": "^19.0.0", "react-dom": "^19.0.0", "typescript": "~5.2.2", + "viem": "2.40.3", "vite": "^5.0.0", + "vite-plugin-dts": "^4.5.4", "vite-plugin-node-polyfills": "0.23.0", - "vitest": "4.0.18" + "vitest": "4.0.18", + "wagmi": "3.3.2" }, - "peerDependencies": { - "react": ">=18.0.0", - "react-dom": ">=18.0.0" + "publishConfig": { + "access": "public" } } diff --git a/packages/swap-widget/src/api/client.ts b/packages/swap-widget/src/api/client.ts index 18aa6b56c28..39156b7e544 100644 --- a/packages/swap-widget/src/api/client.ts +++ b/packages/swap-widget/src/api/client.ts @@ -6,6 +6,7 @@ const DEFAULT_API_BASE_URL = export type ApiClientConfig = { baseUrl?: string affiliateAddress?: string + affiliateBps?: string } export const createApiClient = (config: ApiClientConfig = {}) => { @@ -24,6 +25,9 @@ export const createApiClient = (config: ApiClientConfig = {}) => { if (config.affiliateAddress) { headers['x-affiliate-address'] = config.affiliateAddress } + if (config.affiliateBps) { + headers['x-affiliate-bps'] = config.affiliateBps + } const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), timeoutMs) diff --git a/packages/swap-widget/src/components/SwapWidget.css b/packages/swap-widget/src/components/SwapWidget.css index 44cac46413e..bfc98765860 100644 --- a/packages/swap-widget/src/components/SwapWidget.css +++ b/packages/swap-widget/src/components/SwapWidget.css @@ -47,11 +47,11 @@ --ssw-bg-tertiary: #ffffff; --ssw-bg-input: #f0f2f5; --ssw-bg-hover: rgba(0, 0, 0, 0.04); - --ssw-border: rgba(0, 0, 0, 0.08); - --ssw-border-hover: rgba(0, 0, 0, 0.15); + --ssw-border: rgba(0, 0, 0, 0.12); + --ssw-border-hover: rgba(0, 0, 0, 0.2); --ssw-text-primary: #1a1a2e; - --ssw-text-secondary: #5c5c70; - --ssw-text-muted: #9090a0; + --ssw-text-secondary: #4a4a60; + --ssw-text-muted: #6b6b7b; background: var(--ssw-bg-secondary); color: var(--ssw-text-primary); diff --git a/packages/swap-widget/src/components/SwapWidget.tsx b/packages/swap-widget/src/components/SwapWidget.tsx index df83a9b5766..8a5776ffee3 100644 --- a/packages/swap-widget/src/components/SwapWidget.tsx +++ b/packages/swap-widget/src/components/SwapWidget.tsx @@ -3,8 +3,10 @@ import './SwapWidget.css' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import type { WalletClient } from 'viem' +import { WagmiProvider } from 'wagmi' import { createApiClient } from '../api/client' +import { standaloneWagmiConfig } from '../config/standaloneWagmi' import { DEFAULT_BUY_ASSET, DEFAULT_SELL_ASSET } from '../constants/defaults' import type { SwapWalletContextValue } from '../contexts/SwapWalletContext' import { SwapWalletProvider, useSwapWallet } from '../contexts/SwapWalletContext' @@ -42,6 +44,7 @@ type SwapWidgetContentProps = { theme: SwapWidgetProps['theme'] showPoweredBy: boolean defaultReceiveAddress?: string + affiliateAddress?: string enableWalletConnection: boolean isBuyAssetLocked: boolean onConnectWallet?: () => void @@ -63,6 +66,7 @@ const SwapWidgetContent = ({ theme = 'dark', showPoweredBy, defaultReceiveAddress, + affiliateAddress, enableWalletConnection, isBuyAssetLocked, onConnectWallet, @@ -129,7 +133,7 @@ const SwapWidgetContent = ({ handleSelectRate, handleSlippageChange, handleButtonClick, - } = useSwapHandlers({ onConnectWallet, onAssetSelect }) + } = useSwapHandlers({ onConnectWallet, onAssetSelect, affiliateAddress }) useSwapQuoting({ apiClient, rates, sellAssetBalance }) @@ -356,6 +360,7 @@ type SwapWidgetCoreProps = { defaultBuyAsset: Asset defaultSlippage: string defaultReceiveAddress?: string + affiliateAddress?: string apiClient: ReturnType theme: SwapWidgetProps['theme'] showPoweredBy: boolean @@ -381,6 +386,7 @@ const SwapWidgetCore = ({ defaultBuyAsset, defaultSlippage, defaultReceiveAddress, + affiliateAddress, apiClient, theme, showPoweredBy, @@ -484,6 +490,7 @@ const SwapWidgetCore = ({ theme={theme} showPoweredBy={showPoweredBy} defaultReceiveAddress={defaultReceiveAddress} + affiliateAddress={affiliateAddress} enableWalletConnection={enableWalletConnection} isBuyAssetLocked={isBuyAssetLocked} onConnectWallet={onConnectWallet} @@ -509,39 +516,43 @@ const SwapWidgetWithExternalWallet = (props: SwapWidgetProps) => { createApiClient({ baseUrl: props.apiBaseUrl, affiliateAddress: props.affiliateAddress, + affiliateBps: props.affiliateBps, }), - [props.apiBaseUrl, props.affiliateAddress], + [props.apiBaseUrl, props.affiliateAddress, props.affiliateBps], ) return ( - - - - - + + + + + + + ) } @@ -553,8 +564,9 @@ const SwapWidgetWithInternalWallet = ( createApiClient({ baseUrl: props.apiBaseUrl, affiliateAddress: props.affiliateAddress, + affiliateBps: props.affiliateBps, }), - [props.apiBaseUrl, props.affiliateAddress], + [props.apiBaseUrl, props.affiliateAddress, props.affiliateBps], ) return ( @@ -568,6 +580,7 @@ const SwapWidgetWithInternalWallet = ( defaultBuyAsset={props.defaultBuyAsset ?? DEFAULT_BUY_ASSET} defaultSlippage={props.defaultSlippage ?? '0.5'} defaultReceiveAddress={props.defaultReceiveAddress} + affiliateAddress={props.affiliateAddress} apiClient={apiClient} theme={props.theme} showPoweredBy={props.showPoweredBy ?? true} diff --git a/packages/swap-widget/src/config/appkit.ts b/packages/swap-widget/src/config/appkit.ts index 91ecb38f053..f8a75ab9c11 100644 --- a/packages/swap-widget/src/config/appkit.ts +++ b/packages/swap-widget/src/config/appkit.ts @@ -1,3 +1,4 @@ +import type { AppKitNetwork } from '@reown/appkit/networks' import { arbitrum, avalanche, @@ -21,7 +22,7 @@ import { SolanaAdapter } from '@reown/appkit-adapter-solana/react' import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' import { PhantomWalletAdapter, SolflareWalletAdapter } from '@solana/wallet-adapter-wallets' -export const EVM_NETWORKS = [ +export const EVM_NETWORKS: readonly AppKitNetwork[] = [ mainnet, polygon, arbitrum, @@ -35,9 +36,9 @@ export const EVM_NETWORKS = [ plasma, worldchain, katana, -] as const +] -export const ALL_NETWORKS = [...EVM_NETWORKS, bitcoin, solana] as const +export const ALL_NETWORKS: readonly AppKitNetwork[] = [...EVM_NETWORKS, bitcoin, solana] export type SupportedNetwork = (typeof ALL_NETWORKS)[number] export type EvmNetwork = (typeof EVM_NETWORKS)[number] @@ -94,7 +95,7 @@ export const initializeAppKit = (projectId: string): void => { createAppKit({ adapters: [wagmi, btc, sol], projectId, - networks: [...ALL_NETWORKS], + networks: [...ALL_NETWORKS] as [AppKitNetwork, ...AppKitNetwork[]], metadata: APP_METADATA, }) diff --git a/packages/swap-widget/src/config/standaloneWagmi.ts b/packages/swap-widget/src/config/standaloneWagmi.ts new file mode 100644 index 00000000000..300556f7bee --- /dev/null +++ b/packages/swap-widget/src/config/standaloneWagmi.ts @@ -0,0 +1,18 @@ +import { arbitrum, avalanche, base, bsc, gnosis, mainnet, optimism, polygon } from 'viem/chains' +import { createConfig, http } from 'wagmi' + +const chains = [mainnet, polygon, arbitrum, optimism, base, avalanche, bsc, gnosis] as const + +export const standaloneWagmiConfig = createConfig({ + chains, + transports: { + [mainnet.id]: http(), + [polygon.id]: http(), + [arbitrum.id]: http(), + [optimism.id]: http(), + [base.id]: http(), + [avalanche.id]: http(), + [bsc.id]: http(), + [gnosis.id]: http(), + }, +}) diff --git a/packages/swap-widget/src/config/wagmi.ts b/packages/swap-widget/src/config/wagmi.ts index d8ffb5c949f..0b1388c172a 100644 --- a/packages/swap-widget/src/config/wagmi.ts +++ b/packages/swap-widget/src/config/wagmi.ts @@ -1,3 +1,4 @@ +import type { AppKitNetwork } from '@reown/appkit/networks' import { arbitrum, avalanche, @@ -10,7 +11,7 @@ import { } from '@reown/appkit/networks' import type { Config } from 'wagmi' -export const SUPPORTED_CHAINS = [ +export const SUPPORTED_CHAINS: readonly AppKitNetwork[] = [ mainnet, polygon, arbitrum, @@ -19,9 +20,9 @@ export const SUPPORTED_CHAINS = [ avalanche, bsc, gnosis, -] as const +] export type SupportedChains = typeof SUPPORTED_CHAINS -export type SupportedChainId = SupportedChains[number]['id'] +export type SupportedChainId = number export type WagmiConfig = Config diff --git a/packages/swap-widget/src/demo/App.css b/packages/swap-widget/src/demo/App.css index 0c2e786b03a..76870ee7985 100644 --- a/packages/swap-widget/src/demo/App.css +++ b/packages/swap-widget/src/demo/App.css @@ -434,12 +434,21 @@ body, padding: 10px; } + .demo-hero { + margin-bottom: 0; + } + .demo-title { - font-size: 32px; + font-size: 24px; + margin-bottom: 4px; } .demo-subtitle { - font-size: 14px; + font-size: 13px; + } + + .demo-content { + gap: 16px; } .demo-layout { @@ -448,11 +457,10 @@ body, } .demo-customizer { - width: 100%; - max-width: 420px; + display: none; } .demo-main { - padding: 32px 16px; + padding: 16px 12px; } } diff --git a/packages/swap-widget/src/hooks/useAssets.ts b/packages/swap-widget/src/hooks/useAssets.ts index a1873be5173..b0ed00f5702 100644 --- a/packages/swap-widget/src/hooks/useAssets.ts +++ b/packages/swap-widget/src/hooks/useAssets.ts @@ -1,8 +1,11 @@ import { ASSET_NAMESPACE, fromAssetId } from '@shapeshiftoss/caip' +import type { UseQueryResult } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query' import type { Asset, AssetId, ChainId } from '../types' +type AssetQueryResult = Omit, 'data'> & { data: TData } + const SHAPESHIFT_ASSET_CDN = 'https://app.shapeshift.com' const ASSET_QUERY_STALE_TIME = 5 * 60 * 1000 @@ -37,7 +40,7 @@ const fetchAssetData = async (): Promise => { return response.json() } -export const useAssetData = () => { +export const useAssetData = (): UseQueryResult => { return useQuery({ queryKey: ['assetData'], queryFn: fetchAssetData, @@ -46,7 +49,7 @@ export const useAssetData = () => { }) } -export const useAssets = () => { +export const useAssets = (): AssetQueryResult => { const { data, ...rest } = useAssetData() const assets = data ? data.ids.map(id => data.byId[id]).filter(Boolean) : [] @@ -54,12 +57,12 @@ export const useAssets = () => { return { data: assets, ...rest } } -export const useAssetsById = () => { +export const useAssetsById = (): AssetQueryResult> => { const { data, ...rest } = useAssetData() return { data: data?.byId ?? {}, ...rest } } -export const useAssetById = (assetId: AssetId | undefined) => { +export const useAssetById = (assetId: AssetId | undefined): AssetQueryResult => { const { data: assetsById, ...rest } = useAssetsById() return { data: assetId ? assetsById[assetId] : undefined, @@ -84,7 +87,7 @@ const isNativeAsset = (assetId: string): boolean => { } } -export const useChains = () => { +export const useChains = (): AssetQueryResult => { const { data: assets, ...rest } = useAssets() const chains = (() => { @@ -112,13 +115,15 @@ export const useChains = () => { return { data: chains, ...rest } } -export const useChainInfo = (chainId: ChainId | undefined) => { +export const useChainInfo = ( + chainId: ChainId | undefined, +): AssetQueryResult => { const { data: chains, ...rest } = useChains() const chainInfo = chainId ? chains.find(c => c.chainId === chainId) : undefined return { data: chainInfo, ...rest } } -export const useAssetsByChainId = (chainId: ChainId | undefined) => { +export const useAssetsByChainId = (chainId: ChainId | undefined): AssetQueryResult => { const { data: assets, ...rest } = useAssets() const filteredAssets = chainId ? assets.filter(asset => asset.chainId === chainId) : assets @@ -146,7 +151,7 @@ const scoreAsset = (asset: Asset, query: string): number => { return score } -export const useAssetSearch = (query: string, chainId?: ChainId) => { +export const useAssetSearch = (query: string, chainId?: ChainId): AssetQueryResult => { const { data: assets, ...rest } = useAssets() const searchResults = (() => { diff --git a/packages/swap-widget/src/hooks/useBalances.ts b/packages/swap-widget/src/hooks/useBalances.ts index 5d2fc743327..2d190114036 100644 --- a/packages/swap-widget/src/hooks/useBalances.ts +++ b/packages/swap-widget/src/hooks/useBalances.ts @@ -21,7 +21,7 @@ const balanceQueue = new PQueue({ intervalCap: CONCURRENCY_LIMIT, }) -type BalanceResult = { +export type BalanceResult = { assetId: AssetId balance: string balanceFormatted: string @@ -29,6 +29,12 @@ type BalanceResult = { type BalancesMap = Record +type SingleBalanceResult = { + data: BalanceResult | undefined + isLoading: boolean + refetch: (() => void) | undefined +} + type ParsedAssetEvm = { chainType: 'evm' evmChainId: number @@ -275,7 +281,7 @@ export const useBitcoinBalance = ( address: string | undefined, assetId: AssetId | undefined, precision: number = 8, -) => { +): SingleBalanceResult => { const parsed = assetId ? parseAssetIdMultiChain(assetId) : null const isUtxo = parsed?.chainType === 'utxo' @@ -330,7 +336,7 @@ export const useSolanaBalance = ( address: string | undefined, assetId: AssetId | undefined, precision: number = 9, -) => { +): SingleBalanceResult => { const { connection: walletConnection } = useAppKitConnection() const parsed = assetId ? parseAssetIdMultiChain(assetId) : null const isSolana = parsed?.chainType === 'solana' @@ -422,7 +428,7 @@ export const useMultiChainBalance = ( solanaAddress: string | undefined, assetId: AssetId | undefined, precision: number = 18, -) => { +): SingleBalanceResult => { const parsed = assetId ? parseAssetIdMultiChain(assetId) : null const chainType = parsed?.chainType ?? 'other' const config = useConfig() diff --git a/packages/swap-widget/src/hooks/useMarketData.ts b/packages/swap-widget/src/hooks/useMarketData.ts index 84794d16394..94e438dca22 100644 --- a/packages/swap-widget/src/hooks/useMarketData.ts +++ b/packages/swap-widget/src/hooks/useMarketData.ts @@ -1,9 +1,12 @@ import { adapters } from '@shapeshiftoss/caip' import { BigAmount, bn } from '@shapeshiftoss/utils' +import type { UseQueryResult } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query' import type { AssetId } from '../types' +type MarketDataQueryResult = Omit, 'data'> & { data: TData } + const MARKET_DATA_STALE_TIME = 10 * 60 * 1000 const MARKET_DATA_GC_TIME = 60 * 60 * 1000 @@ -112,7 +115,7 @@ const fetchAllMarketData = async (): Promise => { return result } -export const useAllMarketData = () => { +export const useAllMarketData = (): UseQueryResult => { return useQuery({ queryKey: ['allMarketData'], queryFn: fetchAllMarketData, @@ -123,7 +126,7 @@ export const useAllMarketData = () => { }) } -export const useMarketData = (assetIds: AssetId[]) => { +export const useMarketData = (assetIds: AssetId[]): MarketDataQueryResult => { const { data: allMarketData, ...rest } = useAllMarketData() const filteredData = (() => { @@ -141,7 +144,9 @@ export const useMarketData = (assetIds: AssetId[]) => { return { data: filteredData, ...rest } } -export const useAssetPrice = (assetId: AssetId | undefined) => { +export const useAssetPrice = ( + assetId: AssetId | undefined, +): MarketDataQueryResult => { const { data: allMarketData, ...rest } = useAllMarketData() return { diff --git a/packages/swap-widget/src/hooks/useSwapDisplayValues.ts b/packages/swap-widget/src/hooks/useSwapDisplayValues.ts index 9f50e3519dc..c7a6f8b40fc 100644 --- a/packages/swap-widget/src/hooks/useSwapDisplayValues.ts +++ b/packages/swap-widget/src/hooks/useSwapDisplayValues.ts @@ -1,11 +1,15 @@ +import type { Asset as ShapeshiftAsset } from '@shapeshiftoss/types' import { useMemo } from 'react' import type { ApiClient } from '../api/client' import { getBaseAsset } from '../constants/chains' import { useSwapWallet } from '../contexts/SwapWalletContext' import { SwapMachineCtx } from '../machines/SwapMachineContext' +import type { TradeRate } from '../types' import { formatAmount, getChainType } from '../types' +import type { ChainInfo } from './useAssets' import { useChainInfo } from './useAssets' +import type { BalanceResult } from './useBalances' import { useMultiChainBalance } from './useBalances' import { formatUsdValue, useMarketData } from './useMarketData' import { useSwapRates } from './useSwapRates' @@ -14,7 +18,33 @@ type UseSwapDisplayValuesParams = { apiClient: ApiClient } -export const useSwapDisplayValues = ({ apiClient }: UseSwapDisplayValuesParams) => { +type SwapDisplayValues = { + rates: TradeRate[] | undefined + isLoadingRates: boolean + ratesError: Error | null + sellAssetBalance: BalanceResult | undefined + isSellBalanceLoading: boolean + refetchSellBalance: (() => void) | undefined + buyAssetBalance: BalanceResult | undefined + isBuyBalanceLoading: boolean + refetchBuyBalance: (() => void) | undefined + sellChainInfo: ChainInfo | undefined + buyChainInfo: ChainInfo | undefined + displayRate: TradeRate | undefined + buyAmount: string | undefined + sellChainNativeAsset: ShapeshiftAsset | undefined + networkFeeDisplay: string | undefined + sellUsdValue: string + buyUsdValue: string + sellAssetUsdPrice: string | undefined + buyAssetUsdPrice: string | undefined + sellBalanceFiatValue: string | undefined + buyBalanceFiatValue: string | undefined +} + +export const useSwapDisplayValues = ({ + apiClient, +}: UseSwapDisplayValuesParams): SwapDisplayValues => { const sellAsset = SwapMachineCtx.useSelector(s => s.context.sellAsset) const buyAsset = SwapMachineCtx.useSelector(s => s.context.buyAsset) const sellAmountBaseUnit = SwapMachineCtx.useSelector(s => s.context.sellAmountBaseUnit) diff --git a/packages/swap-widget/src/hooks/useSwapHandlers.ts b/packages/swap-widget/src/hooks/useSwapHandlers.ts index 27cfbe3d8f0..480a8767c53 100644 --- a/packages/swap-widget/src/hooks/useSwapHandlers.ts +++ b/packages/swap-widget/src/hooks/useSwapHandlers.ts @@ -4,13 +4,19 @@ import { useSwapWallet } from '../contexts/SwapWalletContext' import { SwapMachineCtx } from '../machines/SwapMachineContext' import type { Asset, TradeRate } from '../types' import { parseAmount } from '../types' +import { buildShapeShiftTradeUrl } from '../utils/redirect' type UseSwapHandlersParams = { onConnectWallet?: () => void onAssetSelect?: (type: 'sell' | 'buy', asset: Asset) => void + affiliateAddress?: string } -export const useSwapHandlers = ({ onConnectWallet, onAssetSelect }: UseSwapHandlersParams) => { +export const useSwapHandlers = ({ + onConnectWallet, + onAssetSelect, + affiliateAddress, +}: UseSwapHandlersParams) => { const actorRef = SwapMachineCtx.useActorRef() const { walletClient, bitcoin, solana } = useSwapWallet() @@ -62,17 +68,17 @@ export const useSwapHandlers = ({ onConnectWallet, onAssetSelect }: UseSwapHandl const redirectToShapeShift = useCallback(() => { const snap = actorRef.getSnapshot() - const params = new URLSearchParams({ + const sellAmountBaseUnit = snap.context.sellAmount + ? parseAmount(snap.context.sellAmount, snap.context.sellAsset.precision) + : undefined + const url = buildShapeShiftTradeUrl({ sellAssetId: snap.context.sellAsset.assetId, buyAssetId: snap.context.buyAsset.assetId, - sellAmount: snap.context.sellAmount, + sellAmountBaseUnit, + affiliateAddress, }) - window.open( - `https://app.shapeshift.com/trade?${params.toString()}`, - '_blank', - 'noopener,noreferrer', - ) - }, [actorRef]) + window.open(url, '_blank', 'noopener,noreferrer') + }, [actorRef, affiliateAddress]) const handleButtonClick = useCallback(() => { const snap = actorRef.getSnapshot() @@ -91,20 +97,27 @@ export const useSwapHandlers = ({ onConnectWallet, onAssetSelect }: UseSwapHandl !snap.context.isSellAssetUtxo && !snap.context.isSellAssetSolana ) { - const params = new URLSearchParams({ + const sellAmountBaseUnit = snap.context.sellAmount + ? parseAmount(snap.context.sellAmount, snap.context.sellAsset.precision) + : undefined + const url = buildShapeShiftTradeUrl({ sellAssetId: snap.context.sellAsset.assetId, buyAssetId: snap.context.buyAsset.assetId, - sellAmount: snap.context.sellAmount, + sellAmountBaseUnit, + affiliateAddress, }) - window.open( - `https://app.shapeshift.com/trade?${params.toString()}`, - '_blank', - 'noopener,noreferrer', - ) + window.open(url, '_blank', 'noopener,noreferrer') return } actorRef.send({ type: 'FETCH_QUOTE' }) - }, [actorRef, bitcoin.isConnected, solana.isConnected, walletClient, onConnectWallet]) + }, [ + actorRef, + bitcoin.isConnected, + solana.isConnected, + walletClient, + onConnectWallet, + affiliateAddress, + ]) return { handleSwapTokens, diff --git a/packages/swap-widget/src/hooks/useSwapQuote.ts b/packages/swap-widget/src/hooks/useSwapQuote.ts index a3018c7c9a1..5eb9f306834 100644 --- a/packages/swap-widget/src/hooks/useSwapQuote.ts +++ b/packages/swap-widget/src/hooks/useSwapQuote.ts @@ -1,3 +1,4 @@ +import type { UseQueryResult } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query' import type { ApiClient } from '../api/client' @@ -14,7 +15,10 @@ export type UseSwapQuoteParams = { enabled?: boolean } -export const useSwapQuote = (apiClient: ApiClient, params: UseSwapQuoteParams) => { +export const useSwapQuote = ( + apiClient: ApiClient, + params: UseSwapQuoteParams, +): UseQueryResult => { const { sellAssetId, buyAssetId, diff --git a/packages/swap-widget/src/hooks/useSwapRates.ts b/packages/swap-widget/src/hooks/useSwapRates.ts index d87c450d788..04b3d5bc8d4 100644 --- a/packages/swap-widget/src/hooks/useSwapRates.ts +++ b/packages/swap-widget/src/hooks/useSwapRates.ts @@ -1,4 +1,5 @@ import { bnOrZero } from '@shapeshiftoss/utils' +import type { UseQueryResult } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query' import type { ApiClient } from '../api/client' @@ -11,9 +12,13 @@ export type UseSwapRatesParams = { enabled?: boolean allowedSwapperNames?: SwapperName[] refetchInterval?: number + affiliateAddress?: string } -export const useSwapRates = (apiClient: ApiClient, params: UseSwapRatesParams) => { +export const useSwapRates = ( + apiClient: ApiClient, + params: UseSwapRatesParams, +): UseQueryResult => { const { sellAssetId, buyAssetId, @@ -21,10 +26,18 @@ export const useSwapRates = (apiClient: ApiClient, params: UseSwapRatesParams) = enabled = true, allowedSwapperNames, refetchInterval = 15_000, + affiliateAddress, } = params return useQuery({ - queryKey: ['swapRates', sellAssetId, buyAssetId, sellAmountCryptoBaseUnit, allowedSwapperNames], + queryKey: [ + 'swapRates', + sellAssetId, + buyAssetId, + sellAmountCryptoBaseUnit, + allowedSwapperNames, + affiliateAddress, + ], queryFn: async (): Promise => { if (!sellAssetId || !buyAssetId || !sellAmountCryptoBaseUnit) { return [] diff --git a/packages/swap-widget/src/types/index.ts b/packages/swap-widget/src/types/index.ts index c7db4decc18..8324d7d2925 100644 --- a/packages/swap-widget/src/types/index.ts +++ b/packages/swap-widget/src/types/index.ts @@ -139,6 +139,7 @@ export type ThemeConfig = { export type SwapWidgetProps = { affiliateAddress?: string + affiliateBps?: string apiBaseUrl?: string defaultSellAsset?: Asset defaultBuyAsset?: Asset diff --git a/packages/swap-widget/src/utils/redirect.ts b/packages/swap-widget/src/utils/redirect.ts index a24a03f5894..1594b4d91a9 100644 --- a/packages/swap-widget/src/utils/redirect.ts +++ b/packages/swap-widget/src/utils/redirect.ts @@ -6,22 +6,38 @@ const SHAPESHIFT_APP_URL = 'https://app.shapeshift.com' export type RedirectParams = { sellAssetId: AssetId buyAssetId: AssetId - sellAmount?: string + sellAmountBaseUnit?: string + affiliateAddress?: string } +/** + * Build a ShapeShift trade URL using the web app's hash-based route format: + * https://app.shapeshift.com/#/trade/{buyChainId}/{buyAssetSubId}/{sellChainId}/{sellAssetSubId}/{sellAmountBaseUnit}?affiliate=0x... + * + * Asset IDs are CAIP-19 format like "eip155:1/slip44:60" where the first segment is the chainId + * and the second segment is the asset sub-identifier. + */ export const buildShapeShiftTradeUrl = (params: RedirectParams): string => { - const url = new URL(`${SHAPESHIFT_APP_URL}/trade`) - url.searchParams.set('sellAssetId', params.sellAssetId) - url.searchParams.set('buyAssetId', params.buyAssetId) - if (params.sellAmount) { - url.searchParams.set('sellAmount', params.sellAmount) - } - return url.toString() + const { sellAssetId, buyAssetId, sellAmountBaseUnit, affiliateAddress } = params + + // CAIP-19 assetIds have format "chainId/assetSubId" e.g. "eip155:1/slip44:60" + // The first "/" separates chainId from assetSubId + const buySlashIdx = buyAssetId.indexOf('/') + const buyChainId = buyAssetId.substring(0, buySlashIdx) + const buyAssetSubId = buyAssetId.substring(buySlashIdx + 1) + + const sellSlashIdx = sellAssetId.indexOf('/') + const sellChainId = sellAssetId.substring(0, sellSlashIdx) + const sellAssetSubId = sellAssetId.substring(sellSlashIdx + 1) + + const amount = sellAmountBaseUnit || '0' + const affiliate = affiliateAddress ? `?affiliate=${encodeURIComponent(affiliateAddress)}` : '' + + return `${SHAPESHIFT_APP_URL}/#/trade/${buyChainId}/${buyAssetSubId}/${sellChainId}/${sellAssetSubId}/${amount}${affiliate}` } export const redirectToShapeShift = (params: RedirectParams): void => { - const url = buildShapeShiftTradeUrl(params) - window.open(url, '_blank', 'noopener,noreferrer') + window.open(buildShapeShiftTradeUrl(params), '_blank', 'noopener,noreferrer') } export type ChainType = 'evm' | 'utxo' | 'cosmos' | 'solana' | 'other' diff --git a/packages/swap-widget/tsconfig.build.json b/packages/swap-widget/tsconfig.build.json new file mode 100644 index 00000000000..42e05216900 --- /dev/null +++ b/packages/swap-widget/tsconfig.build.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true + }, + "include": ["src"] +} diff --git a/packages/swap-widget/vite.config.ts b/packages/swap-widget/vite.config.ts index 6cb099643a5..ce8ea3dc073 100644 --- a/packages/swap-widget/vite.config.ts +++ b/packages/swap-widget/vite.config.ts @@ -1,12 +1,10 @@ import react from '@vitejs/plugin-react' -import path from 'path' import type { PluginOption } from 'vite' import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' import { nodePolyfills } from 'vite-plugin-node-polyfills' -const isLibBuild = process.env.BUILD_LIB === 'true' - -const libExternals = [ +const LIB_EXTERNAL_PREFIXES = [ 'react', 'react-dom', 'viem', @@ -16,8 +14,14 @@ const libExternals = [ '@reown/appkit-adapter-bitcoin', '@reown/appkit-adapter-solana', '@tanstack/react-query', + '@solana/web3.js', + '@solana/wallet-adapter-wallets', ] +function isExternal(id: string): boolean { + return LIB_EXTERNAL_PREFIXES.some(prefix => id === prefix || id.startsWith(`${prefix}/`)) +} + const defineGlobalThis: PluginOption = { name: 'define-global-this', enforce: 'pre', @@ -32,36 +36,33 @@ const defineGlobalThis: PluginOption = { }, } +const isDemoBuild = process.env.BUILD_DEMO === 'true' + // eslint-disable-next-line import/no-default-export export default defineConfig({ - plugins: isLibBuild - ? [ - defineGlobalThis, - nodePolyfills({ - globals: { - Buffer: true, - global: true, - process: true, - }, - }) as unknown as PluginOption, - react(), - ] - : [defineGlobalThis, react()], + plugins: [ + defineGlobalThis, + nodePolyfills({ + globals: { + Buffer: true, + global: true, + process: true, + }, + }) as unknown as PluginOption, + react(), + ...(!isDemoBuild + ? [ + dts({ + tsconfigPath: './tsconfig.build.json', + rollupTypes: true, + }), + ] + : []), + ], define: { + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'), 'process.env': {}, }, - resolve: { - alias: { - '@reown/appkit/core': path.resolve( - __dirname, - '../../node_modules/@reown/appkit/dist/esm/exports/core.js', - ), - '@reown/appkit/networks': path.resolve( - __dirname, - '../../node_modules/@reown/appkit/dist/esm/exports/networks.js', - ), - }, - }, optimizeDeps: { exclude: ['@shapeshiftoss/caip', '@shapeshiftoss/utils'], esbuildOptions: { @@ -71,22 +72,28 @@ export default defineConfig({ }, }, server: { - port: 3001, + port: 5174, open: false, }, preview: { port: Number(process.env.PORT) || 3000, host: true, }, - build: isLibBuild + publicDir: isDemoBuild ? 'public' : false, + build: isDemoBuild ? { + outDir: 'dist', + } + : { lib: { entry: 'src/index.ts', name: 'SwapWidget', fileName: 'index', + formats: ['es'], }, + cssCodeSplit: false, rollupOptions: { - external: libExternals, + external: isExternal, output: { globals: { react: 'React', @@ -94,8 +101,5 @@ export default defineConfig({ }, }, }, - } - : { - outDir: 'dist', }, }) diff --git a/packages/swapper/package.json b/packages/swapper/package.json index c913dbc807e..b5d9bea0249 100644 --- a/packages/swapper/package.json +++ b/packages/swapper/package.json @@ -21,8 +21,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/swapper/src/cowswap-utils/constants.ts b/packages/swapper/src/cowswap-utils/constants.ts index 2b9cbb18757..635e505ab3e 100644 --- a/packages/swapper/src/cowswap-utils/constants.ts +++ b/packages/swapper/src/cowswap-utils/constants.ts @@ -29,6 +29,9 @@ export const SUPPORTED_CHAIN_IDS: ChainId[] = [ KnownChainIds.AvalancheMainnet, KnownChainIds.PolygonMainnet, KnownChainIds.BnbSmartChainMainnet, + KnownChainIds.PlasmaMainnet, + KnownChainIds.LineaMainnet, + KnownChainIds.InkMainnet, ] export const COW_SWAP_VAULT_RELAYER_ADDRESS = '0xc92e8bdf79f0507f65a392b0ab4667716bfe0110' diff --git a/packages/swapper/src/cowswap-utils/index.ts b/packages/swapper/src/cowswap-utils/index.ts index c2391c3ee4b..a81f81da8b5 100644 --- a/packages/swapper/src/cowswap-utils/index.ts +++ b/packages/swapper/src/cowswap-utils/index.ts @@ -46,6 +46,12 @@ export const getCowNetwork = (chainId: ChainId): CowNetwork | undefined => { return CowNetwork.Polygon case KnownChainIds.BnbSmartChainMainnet: return CowNetwork.Bnb + case KnownChainIds.PlasmaMainnet: + return CowNetwork.Plasma + case KnownChainIds.LineaMainnet: + return CowNetwork.Linea + case KnownChainIds.InkMainnet: + return CowNetwork.Ink default: return } diff --git a/packages/swapper/src/swappers/AcrossSwapper/constant.ts b/packages/swapper/src/swappers/AcrossSwapper/constant.ts index cbbaed98dc8..05e7142aa9e 100644 --- a/packages/swapper/src/swappers/AcrossSwapper/constant.ts +++ b/packages/swapper/src/swappers/AcrossSwapper/constant.ts @@ -7,11 +7,13 @@ import { hyperEvmChainId, inkChainId, lineaChainId, + megaethChainId, modeChainId, monadChainId, optimismChainId, plasmaChainId, polygonChainId, + scrollChainId, solanaChainId, soneiumChainId, unichainChainId, @@ -34,6 +36,7 @@ import { optimism, plasma, polygon, + scroll, soneium, unichain, worldchain, @@ -63,6 +66,8 @@ export const chainIdToAcrossChainId: Record = { [soneiumChainId]: soneium.id, // Across uses a custom Solana chain ID [solanaChainId]: 34268394551451, + [scrollChainId]: scroll.id, + [megaethChainId]: 4326, } export const acrossChainIdToChainId = invert(chainIdToAcrossChainId) diff --git a/packages/swapper/src/swappers/AcrossSwapper/utils/getTrade.ts b/packages/swapper/src/swappers/AcrossSwapper/utils/getTrade.ts index 45f7abc95dc..54c75a6b5e6 100644 --- a/packages/swapper/src/swappers/AcrossSwapper/utils/getTrade.ts +++ b/packages/swapper/src/swappers/AcrossSwapper/utils/getTrade.ts @@ -34,6 +34,7 @@ import type { } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { getInputOutputRate, makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { getTreasuryAddressFromChainId, isNativeEvmAsset } from '../../utils/helpers/helpers' import { ACROSS_SOLANA_TOKEN_ADDRESS, @@ -273,9 +274,9 @@ export async function getTrade({ if (quote.swapTx.ecosystem !== 'svm') return Ok(undefined) try { - const versionedTransaction = VersionedTransaction.deserialize( - new Uint8Array(Buffer.from(quote.swapTx.data, 'base64')), - ) + const txBytes = Buffer.from(quote.swapTx.data, 'base64') + const isOversized = txBytes.length > 1232 + const versionedTransaction = VersionedTransaction.deserialize(new Uint8Array(txBytes)) const adapter = deps.assertGetSolanaChainAdapter(sellAsset.chainId) @@ -307,6 +308,7 @@ export async function getTrade({ return Ok({ instructions: instructionsWithoutComputeBudget, addressLookupTableAddresses: addressLookupTableAccountKeys, + isOversized, }) } catch (e) { return Err( @@ -422,6 +424,17 @@ export async function getTrade({ estimatedExecutionTimeMs: quote.expectedFillTime * 1000, acrossTransactionMetadata, solanaTransactionMetadata, + affiliateFee: + appFee !== undefined && appFeeRecipient !== undefined + ? buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + }) + : undefined, } const baseQuoteOrRate = { diff --git a/packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.ts b/packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.ts index 3509c2aa563..f025abc6bb5 100644 --- a/packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.ts +++ b/packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeQuote.ts @@ -9,6 +9,7 @@ import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constant import type { CommonTradeQuoteInput, SwapErrorRight, SwapperDeps, TradeQuote } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { getInputOutputRate, makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { getTreasuryAddressFromChainId } from '../../utils/helpers/helpers' import { AVNU_SUPPORTED_CHAIN_IDS } from '../utils/constants' import { getTokenAddress } from '../utils/helpers' @@ -176,6 +177,14 @@ export const getTradeQuote = async ( quoteId: bestQuote.quoteId, routes: bestQuote.routes, }, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmount, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + }), }, ], } diff --git a/packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeRate.ts b/packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeRate.ts index 474b6d8c1ab..a1260243cbe 100644 --- a/packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeRate.ts +++ b/packages/swapper/src/swappers/AvnuSwapper/swapperApi/getTradeRate.ts @@ -9,6 +9,7 @@ import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constant import type { GetTradeRateInput, SwapErrorRight, SwapperDeps, TradeRate } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { getInputOutputRate, makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { getTreasuryAddressFromChainId } from '../../utils/helpers/helpers' import { AVNU_SUPPORTED_CHAIN_IDS } from '../utils/constants' import { getTokenAddress } from '../utils/helpers' @@ -142,6 +143,14 @@ export const getTradeRate = async ( sellAsset, source: SwapperName.Avnu, estimatedExecutionTimeMs: undefined, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmount, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + }), }, ], } diff --git a/packages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.ts b/packages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.ts index 2e893139f8d..b71151797ca 100644 --- a/packages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.ts +++ b/packages/swapper/src/swappers/BebopSwapper/getBebopTradeQuote/getBebopTradeQuote.ts @@ -19,6 +19,7 @@ import type { } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { isNativeEvmAsset } from '../../utils/helpers/helpers' import { BEBOP_DUMMY_ADDRESS } from '../types' import { fetchBebopQuote } from '../utils/fetchFromBebop' @@ -151,6 +152,15 @@ export async function getBebopTradeQuote( sellAmountIncludingProtocolFeesCryptoBaseUnit, source: SwapperName.Bebop, bebopTransactionMetadata: transactionMetadata, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + isEstimate: true, + }), }, ] as SingleHopTradeQuoteSteps, }) diff --git a/packages/swapper/src/swappers/BebopSwapper/getBebopTradeRate/getBebopTradeRate.ts b/packages/swapper/src/swappers/BebopSwapper/getBebopTradeRate/getBebopTradeRate.ts index 3da28e99fc1..43af48db438 100644 --- a/packages/swapper/src/swappers/BebopSwapper/getBebopTradeRate/getBebopTradeRate.ts +++ b/packages/swapper/src/swappers/BebopSwapper/getBebopTradeRate/getBebopTradeRate.ts @@ -17,6 +17,7 @@ import type { } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { isNativeEvmAsset } from '../../utils/helpers/helpers' import { fetchBebopPrice } from '../utils/fetchFromBebop' import { assertValidTrade, calculateRate } from '../utils/helpers/helpers' @@ -123,6 +124,15 @@ export async function getBebopTradeRate( buyAmountAfterFeesCryptoBaseUnit, sellAmountIncludingProtocolFeesCryptoBaseUnit, source: SwapperName.Bebop, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + isEstimate: true, + }), }, ] as SingleHopTradeRateSteps, }) diff --git a/packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.ts b/packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.ts index e5fd91f0b12..4dc9c14d8c6 100644 --- a/packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.ts +++ b/packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeQuote.ts @@ -23,6 +23,7 @@ import { getInputOutputRate, makeSwapErrorRight, } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { makeButterSwapAffiliate } from '../utils/constants' import { ButterSwapErrorCode, @@ -192,9 +193,12 @@ export const getTradeQuote = async ( if (sellAsset.chainId !== solanaChainId) return Ok(undefined) const txData = buildTx.data.startsWith('0x') ? buildTx.data.slice(2) : buildTx.data - const versionedTransaction = VersionedTransaction.deserialize( - new Uint8Array(Buffer.from(txData, 'hex')), - ) + const txBytes = Buffer.from(txData, 'hex') + // Solana transactions are limited to 1232 bytes. If Butter returns a larger tx, + // we need to split it into a Jito bundle (2 txs with tip in the last one). + const SOLANA_TX_SIZE_LIMIT = 1232 + const isOversized = txBytes.length > SOLANA_TX_SIZE_LIMIT + const versionedTransaction = VersionedTransaction.deserialize(new Uint8Array(txBytes)) const adapter = _deps.assertGetSolanaChainAdapter(sellAsset.chainId) @@ -226,6 +230,7 @@ export const getTradeQuote = async ( return Ok({ instructions, addressLookupTableAddresses: addressLookupTableAccountKeys, + isOversized, }) } catch (error) { return Err( @@ -273,6 +278,15 @@ export const getTradeQuote = async ( ...(solanaTransactionMetadata && { solanaTransactionMetadata, }), + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + isEstimate: true, + }), } const tradeQuote: TradeQuote = { diff --git a/packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeRate.ts b/packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeRate.ts index 1b54485113a..ce455691d56 100644 --- a/packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeRate.ts +++ b/packages/swapper/src/swappers/ButterSwap/swapperApi/getTradeRate.ts @@ -17,6 +17,7 @@ import { getInputOutputRate, makeSwapErrorRight, } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { makeButterSwapAffiliate } from '../utils/constants' import { ButterSwapErrorCode, @@ -164,6 +165,15 @@ export const getTradeRate = async ( accountNumber, allowanceContract: route.contract ?? '0x0', estimatedExecutionTimeMs: route.timeEstimated * 1000, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + isEstimate: true, + }), } const tradeRate: TradeRate = { diff --git a/packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.ts b/packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.ts index 44957f76228..ea78ba46984 100644 --- a/packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.ts +++ b/packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeQuote.ts @@ -8,6 +8,7 @@ import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constant import type { CommonTradeQuoteInput, SwapErrorRight, SwapperDeps, TradeQuote } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { getAggregatorClient, getSuiClient } from '../utils/helpers' import { getCetusTradeData } from './getCetusTradeData' @@ -121,6 +122,15 @@ export const getTradeQuote = async ( sellAsset, allowanceContract: '0x0', estimatedExecutionTimeMs: undefined, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmount, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + isEstimate: true, + }), }, ], } diff --git a/packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeRate.ts b/packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeRate.ts index 7cc7e0025fa..e137269d8bc 100644 --- a/packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeRate.ts +++ b/packages/swapper/src/swappers/CetusSwapper/swapperApi/getTradeRate.ts @@ -8,6 +8,7 @@ import { getDefaultSlippageDecimalPercentageForSwapper } from '../../../constant import type { GetTradeRateInput, SwapErrorRight, SwapperDeps, TradeRate } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { getAggregatorClient, getSuiClient } from '../utils/helpers' import { getCetusTradeData } from './getCetusTradeData' @@ -114,6 +115,15 @@ export const getTradeRate = async ( sellAsset, allowanceContract: '0x0', estimatedExecutionTimeMs: undefined, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmount, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + isEstimate: true, + }), }, ], } diff --git a/packages/swapper/src/swappers/ChainflipSwapper/constants.ts b/packages/swapper/src/swappers/ChainflipSwapper/constants.ts index e6cecf39352..5ddda98adf0 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/constants.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/constants.ts @@ -9,6 +9,9 @@ import { usdcOnArbitrumOneAssetId, usdcOnSolanaAssetId, usdtAssetId, + usdtOnArbitrumOneAssetId, + usdtOnSolanaAssetId, + wbtcAssetId, } from '@shapeshiftoss/caip' import type { Asset } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' @@ -30,10 +33,14 @@ export const ChainflipSupportedChainIds = [ export type ChainflipSupportedChainId = (typeof ChainflipSupportedChainIds)[number] export const ChainflipSupportedAssetIdsByChainId: Partial> = { - [KnownChainIds.EthereumMainnet]: [ethAssetId, flipAssetId, usdcAssetId, usdtAssetId], - [KnownChainIds.ArbitrumMainnet]: [arbitrumAssetId, usdcOnArbitrumOneAssetId], + [KnownChainIds.EthereumMainnet]: [ethAssetId, flipAssetId, usdcAssetId, usdtAssetId, wbtcAssetId], + [KnownChainIds.ArbitrumMainnet]: [ + arbitrumAssetId, + usdcOnArbitrumOneAssetId, + usdtOnArbitrumOneAssetId, + ], [KnownChainIds.BitcoinMainnet]: [btcAssetId], - [KnownChainIds.SolanaMainnet]: [solAssetId, usdcOnSolanaAssetId], + [KnownChainIds.SolanaMainnet]: [solAssetId, usdcOnSolanaAssetId, usdtOnSolanaAssetId], } export const chainIdToChainflipNetwork: Partial> = { diff --git a/packages/swapper/src/swappers/ChainflipSwapper/utils/getQuoteOrRate.ts b/packages/swapper/src/swappers/ChainflipSwapper/utils/getQuoteOrRate.ts index c3eb69ed432..ab67394df3d 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/utils/getQuoteOrRate.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/utils/getQuoteOrRate.ts @@ -27,6 +27,7 @@ import { getInputOutputRate, makeSwapErrorRight, } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { CHAINFLIP_BOOST_SWAP_SOURCE, CHAINFLIP_DCA_BOOST_SWAP_SOURCE, @@ -355,6 +356,14 @@ export const getQuoteOrRate = async ( : undefined, chainflipMaxBoostFee: getMaxBoostFee(), }, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps: commissionBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: singleQuoteResponse.boostQuote.ingressAmountNative, + buyAmountCryptoBaseUnit: singleQuoteResponse.boostQuote.egressAmountNative, + }), }, ], } as TradeQuote | TradeRate @@ -418,6 +427,14 @@ export const getQuoteOrRate = async ( : undefined, chainflipMaxBoostFee: 0, }, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps: commissionBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: singleQuoteResponse.ingressAmountNative ?? '0', + buyAmountCryptoBaseUnit: singleQuoteResponse.egressAmountNative ?? '0', + }), }, ], } as TradeQuote | TradeRate diff --git a/packages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.ts b/packages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.ts index 91e608e33df..8e6b0ad7ca7 100644 --- a/packages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.ts +++ b/packages/swapper/src/swappers/CowSwapper/getCowSwapTradeQuote/getCowSwapTradeQuote.ts @@ -30,6 +30,7 @@ import { getInputOutputRate, makeSwapErrorRight, } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { isNativeEvmAsset } from '../../utils/helpers/helpers' import { cowService } from '../utils/cowService' import { @@ -186,6 +187,15 @@ export async function getCowSwapTradeQuote( buyAmountBeforeFeesCryptoBaseUnit, buyAmountAfterFeesCryptoBaseUnit, source: SwapperName.CowSwap, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + isEstimate: true, + }), buyAsset, sellAsset, accountNumber, diff --git a/packages/swapper/src/swappers/CowSwapper/getCowSwapTradeRate/getCowSwapTradeRate.ts b/packages/swapper/src/swappers/CowSwapper/getCowSwapTradeRate/getCowSwapTradeRate.ts index aa76cc6d8ae..baa0f185ca9 100644 --- a/packages/swapper/src/swappers/CowSwapper/getCowSwapTradeRate/getCowSwapTradeRate.ts +++ b/packages/swapper/src/swappers/CowSwapper/getCowSwapTradeRate/getCowSwapTradeRate.ts @@ -25,6 +25,7 @@ import { getInputOutputRate, makeSwapErrorRight, } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { isNativeEvmAsset } from '../../utils/helpers/helpers' import { cowService } from '../utils/cowService' import { @@ -181,6 +182,14 @@ export async function getCowSwapTradeRate( buyAmountBeforeFeesCryptoBaseUnit, buyAmountAfterFeesCryptoBaseUnit, source: SwapperName.CowSwap, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + }), buyAsset, sellAsset, accountNumber, diff --git a/packages/swapper/src/swappers/DebridgeSwapper/constant.ts b/packages/swapper/src/swappers/DebridgeSwapper/constant.ts index 4d15d6c3eb3..8bf9c84d85a 100644 --- a/packages/swapper/src/swappers/DebridgeSwapper/constant.ts +++ b/packages/swapper/src/swappers/DebridgeSwapper/constant.ts @@ -2,15 +2,24 @@ import { arbitrumChainId, avalancheChainId, baseChainId, + berachainChainId, + bobChainId, bscChainId, + cronosChainId, ethChainId, + flowEvmChainId, gnosisChainId, hyperEvmChainId, + lineaChainId, + mantleChainId, + megaethChainId, monadChainId, optimismChainId, plasmaChainId, polygonChainId, seiChainId, + sonicChainId, + storyChainId, } from '@shapeshiftoss/caip' import invert from 'lodash/invert' import { zeroAddress } from 'viem' @@ -28,6 +37,15 @@ export const chainIdToDebridgeChainId: Record = { [plasmaChainId]: 100000028, [arbitrumChainId]: 42161, [avalancheChainId]: 43114, + [mantleChainId]: 100000023, + [cronosChainId]: 100000019, + [berachainChainId]: 100000020, + [lineaChainId]: 59144, + [bobChainId]: 100000021, + [sonicChainId]: 100000014, + [storyChainId]: 100000013, + [flowEvmChainId]: 100000009, + [megaethChainId]: 100000031, } export const debridgeChainIdToChainId = invert(chainIdToDebridgeChainId) diff --git a/packages/swapper/src/swappers/JupiterSwapper/swapperApi/getTradeQuote.ts b/packages/swapper/src/swappers/JupiterSwapper/swapperApi/getTradeQuote.ts index 80612714ef9..8dc0e4364d3 100644 --- a/packages/swapper/src/swappers/JupiterSwapper/swapperApi/getTradeQuote.ts +++ b/packages/swapper/src/swappers/JupiterSwapper/swapperApi/getTradeQuote.ts @@ -13,7 +13,12 @@ import type { KnownChainIds } from '@shapeshiftoss/types' import { bn, bnOrZero, convertDecimalPercentageToBasisPoints } from '@shapeshiftoss/utils' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' -import { PublicKey } from '@solana/web3.js' +import { + AddressLookupTableAccount, + MessageV0, + PublicKey, + VersionedTransaction, +} from '@solana/web3.js' import { v4 as uuid } from 'uuid' import type { @@ -25,6 +30,7 @@ import type { } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { getInputOutputRate, makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { COMPUTE_UNIT_MARGIN_MULTIPLIER, TOKEN_2022_PROGRAM_ID } from '../utils/constants' import { calculateAccountCreationCosts, @@ -158,6 +164,34 @@ export const getTradeQuote = async ( jupiterUrl, }) + // Build a trial VersionedTransaction to check if it exceeds the 1232-byte Solana tx size limit + const isOversized = await (async () => { + try { + const lookupTableInfos = await adapter.getAddressLookupTableAccounts( + addressLookupTableAddresses, + ) + const lookupTableAccounts = lookupTableInfos.map( + info => + new AddressLookupTableAccount({ + key: new PublicKey(info.key), + state: AddressLookupTableAccount.deserialize(new Uint8Array(info.data)), + }), + ) + + const messageV0 = MessageV0.compile({ + payerKey: new PublicKey(sendAddress), + instructions, + // Blockhash doesn't affect size, use a dummy one + recentBlockhash: PublicKey.default.toString(), + addressLookupTableAccounts: lookupTableAccounts, + }) + const txBytes = new VersionedTransaction(messageV0).serialize() + return txBytes.length > 1232 + } catch { + return false + } + })() + const getFeeData = async () => { const sellAdapter = deps.assertGetSolanaChainAdapter(sellAsset.chainId) const getFeeDataInput: GetFeeDataInput = { @@ -248,6 +282,7 @@ export const getTradeQuote = async ( solanaTransactionMetadata: { addressLookupTableAddresses, instructions, + isOversized, }, feeData: { protocolFees, @@ -261,6 +296,14 @@ export const getTradeQuote = async ( allowanceContract: '0x0', // Swap are so fasts on solana that times are under 100ms displaying 0 or very small amount of time is not user friendly estimatedExecutionTimeMs: undefined, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: priceResponse.inAmount, + buyAmountCryptoBaseUnit: priceResponse.outAmount, + }), }, ], } diff --git a/packages/swapper/src/swappers/JupiterSwapper/swapperApi/getTradeRate.ts b/packages/swapper/src/swappers/JupiterSwapper/swapperApi/getTradeRate.ts index 282ce179cea..650cefd9803 100644 --- a/packages/swapper/src/swappers/JupiterSwapper/swapperApi/getTradeRate.ts +++ b/packages/swapper/src/swappers/JupiterSwapper/swapperApi/getTradeRate.ts @@ -25,6 +25,7 @@ import type { } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { getInputOutputRate, makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { SOLANA_RANDOM_ADDRESS, TOKEN_2022_PROGRAM_ID } from '../utils/constants' import { calculateAccountCreationCosts, @@ -239,6 +240,14 @@ export const getTradeRate = async ( allowanceContract: '0x0', // Swap are so fasts on solana that times are under 100ms displaying 0 or very small amount of time is not user friendly estimatedExecutionTimeMs: undefined, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: priceResponse.inAmount, + buyAmountCryptoBaseUnit: priceResponse.outAmount, + }), }, ], } diff --git a/packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts b/packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts index 4416f228f2f..30420d367d3 100644 --- a/packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts +++ b/packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts @@ -1,8 +1,9 @@ -import { CHAIN_NAMESPACE, fromAssetId } from '@shapeshiftoss/caip' +import { CHAIN_NAMESPACE, fromAssetId, nearChainId } from '@shapeshiftoss/caip' import { evm } from '@shapeshiftoss/chain-adapters' import { bn, bnOrZero, + chainIdToFeeAssetId, contractAddressOrUndefined, DAO_TREASURY_NEAR, isToken, @@ -25,6 +26,7 @@ import { getInputOutputRate, makeSwapErrorRight, } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { isNativeEvmAsset } from '../../utils/helpers/helpers' import { DEFAULT_QUOTE_DEADLINE_MS, DEFAULT_SLIPPAGE_BPS } from '../constants' import type { QuoteResponse } from '../types' @@ -368,6 +370,17 @@ export const getTradeQuote = async ( timeEstimate: quote.timeEstimate, deadline: quote.deadline ?? '', }, + affiliateFee: buildAffiliateFee({ + strategy: 'fixed_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: quote.amountIn, + buyAmountCryptoBaseUnit: quote.amountOut, + fixedAssetId: chainIdToFeeAssetId(nearChainId), + fixedAsset: deps.assetsById[chainIdToFeeAssetId(nearChainId)], + isEstimate: true, + }), }, ], } diff --git a/packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeRate.ts b/packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeRate.ts index 9513d776b4d..33ca21676c4 100644 --- a/packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeRate.ts +++ b/packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeRate.ts @@ -1,8 +1,9 @@ -import { CHAIN_NAMESPACE, fromAssetId, monadChainId } from '@shapeshiftoss/caip' +import { CHAIN_NAMESPACE, fromAssetId, monadChainId, nearChainId } from '@shapeshiftoss/caip' import { evm } from '@shapeshiftoss/chain-adapters' import { bn, bnOrZero, + chainIdToFeeAssetId, contractAddressOrUndefined, DAO_TREASURY_NEAR, isToken, @@ -28,6 +29,7 @@ import { makeSwapErrorRight, } from '../../../utils' import { simulateWithStateOverrides } from '../../../utils/tenderly' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { isNativeEvmAsset } from '../../utils/helpers/helpers' import { DEFAULT_QUOTE_DEADLINE_MS, DEFAULT_SLIPPAGE_BPS } from '../constants' import type { QuoteResponse } from '../types' @@ -419,6 +421,17 @@ export const getTradeRate = async ( sellAsset, source: SwapperName.NearIntents, estimatedExecutionTimeMs: quote.timeEstimate ? quote.timeEstimate * 1000 : undefined, + affiliateFee: buildAffiliateFee({ + strategy: 'fixed_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: quote.amountIn, + buyAmountCryptoBaseUnit: quote.amountOut, + fixedAssetId: chainIdToFeeAssetId(nearChainId), + fixedAsset: deps.assetsById[chainIdToFeeAssetId(nearChainId)], + isEstimate: true, + }), }, ], } diff --git a/packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts b/packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts index 51df697b98c..cfbb6966333 100644 --- a/packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts +++ b/packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts @@ -23,6 +23,7 @@ import type { } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { getInputOutputRate, makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { getTreasuryAddressFromChainId, isNativeEvmAsset } from '../../utils/helpers/helpers' import { chainIdToPortalsNetwork } from '../constants' import { fetchPortalsTradeOrder, PortalsError } from '../utils/fetchPortalsTradeOrder' @@ -276,6 +277,15 @@ export async function getPortalsTradeQuote( steps: portalsTradeOrderResponse.context.steps, route: portalsTradeOrderResponse.context.route, }, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: input.sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + isEstimate: true, + }), }, ] as SingleHopTradeQuoteSteps, } diff --git a/packages/swapper/src/swappers/RelaySwapper/utils/getTrade.ts b/packages/swapper/src/swappers/RelaySwapper/utils/getTrade.ts index 753bb641e74..ef434e55eb9 100644 --- a/packages/swapper/src/swappers/RelaySwapper/utils/getTrade.ts +++ b/packages/swapper/src/swappers/RelaySwapper/utils/getTrade.ts @@ -1,4 +1,5 @@ import { + baseChainId, btcChainId, fromChainId, monadChainId, @@ -10,6 +11,7 @@ import { evm, isEvmChainId } from '@shapeshiftoss/chain-adapters' import type { UtxoChainId } from '@shapeshiftoss/types' import { bnOrZero, + chainIdToFeeAssetId, convertBasisPointsToPercentage, convertDecimalPercentageToBasisPoints, convertPrecision, @@ -18,7 +20,12 @@ import { import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' import type { TransactionInstruction } from '@solana/web3.js' -import { PublicKey } from '@solana/web3.js' +import { + AddressLookupTableAccount, + MessageV0, + PublicKey, + VersionedTransaction, +} from '@solana/web3.js' import axios from 'axios' import type { Hex } from 'viem' import { getAddress, zeroAddress } from 'viem' @@ -34,6 +41,7 @@ import type { import { MixPanelEvent, SwapperName, TradeQuoteError } from '../../../types' import { getInputOutputRate, makeSwapErrorRight } from '../../../utils' import { simulateWithStateOverrides } from '../../../utils/tenderly' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { isNativeEvmAsset } from '../../utils/helpers/helpers' import type { chainIdToRelayChainId as relayChainMapImplementation } from '../constant' import { MAXIMUM_SUPPORTED_RELAY_STEPS, relayErrorCodeToTradeQuoteError } from '../constant' @@ -581,117 +589,165 @@ export async function getTrade({ return quote.fees.gas.amount })() - const steps = swapSteps.map((quoteStep): TradeQuoteStep | TradeRateStep => { - const selectedItem = quoteStep.items?.[0] - - if (!selectedItem) throw new Error('Relay quote step contains no items') - - // Add back relayer service and gas fees (relayer is including both) since they are downsides - // And add appFees - const buyAmountBeforeFeesCryptoBaseUnit = bnOrZero(currencyOut.amount) - .plus(relayerFeesBuyAssetCryptoBaseUnit) - .plus(appFeesBaseUnit) - .toFixed() - - const { allowanceContract, relayTransactionMetadata, solanaTransactionMetadata } = (() => { - if (!selectedItem.data) throw new Error('Relay quote step contains no data') + const steps = await Promise.all( + swapSteps.map(async (quoteStep): Promise => { + const selectedItem = quoteStep.items?.[0] - if (isRelayQuoteUtxoItemData(selectedItem.data)) { - if (!selectedItem.data.psbt) throw new Error('Relay BTC quote step contains no psbt') - - const relayer = getRelayPsbtRelayer( - selectedItem.data.psbt, - sellAmountIncludingProtocolFeesCryptoBaseUnit, - ) + if (!selectedItem) throw new Error('Relay quote step contains no items') - return { - allowanceContract: '', - relayTransactionMetadata: { - from: sendAddress, - psbt: selectedItem.data.psbt, - opReturnData: orderId, - to: relayer, - relayId: quote.steps[0].requestId, - orderId, - }, - solanaTransactionMetadata: undefined, - } - } + // Add back relayer service and gas fees (relayer is including both) since they are downsides + // And add appFees + const buyAmountBeforeFeesCryptoBaseUnit = bnOrZero(currencyOut.amount) + .plus(relayerFeesBuyAssetCryptoBaseUnit) + .plus(appFeesBaseUnit) + .toFixed() + + const { allowanceContract, relayTransactionMetadata, solanaTransactionMetadata } = + await (async () => { + if (!selectedItem.data) throw new Error('Relay quote step contains no data') + + if (isRelayQuoteUtxoItemData(selectedItem.data)) { + if (!selectedItem.data.psbt) throw new Error('Relay BTC quote step contains no psbt') + + const relayer = getRelayPsbtRelayer( + selectedItem.data.psbt, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + ) + + return { + allowanceContract: '', + relayTransactionMetadata: { + from: sendAddress, + psbt: selectedItem.data.psbt, + opReturnData: orderId, + to: relayer, + relayId: quote.steps[0].requestId, + orderId, + }, + solanaTransactionMetadata: undefined, + } + } - if (isRelayQuoteEvmItemData(selectedItem.data)) { - return { - allowanceContract: selectedItem.data?.to ?? '', - relayTransactionMetadata: { - from: sendAddress, - to: selectedItem.data?.to, - // v2 API may return value as undefined or empty object for ERC-20 swaps - default to '0' - value: typeof selectedItem.data?.value === 'string' ? selectedItem.data.value : '0', - data: selectedItem.data?.data, - // gas is not documented in the relay docs but refers to gasLimit - gasLimit: selectedItem.data?.gas, - chainId: Number(fromChainId(sellAsset.chainId).chainReference), - relayId: quote.steps[0].requestId, - orderId, - }, - solanaTransactionMetadata: undefined, - } - } + if (isRelayQuoteEvmItemData(selectedItem.data)) { + return { + allowanceContract: selectedItem.data?.to ?? '', + relayTransactionMetadata: { + from: sendAddress, + to: selectedItem.data?.to, + // v2 API may return value as undefined or empty object for ERC-20 swaps - default to '0' + value: typeof selectedItem.data?.value === 'string' ? selectedItem.data.value : '0', + data: selectedItem.data?.data, + // gas is not documented in the relay docs but refers to gasLimit + gasLimit: selectedItem.data?.gas, + chainId: Number(fromChainId(sellAsset.chainId).chainReference), + relayId: quote.steps[0].requestId, + orderId, + }, + solanaTransactionMetadata: undefined, + } + } - if (isRelayQuoteSolanaItemData(selectedItem.data)) { - return { - allowanceContract: '', - solanaTransactionMetadata: { - addressLookupTableAddresses: selectedItem.data?.addressLookupTableAddresses, - instructions: selectedItem.data?.instructions?.map(convertSolanaInstruction), - }, - relayTransactionMetadata: { - relayId: quote.steps[0].requestId, - orderId, - }, - } - } + if (isRelayQuoteSolanaItemData(selectedItem.data)) { + const solanaInstructions = + selectedItem.data?.instructions?.map(convertSolanaInstruction) + const addressLookupTableAddresses = selectedItem.data?.addressLookupTableAddresses + + // Build a trial VersionedTransaction to check if it exceeds the 1232-byte Solana tx size limit + const isOversized = await (async () => { + if (!solanaInstructions?.length || !sendAddress) return false + try { + const adapter = deps.assertGetSolanaChainAdapter(sellAsset.chainId) + const lookupTableInfos = await adapter.getAddressLookupTableAccounts( + addressLookupTableAddresses, + ) + const lookupTableAccounts = lookupTableInfos.map( + info => + new AddressLookupTableAccount({ + key: new PublicKey(info.key), + state: AddressLookupTableAccount.deserialize(new Uint8Array(info.data)), + }), + ) + + const messageV0 = MessageV0.compile({ + payerKey: new PublicKey(sendAddress), + instructions: solanaInstructions, + recentBlockhash: PublicKey.default.toString(), + addressLookupTableAccounts: lookupTableAccounts, + }) + const txBytes = new VersionedTransaction(messageV0).serialize() + return txBytes.length > 1232 + } catch { + return false + } + })() + + return { + allowanceContract: '', + solanaTransactionMetadata: { + addressLookupTableAddresses, + instructions: solanaInstructions, + isOversized, + }, + relayTransactionMetadata: { + relayId: quote.steps[0].requestId, + orderId, + }, + } + } - if (isRelayQuoteTronItemData(selectedItem.data)) { - return { - allowanceContract: '', - solanaTransactionMetadata: undefined, - relayTransactionMetadata: { - relayId: quote.steps[0].requestId, - orderId, - to: selectedItem.data?.parameter?.contract_address, - }, - } - } + if (isRelayQuoteTronItemData(selectedItem.data)) { + return { + allowanceContract: '', + solanaTransactionMetadata: undefined, + relayTransactionMetadata: { + relayId: quote.steps[0].requestId, + orderId, + to: selectedItem.data?.parameter?.contract_address, + }, + } + } - throw new Error('Relay quote step contains no data') - })() + throw new Error('Relay quote step contains no data') + })() - return { - allowanceContract, - rate, - buyAmountBeforeFeesCryptoBaseUnit, - buyAmountAfterFeesCryptoBaseUnit, - sellAmountIncludingProtocolFeesCryptoBaseUnit, - buyAsset, - sellAsset, - accountNumber, - feeData: { - networkFeeCryptoBaseUnit, - protocolFees: { - [protocolAssetId]: { - amountCryptoBaseUnit: quote.fees.relayer.amount, - asset: protocolAsset, - requiresBalance: false, + return { + allowanceContract, + rate, + buyAmountBeforeFeesCryptoBaseUnit, + buyAmountAfterFeesCryptoBaseUnit, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAsset, + sellAsset, + accountNumber, + feeData: { + networkFeeCryptoBaseUnit, + protocolFees: { + [protocolAssetId]: { + amountCryptoBaseUnit: quote.fees.relayer.amount, + asset: protocolAsset, + requiresBalance: false, + }, + ...appFeesAsProtocolFee, }, - ...appFeesAsProtocolFee, }, - }, - source: SwapperName.Relay, - estimatedExecutionTimeMs: timeEstimate * 1000, - solanaTransactionMetadata, - relayTransactionMetadata, - } - }) + source: SwapperName.Relay, + estimatedExecutionTimeMs: timeEstimate * 1000, + solanaTransactionMetadata, + relayTransactionMetadata, + affiliateFee: buildAffiliateFee({ + strategy: 'fixed_asset', + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + fixedAssetId: chainIdToFeeAssetId(baseChainId), + fixedAsset: deps.assetsById[chainIdToFeeAssetId(baseChainId)], + isEstimate: true, + }), + } + }), + ) const baseQuoteOrRate = { id: quote.steps[0].requestId, diff --git a/packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts b/packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts index f80c30c4c4b..0c941cd6c7d 100644 --- a/packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts +++ b/packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts @@ -5,6 +5,7 @@ import { SettlementMethod } from '@ston-fi/omniston-sdk' import type { CommonTradeQuoteInput, TradeQuote, TradeQuoteResult } from '../../../types' import { SwapperName, TradeQuoteError } from '../../../types' import { makeSwapErrorRight } from '../../../utils' +import { buildAffiliateFee } from '../../utils/affiliateFee' import { getTreasuryAddressFromChainId } from '../../utils/helpers/helpers' import type { OmnistonAssetAddress, StonfiTradeSpecific } from '../types' import { STONFI_DEFAULT_SLIPPAGE_BPS, STONFI_QUOTE_TIMEOUT_MS } from '../utils/constants' @@ -155,6 +156,15 @@ export const getTradeQuote = async (input: CommonTradeQuoteInput): Promise { + const { + strategy, + affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit, + buyAmountCryptoBaseUnit, + fixedAsset, + fixedAssetId, + isEstimate, + } = params + + if (bnOrZero(affiliateBps).lte(0)) return undefined + + const bpsDecimal = bn(affiliateBps).div(10000) + + switch (strategy) { + case 'buy_asset': { + const feeAmount = bnOrZero(buyAmountCryptoBaseUnit).times(bpsDecimal).toFixed(0) + return { + assetId: buyAsset.assetId, + amountCryptoBaseUnit: feeAmount, + asset: buyAsset, + isEstimate, + } + } + case 'sell_asset': { + const feeAmount = bnOrZero(sellAmountCryptoBaseUnit).times(bpsDecimal).toFixed(0) + return { + assetId: sellAsset.assetId, + amountCryptoBaseUnit: feeAmount, + asset: sellAsset, + isEstimate, + } + } + case 'fixed_asset': { + if (!fixedAsset || !fixedAssetId) return undefined + return { + assetId: fixedAssetId, + amountCryptoBaseUnit: '0', + asset: fixedAsset, + isEstimate: true, + } + } + default: + return undefined + } +} diff --git a/packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts b/packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts index cda8ec7234f..a3f2bb8fb2a 100644 --- a/packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts +++ b/packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts @@ -19,6 +19,7 @@ import { TronWeb } from 'tronweb' import { v4 as uuid } from 'uuid' import { getDefaultSlippageDecimalPercentageForSwapper } from '../index' +import { buildAffiliateFee } from '../swappers/utils/affiliateFee' import type { CommonTradeQuoteInput, GetEvmTradeQuoteInput, @@ -302,6 +303,14 @@ export const getL1RateOrQuote = async ( accountNumber, allowanceContract, feeData, + affiliateFee: buildAffiliateFee({ + strategy: 'buy_asset', + affiliateBps: route.affiliateBps, + sellAsset, + buyAsset, + sellAmountCryptoBaseUnit, + buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit, + }), thorchainSpecific: { maxStreamingQuantity: route.quote.max_streaming_quantity, }, diff --git a/packages/swapper/src/types.ts b/packages/swapper/src/types.ts index f8a60c75509..e5c1f785c7a 100644 --- a/packages/swapper/src/types.ts +++ b/packages/swapper/src/types.ts @@ -198,6 +198,7 @@ type CommonTradeInputBase = { buyAsset: Asset sellAmountIncludingProtocolFeesCryptoBaseUnit: string affiliateBps: string + affiliateAddress?: string allowMultiHop: boolean slippageTolerancePercentageDecimal?: string } @@ -387,6 +388,13 @@ export type SwapperDeps = { StarknetSwapperDeps & TonSwapperDeps +export type AffiliateFee = { + assetId: AssetId + amountCryptoBaseUnit: string + asset: Asset + isEstimate?: boolean +} + export type TradeQuoteStep = { buyAmountBeforeFeesCryptoBaseUnit: string buyAmountAfterFeesCryptoBaseUnit: string @@ -435,6 +443,8 @@ export type TradeQuoteStep = { solanaTransactionMetadata?: { addressLookupTableAddresses: string[] instructions?: TransactionInstruction[] + /** True when the serialized tx exceeds the 1232-byte Solana limit and needs Jito bundle splitting */ + isOversized?: boolean } cowswapQuoteResponse?: OrderQuoteResponse chainflipSpecific?: { @@ -504,6 +514,7 @@ export type TradeQuoteStep = { } acrossTransactionMetadata?: AcrossTransactionMetadata debridgeTransactionMetadata?: DebridgeTransactionMetadata + affiliateFee?: AffiliateFee } export type TradeRateStep = Omit & { @@ -681,6 +692,8 @@ export type CosmosSdkTransactionExecutionProps = { export type SolanaTransactionExecutionProps = { signAndBroadcastTransaction: (txToSign: SolanaSignTx) => Promise + /** Sign-only callback for Jito bundle flow (sign without broadcasting) */ + signTransaction?: (txToSign: SolanaSignTx) => Promise } export type SolanaMessageToSign = { diff --git a/packages/types/package.json b/packages/types/package.json index 51ad4c7155f..360eb29ced4 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/types", - "version": "8.6.6", + "version": "8.6.7", "description": "Common types shared across packages", "repository": "https://github.com/shapeshift/web", "license": "MIT", @@ -22,8 +22,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/packages/types/src/cowSwap.ts b/packages/types/src/cowSwap.ts index e2481fecb20..a1ca0c43f72 100644 --- a/packages/types/src/cowSwap.ts +++ b/packages/types/src/cowSwap.ts @@ -321,12 +321,22 @@ export enum CowNetwork { Avalanche = 'avalanche', Polygon = 'polygon', Bnb = 'bnb', + Plasma = 'plasma', + Linea = 'linea', + Ink = 'ink', } export type CowChainId = | KnownChainIds.EthereumMainnet | KnownChainIds.GnosisMainnet | KnownChainIds.ArbitrumMainnet + | KnownChainIds.BaseMainnet + | KnownChainIds.AvalancheMainnet + | KnownChainIds.PolygonMainnet + | KnownChainIds.BnbSmartChainMainnet + | KnownChainIds.PlasmaMainnet + | KnownChainIds.LineaMainnet + | KnownChainIds.InkMainnet export type TypedDataTypes = Record diff --git a/packages/unchained-client/package.json b/packages/unchained-client/package.json index 4599dcda140..b6d20a3930a 100644 --- a/packages/unchained-client/package.json +++ b/packages/unchained-client/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/unchained-client", - "version": "10.14.9-coderabbit-fix.2", + "version": "10.14.10", "repository": "https://github.com/shapeshift/web", "license": "MIT", "type": "module", @@ -21,12 +21,10 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm run generate && pnpm exec tsc --build && pnpm run postbuild", - "build:docker": "pnpm run clean:dist && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist src/generated", - "clean:dist": "rm -rf dist", + "build": "rm -rf dist && pnpm run generate && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist src/generated node_modules", "dev": "pnpm exec tsc --build --watch", - "generate": "TS_POST_PROCESS_FILE=./generator/post_process.sh JAVA_OPTS='-Dlog.level=error' openapi-generator-cli generate", + "generate": "rm -rf src/generated && TS_POST_PROCESS_FILE=./generator/post_process.sh JAVA_OPTS='-Dlog.level=error' openapi-generator-cli generate", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", "postbuild:cjs": "echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json" diff --git a/packages/utils/package.json b/packages/utils/package.json index 4fe15895434..45115752c37 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -21,8 +21,8 @@ "access": "public" }, "scripts": { - "build": "pnpm run clean && pnpm exec tsc --build && pnpm run postbuild", - "clean": "rm -rf dist", + "build": "rm -rf dist && pnpm exec tsc --build && pnpm run postbuild", + "clean": "rm -rf dist node_modules", "dev": "pnpm exec tsc --build --watch", "postbuild": "pnpm run postbuild:esm && pnpm run postbuild:cjs", "postbuild:esm": "pnpm exec tsc-esm-fix --target=dist/esm --ext=.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5636260fb6d..c9f59232e24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -285,6 +285,12 @@ importers: '@visx/xychart': specifier: ^3.12.0 version: 3.12.0(@react-spring/web@9.7.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@wallet-standard/app': + specifier: ^1.1.0 + version: 1.1.0 + '@wallet-standard/base': + specifier: ^1.1.0 + version: 1.1.0 '@walletconnect/core': specifier: ^2.20.2 version: 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) @@ -801,7 +807,32 @@ importers: version: 5.1.4(typescript@5.2.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) vitest: specifier: 3.0.9 - version: 3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0) + + packages/affiliate-dashboard: + dependencies: + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: 18.2.0 + version: 18.2.0(patch_hash=472f33b26781cbf66543b4c1cdf5be1c2ea4cd7232403bb255cd75e280bf3158)(react@18.3.1) + devDependencies: + '@types/react': + specifier: 19.1.2 + version: 19.1.2 + '@types/react-dom': + specifier: 19.1.2 + version: 19.1.2(@types/react@19.1.2) + '@vitejs/plugin-react': + specifier: ^4.0.0 + version: 4.7.0(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)) + typescript: + specifier: ^5.0.0 + version: 5.2.2 + vite: + specifier: ^5.0.0 + version: 5.4.21(@types/node@25.3.5)(terser@5.46.0) packages/caip: dependencies: @@ -813,7 +844,7 @@ importers: dependencies: '@mysten/sui': specifier: 1.45.2 - version: 1.45.2(typescript@5.2.2) + version: 1.45.2(typescript@5.8.2) '@near-js/crypto': specifier: ^2.5.1 version: 2.5.1(@near-js/types@2.5.1)(@near-js/utils@2.5.1(@near-js/types@2.5.1)) @@ -846,7 +877,7 @@ importers: version: link:../utils '@solana/spl-token': specifier: ^0.4.9 - version: 0.4.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10) + version: 0.4.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10) '@solana/web3.js': specifier: 1.98.0 version: 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -888,7 +919,7 @@ importers: version: 6.6.2 viem: specifier: 2.43.5 - version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) devDependencies: '@types/bs58check': specifier: ^2.1.0 @@ -928,7 +959,7 @@ importers: version: 4.17.23 viem: specifier: 2.43.5 - version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) devDependencies: '@types/lodash': specifier: 4.14.182 @@ -976,7 +1007,7 @@ importers: dependencies: '@shapeshiftoss/bitcoinjs-lib': specifier: 7.0.0-shapeshift.2 - version: 7.0.0-shapeshift.2(typescript@5.2.2) + version: 7.0.0-shapeshift.2(typescript@5.8.2) '@shapeshiftoss/proto-tx-builder': specifier: 0.10.0 version: 0.10.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -1099,7 +1130,7 @@ importers: devDependencies: vitest: specifier: 3.0.9 - version: 3.0.9(@types/debug@4.1.12)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.27.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.27.2)(terser@5.46.0) packages/hdwallet-keepkey: dependencies: @@ -1120,7 +1151,7 @@ importers: version: 7.0.3 '@shapeshiftoss/bitcoinjs-lib': specifier: 7.0.0-shapeshift.2 - version: 7.0.0-shapeshift.2(typescript@5.2.2) + version: 7.0.0-shapeshift.2(typescript@5.8.2) '@shapeshiftoss/hdwallet-core': specifier: workspace:^ version: link:../hdwallet-core @@ -1243,8 +1274,8 @@ importers: specifier: workspace:^ version: link:../hdwallet-keepkey axios: - specifier: ^0.21.1 - version: 0.21.4 + specifier: ^1.13.5 + version: 1.13.6(debug@4.4.3) packages/hdwallet-keepkey-webusb: dependencies: @@ -1288,7 +1319,7 @@ importers: devDependencies: '@keplr-wallet/types': specifier: ^0.12.35 - version: 0.12.313(starknet@9.4.0(typescript@5.2.2)(zod@3.25.76)) + version: 0.12.313(starknet@9.4.0(typescript@5.8.2)(zod@3.25.76)) '@types/lodash': specifier: 4.14.182 version: 4.14.182 @@ -1333,7 +1364,7 @@ importers: version: 0.7.0 '@shapeshiftoss/bitcoinjs-lib': specifier: 7.0.0-shapeshift.2 - version: 7.0.0-shapeshift.2(typescript@5.2.2) + version: 7.0.0-shapeshift.2(typescript@5.8.2) '@shapeshiftoss/hdwallet-core': specifier: workspace:^ version: link:../hdwallet-core @@ -1437,12 +1468,18 @@ importers: packages/hdwallet-metamask-multichain: dependencies: + '@bitcoinerlab/secp256k1': + specifier: ^1.1.1 + version: 1.2.0 '@metamask/detect-provider': specifier: ^1.2.0 version: 1.2.0 '@metamask/onboarding': specifier: ^1.0.1 version: 1.0.1 + '@shapeshiftoss/bitcoinjs-lib': + specifier: 7.0.0-shapeshift.2 + version: 7.0.0-shapeshift.2(typescript@5.8.2) '@shapeshiftoss/common-api': specifier: ^9.3.0 version: 9.3.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -1451,10 +1488,22 @@ importers: version: link:../hdwallet-core '@shapeshiftoss/metamask-snaps-adapter': specifier: ^1.0.12 - version: 1.0.13(211af8fc05d26bebb634bbf1f0177cc6) + version: 1.0.13(ffe613154a69d26e0f11f7fab9dfa120) '@shapeshiftoss/metamask-snaps-types': specifier: ^1.0.12 - version: 1.0.13(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 1.0.13(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@solana/web3.js': + specifier: 1.98.0 + version: 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@wallet-standard/app': + specifier: ^1.1.0 + version: 1.1.0 + '@wallet-standard/base': + specifier: ^1.1.0 + version: 1.1.0 + '@wallet-standard/features': + specifier: ^1.1.0 + version: 1.1.0 eth-rpc-errors: specifier: ^4.0.3 version: 4.0.3 @@ -1466,7 +1515,7 @@ importers: version: 4.17.23 mipd: specifier: ^0.0.7 - version: 0.0.7(typescript@5.2.2) + version: 0.0.7(typescript@5.8.2) devDependencies: '@types/express': specifier: ^4.17.17 @@ -1649,7 +1698,7 @@ importers: dependencies: '@shapeshiftoss/bitcoinjs-lib': specifier: 7.0.0-shapeshift.2 - version: 7.0.0-shapeshift.2(typescript@5.2.2) + version: 7.0.0-shapeshift.2(typescript@5.8.2) '@shapeshiftoss/hdwallet-core': specifier: workspace:^ version: link:../hdwallet-core @@ -1749,7 +1798,7 @@ importers: version: 0.8.0-beta.0 web3: specifier: 4.2.1-dev.a0d6730.0 - version: 4.2.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + version: 4.2.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) devDependencies: '@types/jquery': specifier: ^3.5.22 @@ -1765,7 +1814,7 @@ importers: dependencies: '@mysten/sui': specifier: 1.45.2 - version: 1.45.2(typescript@5.2.2) + version: 1.45.2(typescript@5.8.2) '@shapeshiftoss/hdwallet-core': specifier: workspace:^ version: link:../hdwallet-core @@ -1823,7 +1872,7 @@ importers: version: link:../hdwallet-trezor '@trezor/connect-web': specifier: ^9.6.4 - version: 9.7.2(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.2.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + version: 9.7.2(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) packages/hdwallet-vultisig: dependencies: @@ -1835,7 +1884,7 @@ importers: version: 0.28.13(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@shapeshiftoss/bitcoinjs-lib': specifier: 7.0.0-shapeshift.2 - version: 7.0.0-shapeshift.2(typescript@5.2.2) + version: 7.0.0-shapeshift.2(typescript@5.8.2) '@shapeshiftoss/hdwallet-core': specifier: workspace:^ version: link:../hdwallet-core @@ -1866,13 +1915,13 @@ importers: version: 1.2.0 '@shapeshiftoss/bitcoinjs-lib': specifier: 7.0.0-shapeshift.2 - version: 7.0.0-shapeshift.2(typescript@5.2.2) + version: 7.0.0-shapeshift.2(typescript@5.8.2) '@shapeshiftoss/hdwallet-core': specifier: workspace:^ version: link:../hdwallet-core '@walletconnect/ethereum-provider': specifier: ^2.20.2 - version: 2.23.7(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.23.7(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/modal': specifier: ^2.6.2 version: 2.7.0(@types/react@19.1.2)(react@19.2.4) @@ -1949,6 +1998,31 @@ importers: packages/swap-widget: dependencies: + '@shapeshiftoss/caip': + specifier: ^8.0.0 + version: 8.16.8 + '@shapeshiftoss/types': + specifier: ^8.0.0 + version: 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/utils': + specifier: ^1.0.0 + version: 1.0.6(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@xstate/react': + specifier: 5.0.5 + version: 5.0.5(@types/react@19.1.2)(react@19.2.4)(xstate@5.28.0) + bech32: + specifier: ^2.0.0 + version: 2.0.0 + p-queue: + specifier: ^6.6.2 + version: 6.6.2 + react-virtuoso: + specifier: 4.7.11 + version: 4.7.11(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + xstate: + specifier: 5.28.0 + version: 5.28.0 + devDependencies: '@reown/appkit': specifier: ^1.8.17 version: 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) @@ -1961,15 +2035,6 @@ importers: '@reown/appkit-adapter-wagmi': specifier: ^1.8.17 version: 1.8.18(b2de09fc1a71c9091ac547a86f40bdf9) - '@shapeshiftoss/caip': - specifier: workspace:^ - version: link:../caip - '@shapeshiftoss/types': - specifier: workspace:^ - version: link:../types - '@shapeshiftoss/utils': - specifier: workspace:^ - version: link:../utils '@solana/wallet-adapter-wallets': specifier: ^0.19.32 version: 0.19.37(@babel/runtime@7.28.6)(@sentry/types@8.26.0)(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@types/react@19.1.2)(bs58@6.0.0)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tslib@2.8.1)(typescript@5.2.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) @@ -1979,28 +2044,6 @@ importers: '@tanstack/react-query': specifier: 5.69.0 version: 5.69.0(react@19.2.4) - '@xstate/react': - specifier: 5.0.5 - version: 5.0.5(@types/react@19.1.2)(react@19.2.4)(xstate@5.28.0) - bech32: - specifier: ^2.0.0 - version: 2.0.0 - p-queue: - specifier: ^6.6.2 - version: 6.6.2 - react-virtuoso: - specifier: 4.7.11 - version: 4.7.11(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - viem: - specifier: 2.40.3 - version: 2.40.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: - specifier: 3.3.2 - version: 3.3.2(7ca313f9f1f7d2a7af3844c31bba492f) - xstate: - specifier: 5.28.0 - version: 5.28.0 - devDependencies: '@testing-library/jest-dom': specifier: 6.9.1 version: 6.9.1 @@ -2028,15 +2071,24 @@ importers: typescript: specifier: ~5.2.2 version: 5.2.2 + viem: + specifier: 2.40.3 + version: 2.40.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) vite: specifier: ^5.0.0 version: 5.4.21(@types/node@25.3.5)(terser@5.46.0) + vite-plugin-dts: + specifier: ^4.5.4 + version: 4.5.4(@types/node@25.3.5)(rollup@4.59.0)(typescript@5.2.2)(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)) vite-plugin-node-polyfills: specifier: 0.23.0 version: 0.23.0(rollup@4.59.0)(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)) vitest: specifier: 4.0.18 version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + wagmi: + specifier: 3.3.2 + version: 3.3.2(7ca313f9f1f7d2a7af3844c31bba492f) packages/swapper: dependencies: @@ -2045,7 +2097,7 @@ importers: version: 4.0.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@avnu/avnu-sdk': specifier: ^4.0.1 - version: 4.0.1(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(starknet@9.4.0(typescript@5.2.2)(zod@3.25.76)) + version: 4.0.1(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(starknet@9.4.0(typescript@5.8.2)(zod@3.25.76)) '@coral-xyz/anchor': specifier: 0.29.0 version: 0.29.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -2060,10 +2112,10 @@ importers: version: 6.0.30 '@mysten/sui': specifier: 1.45.2 - version: 1.45.2(typescript@5.2.2) + version: 1.45.2(typescript@5.8.2) '@shapeshiftoss/bitcoinjs-lib': specifier: 7.0.0-shapeshift.0 - version: 7.0.0-shapeshift.0(typescript@5.2.2) + version: 7.0.0-shapeshift.0(typescript@5.8.2) '@shapeshiftoss/caip': specifier: workspace:^ version: link:../caip @@ -2099,7 +2151,7 @@ importers: version: 5.9.0 '@uniswap/v3-sdk': specifier: ^3.13.1 - version: 3.28.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) + version: 3.28.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)) axios: specifier: ^1.13.5 version: 1.13.6(debug@4.4.3) @@ -2141,7 +2193,7 @@ importers: version: 9.0.1 viem: specifier: 2.43.5 - version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) devDependencies: '@types/lodash': specifier: 4.14.182 @@ -2172,7 +2224,7 @@ importers: version: ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) viem: specifier: 2.43.5 - version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) packages/unchained-client: dependencies: @@ -2205,7 +2257,7 @@ importers: version: 4.0.1(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) viem: specifier: 2.43.5 - version: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + version: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) ws: specifier: ^8.17.1 version: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) @@ -4921,6 +4973,19 @@ packages: '@metaplex-foundation/mpl-token-metadata@2.13.0': resolution: {integrity: sha512-Fl/8I0L9rv4bKTV/RAl5YIbJe9SnQPInKvLz+xR1fEc4/VQkuCn3RPgypfUMEKWmCznzaw4sApDxy6CFS4qmJw==} + '@microsoft/api-extractor-model@7.33.4': + resolution: {integrity: sha512-u1LTaNTikZAQ9uK6KG1Ms7nvNedsnODnspq/gH2dcyETWvH4hVNGNDvRAEutH66kAmxA4/necElqGNs1FggC8w==} + + '@microsoft/api-extractor@7.57.7': + resolution: {integrity: sha512-kmnmVs32MFWbV5X6BInC1/TfCs7y1ugwxv1xHsAIj/DyUfoe7vtO0alRUgbQa57+yRGHBBjlNcEk33SCAt5/dA==} + hasBin: true + + '@microsoft/tsdoc-config@0.18.1': + resolution: {integrity: sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==} + + '@microsoft/tsdoc@0.16.0': + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@mixpanel/rrdom@2.0.0-alpha.18.3': resolution: {integrity: sha512-FpQ/WJkVgb0kF49ebqtqf5F7dsqU/o9CfzPR8BAafzVQkieaPCRBFyLh8CDCtKKY0k8DJRqcamj388MLd6QJpQ==} @@ -6039,6 +6104,36 @@ packages: '@rushstack/eslint-patch@1.16.1': resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} + '@rushstack/node-core-library@5.20.3': + resolution: {integrity: sha512-95JgEPq2k7tHxhF9/OJnnyHDXfC9cLhhta0An/6MlkDsX2A6dTzDrTUG18vx4vjc280V0fi0xDH9iQczpSuWsw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/problem-matcher@0.2.1': + resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.7.2': + resolution: {integrity: sha512-9XbFWuqMYcHUso4mnETfhGVUSaADBRj6HUAAEYk50nMPn8WRICmBuCphycQGNB3duIR6EEZX3Xj3SYc2XiP+9A==} + + '@rushstack/terminal@0.22.3': + resolution: {integrity: sha512-gHC9pIMrUPzAbBiI4VZMU7Q+rsCzb8hJl36lFIulIzoceKotyKL3Rd76AZ2CryCTKEg+0bnTj406HE5YY5OQvw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@5.3.3': + resolution: {integrity: sha512-c+ltdcvC7ym+10lhwR/vWiOhsrm/bP3By2VsFcs5qTKv+6tTmxgbVrtJ5NdNjANiV5TcmOZgUN+5KYQ4llsvEw==} + '@safe-global/safe-apps-provider@0.18.6': resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} @@ -7570,6 +7665,9 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -8442,6 +8540,35 @@ packages: '@vitest/utils@4.0.18': resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vue/compiler-core@3.5.30': + resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==} + + '@vue/compiler-dom@3.5.30': + resolution: {integrity: sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/language-core@2.2.0': + resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/shared@3.5.30': + resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==} + '@wagmi/connectors@6.2.0': resolution: {integrity: sha512-2NfkbqhNWdjfibb4abRMrn7u6rPjEGolMfApXss6HCDVt9AW2oVC6k8Q5FouzpJezElxLJSagWz9FW1zaRlanA==} peerDependencies: @@ -9070,6 +9197,14 @@ packages: peerDependencies: zod: 3.25.76 + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -9078,6 +9213,14 @@ packages: ajv: optional: true + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: @@ -9103,6 +9246,9 @@ packages: resolution: {integrity: sha512-9moZxdqeJ6GdE4N6fA/GlUP4LrbLZMYcYkt141J4Ss68OfEgH9qW0wBuZ3ZOKEx/xjc5bg7mLP2Gjg7nwrkmww==} engines: {node: '>=14.0.0'} + alien-signals@0.4.14: + resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -10153,6 +10299,12 @@ packages: engines: {node: '>=18'} hasBin: true + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} @@ -10479,6 +10631,9 @@ packages: dayjs@1.11.19: resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -10664,6 +10819,10 @@ packages: resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} + diff@8.0.3: + resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} + engines: {node: '>=0.3.1'} + diffie-hellman@5.0.3: resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} @@ -11444,6 +11603,9 @@ packages: resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} engines: {node: '>= 0.10.0'} + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + ext@1.7.0: resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} @@ -12156,6 +12318,10 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -12547,6 +12713,9 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + jose@4.15.9: resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} @@ -12761,6 +12930,9 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -12844,6 +13016,10 @@ packages: resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} + local-pkg@1.1.2: + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} + engines: {node: '>=14'} + localforage@1.10.0: resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} @@ -13255,6 +13431,10 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@10.2.3: + resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} + engines: {node: 18 || 20 || >=22} + minimatch@10.2.4: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} @@ -13266,6 +13446,10 @@ packages: resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} engines: {node: '>=10'} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -13311,6 +13495,9 @@ packages: engines: {node: '>=10'} hasBin: true + mlly@1.8.1: + resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==} + mnemonist@0.38.5: resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} @@ -13362,6 +13549,9 @@ packages: resolution: {integrity: sha512-K7lOQoYqhGhTSChsmHMQbf/SDCsxh/m0uhN6Ipt206lGoe81fpTmaGD0KLh4jUxCONMOUnwCSj0jtX2CM4pEdw==} hasBin: true + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + multibase@4.0.6: resolution: {integrity: sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ==} engines: {node: '>=12.0.0', npm: '>=6.0.0'} @@ -14011,6 +14201,12 @@ packages: resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} engines: {node: '>=10'} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + playwright-core@1.58.2: resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} engines: {node: '>=18'} @@ -14268,6 +14464,9 @@ packages: resolution: {integrity: sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==} engines: {node: '>=0.6'} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + query-string@6.13.5: resolution: {integrity: sha512-svk3xg9qHR39P3JlHuD7g3nRnyay5mHbrPctEBDUxUkHRifPHXJDhBUycdCC0NBjXoDf44Gb+IsOZL1Uwn8M/Q==} engines: {node: '>=6'} @@ -14996,6 +15195,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + semver@7.7.1: resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} @@ -15344,6 +15548,10 @@ packages: resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} engines: {node: '>=4'} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + string-natural-compare@3.0.1: resolution: {integrity: sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==} @@ -15917,6 +16125,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + typeson-registry@1.0.0-alpha.39: resolution: {integrity: sha512-NeGDEquhw+yfwNhguLPcZ9Oj0fzbADiX4R0WxvoY8nGhy98IbzQy1sezjoEFWOywOboj/DWehI+/aUlRVrJnnw==} engines: {node: '>=10.0.0'} @@ -16459,6 +16672,15 @@ packages: vue-tsc: optional: true + vite-plugin-dts@4.5.4: + resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==} + peerDependencies: + typescript: '*' + vite: '*' + peerDependenciesMeta: + vite: + optional: true + vite-plugin-node-polyfills@0.23.0: resolution: {integrity: sha512-4n+Ys+2bKHQohPBKigFlndwWQ5fFKwaGY6muNDMTb0fSQLyBzS+jjUNRZG9sKF0S/Go4ApG6LFnUGopjkILg3w==} peerDependencies: @@ -17192,6 +17414,12 @@ snapshots: graphql: 16.13.0 typescript: 5.2.2 + '@0no-co/graphqlsp@1.15.2(graphql@16.13.0)(typescript@5.8.2)': + dependencies: + '@gql.tada/internal': 1.0.8(graphql@16.13.0)(typescript@5.8.2) + graphql: 16.13.0 + typescript: 5.8.2 + '@acemir/cssom@0.9.31': {} '@adobe/css-tools@4.4.4': {} @@ -17293,13 +17521,13 @@ snapshots: openapi3-ts: 4.5.0 zod: 3.25.76 - '@avnu/avnu-sdk@4.0.1(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(starknet@9.4.0(typescript@5.2.2)(zod@3.25.76))': + '@avnu/avnu-sdk@4.0.1(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(starknet@9.4.0(typescript@5.8.2)(zod@3.25.76))': dependencies: dayjs: 1.11.19 ethers: 6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) moment: 2.30.1 qs: 6.15.0 - starknet: 9.4.0(typescript@5.2.2)(zod@3.25.76) + starknet: 9.4.0(typescript@5.8.2)(zod@3.25.76) zod: 3.25.76 '@babel/code-frame@7.29.0': @@ -17323,7 +17551,7 @@ snapshots: '@babel/types': 7.29.0 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -17383,7 +17611,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.11 transitivePeerDependencies: @@ -18166,7 +18394,7 @@ snapshots: '@babel/parser': 7.29.0 '@babel/template': 7.28.6 '@babel/types': 7.29.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18224,6 +18452,31 @@ snapshots: - utf-8-validate - zod + '@base-org/account@2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@coinbase/cdp-sdk': 1.44.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.8.2)(zod@3.25.76) + preact: 10.24.2 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + optional: true + '@bitcoinerlab/secp256k1@1.2.0': dependencies: '@noble/curves': 1.9.7 @@ -18492,6 +18745,29 @@ snapshots: - typescript - utf-8-validate + '@coinbase/cdp-sdk@1.44.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10)': + dependencies: + '@solana-program/system': 0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10)) + '@solana-program/token': 0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10)) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + abitype: 1.0.6(typescript@5.8.2)(zod@3.25.76) + axios: 1.13.6(debug@4.4.3) + axios-retry: 4.5.0(axios@1.13.6) + jose: 6.1.3 + md5: 2.3.0 + uncrypto: 0.1.3 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + optional: true + '@coinbase/wallet-sdk@3.9.3': dependencies: bn.js: 5.2.3 @@ -19408,7 +19684,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.14.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 @@ -20229,12 +20505,25 @@ snapshots: graphql: 16.13.0 typescript: 5.2.2 + '@gql.tada/cli-utils@1.7.2(@0no-co/graphqlsp@1.15.2(graphql@16.13.0)(typescript@5.2.2))(graphql@16.13.0)(typescript@5.8.2)': + dependencies: + '@0no-co/graphqlsp': 1.15.2(graphql@16.13.0)(typescript@5.2.2) + '@gql.tada/internal': 1.0.8(graphql@16.13.0)(typescript@5.8.2) + graphql: 16.13.0 + typescript: 5.8.2 + '@gql.tada/internal@1.0.8(graphql@16.13.0)(typescript@5.2.2)': dependencies: '@0no-co/graphql.web': 1.2.0(graphql@16.13.0) graphql: 16.13.0 typescript: 5.2.2 + '@gql.tada/internal@1.0.8(graphql@16.13.0)(typescript@5.8.2)': + dependencies: + '@0no-co/graphql.web': 1.2.0(graphql@16.13.0) + graphql: 16.13.0 + typescript: 5.8.2 + '@graphql-typed-document-node/core@3.2.0(graphql@16.13.0)': dependencies: graphql: 16.13.0 @@ -20242,7 +20531,7 @@ snapshots: '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -20527,6 +20816,11 @@ snapshots: long: 4.0.0 starknet: 9.4.0(typescript@5.2.2)(zod@3.25.76) + '@keplr-wallet/types@0.12.313(starknet@9.4.0(typescript@5.8.2)(zod@3.25.76))': + dependencies: + long: 4.0.0 + starknet: 9.4.0(typescript@5.8.2)(zod@3.25.76) + '@keystonehq/alias-sampling@0.1.2': {} '@keystonehq/bc-ur-registry-sol@0.9.5': @@ -20574,7 +20868,7 @@ snapshots: '@kwsites/file-exists@1.1.1': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -20976,12 +21270,12 @@ snapshots: '@metamask/detect-provider@2.0.0': {} - '@metamask/eslint-config@15.0.0(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-jsdoc@50.8.0(eslint@8.57.1))(eslint-plugin-prettier@5.0.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3))(eslint-plugin-promise@7.2.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3)': + '@metamask/eslint-config@15.0.0(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-jsdoc@50.8.0(eslint@8.57.1))(eslint-plugin-prettier@5.0.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3))(eslint-plugin-promise@7.2.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3)': dependencies: '@eslint/js': 9.39.3 eslint: 8.57.1 eslint-config-prettier: 9.1.2(eslint@8.57.1) - eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) eslint-plugin-jsdoc: 50.8.0(eslint@8.57.1) eslint-plugin-prettier: 5.0.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3) eslint-plugin-promise: 7.2.1(eslint@8.57.1) @@ -21316,7 +21610,7 @@ snapshots: '@scure/base': 1.1.9 '@types/debug': 4.1.12 '@types/lodash': 4.14.182 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) lodash: 4.17.23 pony-cause: 2.1.11 semver: 7.7.4 @@ -21328,7 +21622,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) semver: 7.7.4 superstruct: 1.0.4 transitivePeerDependencies: @@ -21363,7 +21657,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) pony-cause: 2.1.11 semver: 7.7.4 uuid: 9.0.1 @@ -21377,7 +21671,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.1.9 '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) pony-cause: 2.1.11 semver: 7.7.4 uuid: 9.0.1 @@ -21389,7 +21683,7 @@ snapshots: '@metaplex-foundation/beet': 0.7.1 '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bs58: 5.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - encoding @@ -21401,7 +21695,7 @@ snapshots: '@metaplex-foundation/beet': 0.7.1 '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bs58: 5.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - encoding @@ -21413,7 +21707,7 @@ snapshots: '@metaplex-foundation/beet': 0.7.1 '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bs58: 5.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - encoding @@ -21424,7 +21718,7 @@ snapshots: dependencies: ansicolors: 0.3.2 bn.js: 5.2.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -21432,7 +21726,7 @@ snapshots: dependencies: ansicolors: 0.3.2 bn.js: 5.2.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -21440,7 +21734,7 @@ snapshots: dependencies: ansicolors: 0.3.2 bn.js: 5.2.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -21465,7 +21759,7 @@ snapshots: bn.js: 5.2.3 bs58: 5.0.0 buffer: 6.0.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) eventemitter3: 4.0.7 lodash.clonedeep: 4.5.0 lodash.isequal: 4.5.0 @@ -21565,7 +21859,7 @@ snapshots: '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) bn.js: 5.2.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - encoding @@ -21574,6 +21868,42 @@ snapshots: - typescript - utf-8-validate + '@microsoft/api-extractor-model@7.33.4(@types/node@25.3.5)': + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@rushstack/node-core-library': 5.20.3(@types/node@25.3.5) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.57.7(@types/node@25.3.5)': + dependencies: + '@microsoft/api-extractor-model': 7.33.4(@types/node@25.3.5) + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@rushstack/node-core-library': 5.20.3(@types/node@25.3.5) + '@rushstack/rig-package': 0.7.2 + '@rushstack/terminal': 0.22.3(@types/node@25.3.5) + '@rushstack/ts-command-line': 5.3.3(@types/node@25.3.5) + diff: 8.0.3 + lodash: 4.17.23 + minimatch: 10.2.3 + resolve: 1.22.11 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/tsdoc-config@0.18.1': + dependencies: + '@microsoft/tsdoc': 0.16.0 + ajv: 8.18.0 + jju: 1.4.0 + resolve: 1.22.11 + + '@microsoft/tsdoc@0.16.0': {} + '@mixpanel/rrdom@2.0.0-alpha.18.3': dependencies: '@mixpanel/rrweb-snapshot': 2.0.0-alpha.18.3 @@ -21775,7 +22105,7 @@ snapshots: dependencies: '@open-draft/until': 1.0.3 '@xmldom/xmldom': 0.7.13 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) headers-utils: 3.0.2 outvariant: 1.4.3 strict-event-emitter: 0.2.8 @@ -21824,6 +22154,28 @@ snapshots: - '@gql.tada/vue-support' - typescript + '@mysten/sui@1.45.2(typescript@5.8.2)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.0) + '@mysten/bcs': 1.9.2 + '@mysten/utils': 0.2.0 + '@noble/curves': 1.9.4 + '@noble/hashes': 1.8.0 + '@protobuf-ts/grpcweb-transport': 2.11.1 + '@protobuf-ts/runtime': 2.11.1 + '@protobuf-ts/runtime-rpc': 2.11.1 + '@scure/base': 1.2.6 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + gql.tada: 1.9.0(graphql@16.13.0)(typescript@5.8.2) + graphql: 16.13.0 + poseidon-lite: 0.2.1 + valibot: 1.2.0(typescript@5.8.2) + transitivePeerDependencies: + - '@gql.tada/svelte-support' + - '@gql.tada/vue-support' + - typescript + '@mysten/utils@0.2.0': dependencies: '@scure/base': 1.2.6 @@ -22869,6 +23221,7 @@ snapshots: - typescript - utf-8-validate - zod + optional: true '@reown/appkit-common@1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: @@ -22881,6 +23234,17 @@ snapshots: - utf-8-validate - zod + '@reown/appkit-common@1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + '@reown/appkit-common@1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: big.js: 6.2.2 @@ -22996,6 +23360,7 @@ snapshots: - uploadthing - utf-8-validate - zod + optional: true '@reown/appkit-controllers@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: @@ -23032,125 +23397,13 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) - viem: 2.46.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - - '@reown/appkit-pay@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - lit: 3.3.0 - valtio: 1.13.2(@types/react@19.1.2)(react@19.2.4) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - - '@reown/appkit-pay@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - lit: 3.3.0 - valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - debug - - encoding - - fastestsmallesttextencoderdecoder - - immer - - ioredis - - react - - typescript - - uploadthing - - use-sync-external-store - - utf-8-validate - - zod - - '@reown/appkit-pay@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-controllers@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - lit: 3.3.0 + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23171,82 +23424,21 @@ snapshots: - aws4fetch - bufferutil - db0 - - debug - encoding - - fastestsmallesttextencoderdecoder - - immer - ioredis - react - typescript - uploadthing - - use-sync-external-store - utf-8-validate - zod - '@reown/appkit-pay@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-controllers@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - lit: 3.3.0 + '@reown/appkit-wallet': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - debug - - encoding - - fastestsmallesttextencoderdecoder - - immer - - ioredis - - react - - typescript - - uploadthing - - use-sync-external-store - - utf-8-validate - - zod - - '@reown/appkit-polyfills@1.7.2': - dependencies: - buffer: 6.0.3 - - '@reown/appkit-polyfills@1.7.8': - dependencies: - buffer: 6.0.3 - - '@reown/appkit-polyfills@1.8.17-wc-circular-dependencies-fix.0': - dependencies: - buffer: 6.0.3 - - '@reown/appkit-polyfills@1.8.18': - dependencies: - buffer: 6.0.3 - - '@reown/appkit-scaffold-ui@1.7.2(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.2(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.2(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) - lit: 3.1.0 + viem: 2.46.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23273,17 +23465,16 @@ snapshots: - typescript - uploadthing - utf-8-validate - - valtio - zod - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-pay@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) lit: 3.3.0 + valtio: 1.13.2(@types/react@19.1.2)(react@19.2.4) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23310,18 +23501,16 @@ snapshots: - typescript - uploadthing - utf-8-validate - - valtio - zod - '@reown/appkit-scaffold-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-pay@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) lit: 3.3.0 + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23352,18 +23541,17 @@ snapshots: - uploadthing - use-sync-external-store - utf-8-validate - - valtio - zod + optional: true - '@reown/appkit-scaffold-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-pay@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-pay': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) lit: 3.3.0 + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23394,17 +23582,313 @@ snapshots: - uploadthing - use-sync-external-store - utf-8-validate - - valtio - zod - '@reown/appkit-scaffold-ui@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-pay@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-wallet': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + lit: 3.3.0 + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-pay@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + lit: 3.3.0 + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-polyfills@1.7.2': + dependencies: + buffer: 6.0.3 + + '@reown/appkit-polyfills@1.7.8': + dependencies: + buffer: 6.0.3 + + '@reown/appkit-polyfills@1.8.17-wc-circular-dependencies-fix.0': + dependencies: + buffer: 6.0.3 + + '@reown/appkit-polyfills@1.8.18': + dependencies: + buffer: 6.0.3 + + '@reown/appkit-scaffold-ui@1.7.2(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.2(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.2(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + lit: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - valtio + - zod + + '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - valtio + - zod + + '@reown/appkit-scaffold-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - valtio + - zod + optional: true + + '@reown/appkit-scaffold-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - valtio + - zod + + '@reown/appkit-scaffold-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - valtio + - zod + + '@reown/appkit-scaffold-ui@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) lit: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -23544,6 +24028,7 @@ snapshots: - uploadthing - utf-8-validate - zod + optional: true '@reown/appkit-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: @@ -23581,6 +24066,42 @@ snapshots: - utf-8-validate - zod + '@reown/appkit-ui@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@phosphor-icons/webcomponents': 2.1.5 + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + '@reown/appkit-ui@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@phosphor-icons/webcomponents': 2.1.5 @@ -23655,16 +24176,59 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-utils@1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@walletconnect/logger': 2.1.2 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + valtio: 1.13.2(@types/react@19.1.2)(react@19.2.4) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-utils@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) - '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - valtio: 1.13.2(@types/react@19.1.2)(react@19.2.4) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17-wc-circular-dependencies-fix.0 + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@wallet-standard/wallet': 1.1.0 + '@walletconnect/logger': 3.0.2 + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23685,29 +24249,34 @@ snapshots: - aws4fetch - bufferutil - db0 + - debug - encoding + - fastestsmallesttextencoderdecoder + - immer - ioredis - react - typescript - uploadthing + - use-sync-external-store - utf-8-validate - zod + optional: true - '@reown/appkit-utils@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-utils@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-polyfills': 1.8.17-wc-circular-dependencies-fix.0 - '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) '@wallet-standard/wallet': 1.1.0 '@walletconnect/logger': 3.0.2 - '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) optionalDependencies: - '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23740,21 +24309,21 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-utils@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.8.17-wc-circular-dependencies-fix.0 - '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) '@wallet-standard/wallet': 1.1.0 '@walletconnect/logger': 3.0.2 - '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: - '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23866,6 +24435,7 @@ snapshots: - bufferutil - typescript - utf-8-validate + optional: true '@reown/appkit-wallet@1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: @@ -23878,6 +24448,17 @@ snapshots: - typescript - utf-8-validate + '@reown/appkit-wallet@1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17-wc-circular-dependencies-fix.0 + '@walletconnect/logger': 3.0.2 + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + '@reown/appkit-wallet@1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -24022,6 +24603,7 @@ snapshots: - use-sync-external-store - utf-8-validate - zod + optional: true '@reown/appkit@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: @@ -24072,6 +24654,55 @@ snapshots: - utf-8-validate - zod + '@reown/appkit@1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.17-wc-circular-dependencies-fix.0 + '@reown/appkit-scaffold-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-ui': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(valtio@2.1.7(@types/react@19.1.2)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.17-wc-circular-dependencies-fix.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.23.2(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + bs58: 6.0.0 + semver: 7.7.2 + valtio: 2.1.7(@types/react@19.1.2)(react@19.2.4) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + '@lit/react': 1.0.8(@types/react@19.1.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + '@reown/appkit@1.8.18(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.8.18(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -24256,6 +24887,45 @@ snapshots: '@rushstack/eslint-patch@1.16.1': {} + '@rushstack/node-core-library@5.20.3(@types/node@25.3.5)': + dependencies: + ajv: 8.18.0 + ajv-draft-04: 1.0.0(ajv@8.18.0) + ajv-formats: 3.0.1(ajv@8.18.0) + fs-extra: 11.3.3 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.5.4 + optionalDependencies: + '@types/node': 25.3.5 + + '@rushstack/problem-matcher@0.2.1(@types/node@25.3.5)': + optionalDependencies: + '@types/node': 25.3.5 + + '@rushstack/rig-package@0.7.2': + dependencies: + resolve: 1.22.11 + strip-json-comments: 3.1.1 + + '@rushstack/terminal@0.22.3(@types/node@25.3.5)': + dependencies: + '@rushstack/node-core-library': 5.20.3(@types/node@25.3.5) + '@rushstack/problem-matcher': 0.2.1(@types/node@25.3.5) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 25.3.5 + + '@rushstack/ts-command-line@5.3.3(@types/node@25.3.5)': + dependencies: + '@rushstack/terminal': 0.22.3(@types/node@25.3.5) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -24277,6 +24947,17 @@ snapshots: - utf-8-validate - zod + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + optional: true + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 @@ -24298,6 +24979,17 @@ snapshots: - utf-8-validate - zod + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@safe-global/safe-gateway-typescript-sdk': 3.23.1 + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + optional: true + '@safe-global/safe-gateway-typescript-sdk@3.23.1': {} '@sats-connect/core@0.6.5(typescript@5.2.2)': @@ -24346,7 +25038,7 @@ snapshots: '@scure/bip32@1.1.5': dependencies: '@noble/hashes': 1.2.0 - '@noble/secp256k1': 1.7.1 + '@noble/secp256k1': 1.7.2 '@scure/base': 1.1.9 '@scure/bip32@1.4.0': @@ -24357,8 +25049,8 @@ snapshots: '@scure/bip32@1.6.2': dependencies: - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 + '@noble/curves': 1.8.2 + '@noble/hashes': 1.7.2 '@scure/base': 1.2.6 '@scure/bip32@1.7.0': @@ -24384,7 +25076,7 @@ snapshots: '@scure/bip39@1.5.4': dependencies: - '@noble/hashes': 1.7.1 + '@noble/hashes': 1.7.2 '@scure/base': 1.2.6 '@scure/bip39@1.6.0': @@ -24511,14 +25203,14 @@ snapshots: varuint-bitcoin: 1.1.2 wif: 2.0.6 - '@shapeshiftoss/bitcoinjs-lib@7.0.0-shapeshift.0(typescript@5.2.2)': + '@shapeshiftoss/bitcoinjs-lib@7.0.0-shapeshift.0(typescript@5.8.2)': dependencies: '@noble/hashes': 1.8.0 bech32: 2.0.0 bip174: 3.0.0 bs58check: 4.0.0 uint8array-tools: 0.0.9 - valibot: 0.38.0(typescript@5.2.2) + valibot: 0.38.0(typescript@5.8.2) varuint-bitcoin: 2.0.0 transitivePeerDependencies: - typescript @@ -24535,6 +25227,18 @@ snapshots: transitivePeerDependencies: - typescript + '@shapeshiftoss/bitcoinjs-lib@7.0.0-shapeshift.2(typescript@5.8.2)': + dependencies: + '@noble/hashes': 1.8.0 + bech32: 2.0.0 + bip174: 3.0.0 + bs58check: 4.0.0 + uint8array-tools: 0.0.9 + valibot: 0.38.0(typescript@5.8.2) + varuint-bitcoin: 2.0.0 + transitivePeerDependencies: + - typescript + '@shapeshiftoss/blockbook@9.3.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -24577,16 +25281,16 @@ snapshots: - bufferutil - utf-8-validate - '@shapeshiftoss/contracts@1.0.6(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@shapeshiftoss/contracts@1.0.6(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@shapeshiftoss/caip': 8.16.8 - '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@shapeshiftoss/utils': 1.0.6(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/utils': 1.0.6(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@uniswap/sdk': 3.0.3(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0) ethers: 6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) ethers5: ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) lodash: 4.17.23 - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@ethersproject/address' - '@ethersproject/contracts' @@ -24621,13 +25325,32 @@ snapshots: - typescript - utf-8-validate - '@shapeshiftoss/hdwallet-native@1.62.41(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@shapeshiftoss/hdwallet-core@1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)': + dependencies: + '@shapeshiftoss/bitcoinjs-lib': 7.0.0-shapeshift.2(typescript@5.8.2) + '@shapeshiftoss/proto-tx-builder': 0.10.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.1.0)(utf-8-validate@5.0.10) + bs58check: 4.0.0 + eip-712: 1.0.0 + ethers: 5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) + eventemitter2: 5.0.1 + lodash: 4.17.23 + rxjs: 6.6.7 + type-assertions: 1.1.0 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - typescript + - utf-8-validate + + '@shapeshiftoss/hdwallet-native@1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@bitcoinerlab/secp256k1': 1.2.0 '@noble/curves': 1.9.7 '@scure/starknet': 1.1.2 - '@shapeshiftoss/bitcoinjs-lib': 7.0.0-shapeshift.2(typescript@5.2.2) - '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@shapeshiftoss/bitcoinjs-lib': 7.0.0-shapeshift.2(typescript@5.8.2) + '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) '@shapeshiftoss/proto-tx-builder': 0.10.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@ton/core': 0.62.1(@ton/crypto@3.3.0) '@ton/crypto': 3.3.0 @@ -24641,7 +25364,7 @@ snapshots: bnb-javascript-sdk-nobroadcast: 2.16.15(bufferutil@4.1.0)(utf-8-validate@5.0.10) bs58check: 4.0.0 crypto-js: 4.2.0 - ecpair: 3.0.1(typescript@5.2.2) + ecpair: 3.0.1(typescript@5.8.2) eip-712: 1.0.0 ethers: 5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) eventemitter2: 5.0.1 @@ -24651,8 +25374,8 @@ snapshots: node-fetch: 2.7.0 p-lazy: 3.1.0 scrypt-js: 3.0.1 - starknet: 9.4.0(typescript@5.2.2)(zod@3.25.76) - tendermint-tx-builder: 1.0.16(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + starknet: 9.4.0(typescript@5.8.2)(zod@3.25.76) + tendermint-tx-builder: 1.0.16(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -24666,20 +25389,20 @@ snapshots: '@shapeshiftoss/logger@1.1.3': {} - '@shapeshiftoss/metamask-snaps-adapter@1.0.13(211af8fc05d26bebb634bbf1f0177cc6)': + '@shapeshiftoss/metamask-snaps-adapter@1.0.13(ffe613154a69d26e0f11f7fab9dfa120)': dependencies: '@ethersproject/providers': 5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@metamask/detect-provider': 2.0.0 '@metamask/snaps-ui': 1.0.2 '@shapeshiftoss/caip': link:packages/caip - '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) + '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) '@shapeshiftoss/logger': 1.1.3 - '@shapeshiftoss/metamask-snaps': 1.0.13(9a3d51cc564618bc072132bfbda37d2e) - '@shapeshiftoss/metamask-snaps-types': 1.0.13(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/metamask-snaps': 1.0.13(8806108aec3f794b3300cf4ceae632a3) + '@shapeshiftoss/metamask-snaps-types': 1.0.13(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@shapeshiftoss/types': link:packages/types eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1) - mipd: 0.0.7(typescript@5.2.2) + mipd: 0.0.7(typescript@5.8.2) p-queue: 6.6.2 webpack: 5.105.3 transitivePeerDependencies: @@ -24714,12 +25437,12 @@ snapshots: - utf-8-validate - zod - '@shapeshiftoss/metamask-snaps-types@1.0.13(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@shapeshiftoss/metamask-snaps-types@1.0.13(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@metamask/types': 1.1.0 - '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@shapeshiftoss/hdwallet-native': 1.62.41(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@shapeshiftoss/unchained-client': 10.14.10(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@shapeshiftoss/hdwallet-native': 1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/unchained-client': 10.14.10(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@ethersproject/address' - '@ethersproject/contracts' @@ -24739,26 +25462,26 @@ snapshots: - utf-8-validate - zod - '@shapeshiftoss/metamask-snaps@1.0.13(9a3d51cc564618bc072132bfbda37d2e)': + '@shapeshiftoss/metamask-snaps@1.0.13(8806108aec3f794b3300cf4ceae632a3)': dependencies: '@babel/core': 7.29.0 '@ethersproject/providers': 5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@metamask/detect-provider': 2.0.0 - '@metamask/eslint-config': 15.0.0(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-jsdoc@50.8.0(eslint@8.57.1))(eslint-plugin-prettier@5.0.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3))(eslint-plugin-promise@7.2.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3) + '@metamask/eslint-config': 15.0.0(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1))(eslint-plugin-jsdoc@50.8.0(eslint@8.57.1))(eslint-plugin-prettier@5.0.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3))(eslint-plugin-promise@7.2.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3) '@metamask/key-tree': 9.1.2 '@metamask/snaps-types': 1.0.2(@metamask/approval-controller@3.5.2) '@metamask/snaps-ui': 1.0.2 '@shapeshiftoss/caip': 8.16.8 - '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10) - '@shapeshiftoss/hdwallet-native': 1.62.41(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@shapeshiftoss/hdwallet-native': 1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@shapeshiftoss/logger': 1.1.3 - '@shapeshiftoss/metamask-snaps-types': 1.0.13(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@shapeshiftoss/unchained-client': 10.1.1(@ethersproject/abi@5.8.0)(@ethersproject/address@5.8.0)(@ethersproject/bignumber@5.8.0)(@ethersproject/bytes@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@shapeshiftoss/caip@8.16.8)(@shapeshiftoss/logger@1.1.3)(@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.2.2))(eslint@8.57.1)(typescript@5.2.2) - '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.2.2) + '@shapeshiftoss/metamask-snaps-types': 1.0.13(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/unchained-client': 10.1.1(@ethersproject/abi@5.8.0)(@ethersproject/address@5.8.0)(@ethersproject/bignumber@5.8.0)(@ethersproject/bytes@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@shapeshiftoss/caip@8.16.8)(@shapeshiftoss/logger@1.1.3)(@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76))(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.8.2) eslint: 8.57.1 - eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.2.2))(eslint@8.57.1)(typescript@5.2.2))(eslint@8.57.1)(typescript@5.2.2) + eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-prettier: 5.0.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.0.3) eslint-plugin-react: 7.37.5(eslint@8.57.1) @@ -24806,12 +25529,12 @@ snapshots: - debug - utf-8-validate - '@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@cowprotocol/app-data': 2.5.1(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0) '@shapeshiftoss/caip': 8.16.8 ethers5: ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - cross-fetch @@ -24823,11 +25546,28 @@ snapshots: - utf-8-validate - zod - '@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@cowprotocol/app-data': 2.5.1(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0) '@shapeshiftoss/caip': 8.16.8 ethers5: ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - cross-fetch + - debug + - ethers + - ipfs-only-hash + - multiformats + - typescript + - utf-8-validate + - zod + + '@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@cowprotocol/app-data': 2.5.1(cross-fetch@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0) + '@shapeshiftoss/caip': 8.16.8 + ethers5: ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil @@ -24840,11 +25580,11 @@ snapshots: - utf-8-validate - zod - '@shapeshiftoss/unchained-client@10.1.1(@ethersproject/abi@5.8.0)(@ethersproject/address@5.8.0)(@ethersproject/bignumber@5.8.0)(@ethersproject/bytes@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@shapeshiftoss/caip@8.16.8)(@shapeshiftoss/logger@1.1.3)(@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76))(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@shapeshiftoss/unchained-client@10.1.1(@ethersproject/abi@5.8.0)(@ethersproject/address@5.8.0)(@ethersproject/bignumber@5.8.0)(@ethersproject/bytes@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@shapeshiftoss/caip@8.16.8)(@shapeshiftoss/logger@1.1.3)(@shapeshiftoss/types@8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76))(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@shapeshiftoss/caip': 8.16.8 '@shapeshiftoss/logger': 1.1.3 - '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@yfi/sdk': 1.2.0(@ethersproject/abi@5.8.0)(@ethersproject/address@5.8.0)(@ethersproject/bignumber@5.8.0)(@ethersproject/bytes@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) bignumber.js: 9.3.1 ethers: 5.7.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -24861,17 +25601,17 @@ snapshots: - encoding - utf-8-validate - '@shapeshiftoss/unchained-client@10.14.10(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@shapeshiftoss/unchained-client@10.14.10(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@shapeshiftoss/caip': 8.16.8 '@shapeshiftoss/common-api': 9.3.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@shapeshiftoss/contracts': 1.0.6(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@shapeshiftoss/utils': 1.0.6(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/contracts': 1.0.6(@ethersproject/address@5.8.0)(@ethersproject/contracts@5.8.0)(@ethersproject/networks@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(@ethersproject/solidity@5.8.0)(bufferutil@4.1.0)(cross-fetch@4.1.0)(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/utils': 1.0.6(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) axios: 1.13.6(debug@4.4.3) bignumber.js: 9.3.1 ethers: 6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) isomorphic-ws: 4.0.1(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - viem: 2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@ethersproject/address' @@ -24888,10 +25628,29 @@ snapshots: - utf-8-validate - zod - '@shapeshiftoss/utils@1.0.6(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@shapeshiftoss/utils@1.0.6(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@shapeshiftoss/caip': 8.16.8 + '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@sniptt/monads': 0.5.10 + bignumber.js: 9.3.1 + dayjs: 1.11.19 + lodash-es: 4.17.23 + transitivePeerDependencies: + - bufferutil + - cross-fetch + - debug + - ethers + - ipfs-only-hash + - multiformats + - typescript + - utf-8-validate + - zod + + '@shapeshiftoss/utils@1.0.6(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@shapeshiftoss/caip': 8.16.8 - '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.11.1(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@shapeshiftoss/types': 8.6.7(bufferutil@4.1.0)(cross-fetch@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(ipfs-only-hash@4.0.0)(multiformats@9.9.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@sniptt/monads': 0.5.10 bignumber.js: 9.3.1 dayjs: 1.11.19 @@ -24919,17 +25678,17 @@ snapshots: dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': + '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana-program/stake@0.2.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@solana-program/stake@0.2.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': + '@solana-program/stake@0.2.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10))': dependencies: @@ -24940,30 +25699,35 @@ snapshots: dependencies: '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10) + optional: true + '@solana-program/system@0.7.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@solana-program/system@0.7.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': + '@solana-program/system@0.7.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2))': + '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@solana/sysvars': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/sysvars': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': + '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10))': dependencies: @@ -24974,6 +25738,11 @@ snapshots: dependencies: '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6) + '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10) + optional: true + '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -24986,6 +25755,18 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec': 2.3.0(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/accounts@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -24999,6 +25780,19 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/accounts@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec': 5.5.1(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/addresses@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/assertions': 2.3.0(typescript@5.2.2) @@ -25010,6 +25804,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/addresses@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/assertions': 2.3.0(typescript@5.8.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/nominal-types': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/addresses@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/assertions': 5.5.1(typescript@5.2.2) @@ -25022,17 +25827,40 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/addresses@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/nominal-types': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/assertions@2.3.0(typescript@5.2.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) typescript: 5.2.2 + '@solana/assertions@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + '@solana/assertions@5.5.1(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) optionalDependencies: typescript: 5.2.2 + '@solana/assertions@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + '@solana/buffer-layout-utils@0.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 @@ -25064,17 +25892,33 @@ snapshots: '@solana/errors': 2.0.0-rc.1(typescript@5.2.2) typescript: 5.2.2 + '@solana/codecs-core@2.0.0-rc.1(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.0.0-rc.1(typescript@5.8.2) + typescript: 5.8.2 + '@solana/codecs-core@2.3.0(typescript@5.2.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) typescript: 5.2.2 + '@solana/codecs-core@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + '@solana/codecs-core@5.5.1(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) optionalDependencies: typescript: 5.2.2 + '@solana/codecs-core@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.2.2) @@ -25082,6 +25926,13 @@ snapshots: '@solana/errors': 2.0.0-rc.1(typescript@5.2.2) typescript: 5.2.2 + '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.2) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.2) + typescript: 5.8.2 + '@solana/codecs-data-structures@2.3.0(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.3.0(typescript@5.2.2) @@ -25089,6 +25940,13 @@ snapshots: '@solana/errors': 2.3.0(typescript@5.2.2) typescript: 5.2.2 + '@solana/codecs-data-structures@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + '@solana/codecs-data-structures@5.5.1(typescript@5.2.2)': dependencies: '@solana/codecs-core': 5.5.1(typescript@5.2.2) @@ -25097,18 +25955,38 @@ snapshots: optionalDependencies: typescript: 5.2.2 + '@solana/codecs-data-structures@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-numbers': 5.5.1(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.2.2) '@solana/errors': 2.0.0-rc.1(typescript@5.2.2) typescript: 5.2.2 + '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.2) + typescript: 5.8.2 + '@solana/codecs-numbers@2.3.0(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.3.0(typescript@5.2.2) '@solana/errors': 2.3.0(typescript@5.2.2) typescript: 5.2.2 + '@solana/codecs-numbers@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + '@solana/codecs-numbers@5.5.1(typescript@5.2.2)': dependencies: '@solana/codecs-core': 5.5.1(typescript@5.2.2) @@ -25116,6 +25994,13 @@ snapshots: optionalDependencies: typescript: 5.2.2 + '@solana/codecs-numbers@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.2.2) @@ -25124,6 +26009,14 @@ snapshots: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.2.2 + '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.2) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.2) + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.8.2 + '@solana/codecs-strings@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.3.0(typescript@5.2.2) @@ -25132,6 +26025,14 @@ snapshots: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.2.2 + '@solana/codecs-strings@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.8.2 + '@solana/codecs-strings@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 5.5.1(typescript@5.2.2) @@ -25141,6 +26042,15 @@ snapshots: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.2.2 + '@solana/codecs-strings@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-numbers': 5.5.1(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.8.2 + '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.2.2) @@ -25152,6 +26062,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.8.2) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.2) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/codecs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.3.0(typescript@5.2.2) @@ -25163,6 +26084,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/codecs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-data-structures': 2.3.0(typescript@5.8.2) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/options': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/codecs@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 5.5.1(typescript@5.2.2) @@ -25175,18 +26107,42 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/codecs@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-data-structures': 5.5.1(typescript@5.8.2) + '@solana/codecs-numbers': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/options': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/errors@2.0.0-rc.1(typescript@5.2.2)': dependencies: chalk: 5.3.0 commander: 12.1.0 typescript: 5.2.2 + '@solana/errors@2.0.0-rc.1(typescript@5.8.2)': + dependencies: + chalk: 5.3.0 + commander: 12.1.0 + typescript: 5.8.2 + '@solana/errors@2.3.0(typescript@5.2.2)': dependencies: chalk: 5.6.2 commander: 14.0.3 typescript: 5.2.2 + '@solana/errors@2.3.0(typescript@5.8.2)': + dependencies: + chalk: 5.6.2 + commander: 14.0.3 + typescript: 5.8.2 + '@solana/errors@5.5.1(typescript@5.2.2)': dependencies: chalk: 5.6.2 @@ -25194,22 +26150,47 @@ snapshots: optionalDependencies: typescript: 5.2.2 + '@solana/errors@5.5.1(typescript@5.8.2)': + dependencies: + chalk: 5.6.2 + commander: 14.0.2 + optionalDependencies: + typescript: 5.8.2 + '@solana/fast-stable-stringify@2.3.0(typescript@5.2.2)': dependencies: typescript: 5.2.2 + '@solana/fast-stable-stringify@2.3.0(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + '@solana/fast-stable-stringify@5.5.1(typescript@5.2.2)': optionalDependencies: typescript: 5.2.2 + '@solana/fast-stable-stringify@5.5.1(typescript@5.8.2)': + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/functional@2.3.0(typescript@5.2.2)': dependencies: typescript: 5.2.2 + '@solana/functional@2.3.0(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + '@solana/functional@5.5.1(typescript@5.2.2)': optionalDependencies: typescript: 5.2.2 + '@solana/functional@5.5.1(typescript@5.8.2)': + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/instruction-plans@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) @@ -25223,12 +26204,32 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/instruction-plans@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/instructions': 5.5.1(typescript@5.8.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/promises': 5.5.1(typescript@5.8.2) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/instructions@2.3.0(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.3.0(typescript@5.2.2) '@solana/errors': 2.3.0(typescript@5.2.2) typescript: 5.2.2 + '@solana/instructions@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + '@solana/instructions@5.5.1(typescript@5.2.2)': dependencies: '@solana/codecs-core': 5.5.1(typescript@5.2.2) @@ -25236,6 +26237,14 @@ snapshots: optionalDependencies: typescript: 5.2.2 + '@solana/instructions@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/keys@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/assertions': 2.3.0(typescript@5.2.2) @@ -25247,6 +26256,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/keys@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/assertions': 2.3.0(typescript@5.8.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/nominal-types': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/keys@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/assertions': 5.5.1(typescript@5.2.2) @@ -25259,6 +26279,19 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/keys@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/nominal-types': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25284,27 +26317,27 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': - dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/errors': 2.3.0(typescript@5.2.2) - '@solana/functional': 2.3.0(typescript@5.2.2) - '@solana/instructions': 2.3.0(typescript@5.2.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.2.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.2.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - typescript: 5.2.2 + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + dependencies: + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/functional': 2.3.0(typescript@5.8.2) + '@solana/instructions': 2.3.0(typescript@5.8.2) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws @@ -25372,14 +26405,54 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate + '@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10)': + dependencies: + '@solana/accounts': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/functional': 5.5.1(typescript@5.8.2) + '@solana/instruction-plans': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/instructions': 5.5.1(typescript@5.8.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/offchain-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/plugin-core': 5.5.1(typescript@5.8.2) + '@solana/programs': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-api': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/signers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/sysvars': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-confirmation': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + '@solana/nominal-types@2.3.0(typescript@5.2.2)': dependencies: typescript: 5.2.2 + '@solana/nominal-types@2.3.0(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + '@solana/nominal-types@5.5.1(typescript@5.2.2)': optionalDependencies: typescript: 5.2.2 + '@solana/nominal-types@5.5.1(typescript@5.8.2)': + optionalDependencies: + typescript: 5.8.2 + '@solana/offchain-messages@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25395,6 +26468,22 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/offchain-messages@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-data-structures': 5.5.1(typescript@5.8.2) + '@solana/codecs-numbers': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/nominal-types': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.2.2) @@ -25406,6 +26495,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.2) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.8.2) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.2) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/options@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 2.3.0(typescript@5.2.2) @@ -25417,6 +26517,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/options@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-data-structures': 2.3.0(typescript@5.8.2) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/options@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/codecs-core': 5.5.1(typescript@5.2.2) @@ -25429,6 +26540,18 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/options@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-data-structures': 5.5.1(typescript@5.8.2) + '@solana/codecs-numbers': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/pay@0.2.6(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@6.0.6)': dependencies: '@solana/qr-code-styling': 1.6.0 @@ -25449,6 +26572,11 @@ snapshots: optionalDependencies: typescript: 5.2.2 + '@solana/plugin-core@5.5.1(typescript@5.8.2)': + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/programs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25457,6 +26585,14 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/programs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/programs@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25466,14 +26602,33 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/programs@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/promises@2.3.0(typescript@5.2.2)': dependencies: typescript: 5.2.2 + '@solana/promises@2.3.0(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + '@solana/promises@5.5.1(typescript@5.2.2)': optionalDependencies: typescript: 5.2.2 + '@solana/promises@5.5.1(typescript@5.8.2)': + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/qr-code-styling@1.6.0': dependencies: qrcode-generator: 1.5.2 @@ -25495,6 +26650,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec': 2.3.0(typescript@5.8.2) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/rpc-api@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25513,28 +26685,70 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc-api@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec': 5.5.1(typescript@5.8.2) + '@solana/rpc-transformers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/rpc-parsed-types@2.3.0(typescript@5.2.2)': dependencies: typescript: 5.2.2 + '@solana/rpc-parsed-types@2.3.0(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + '@solana/rpc-parsed-types@5.5.1(typescript@5.2.2)': optionalDependencies: typescript: 5.2.2 + '@solana/rpc-parsed-types@5.5.1(typescript@5.8.2)': + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/rpc-spec-types@2.3.0(typescript@5.2.2)': dependencies: typescript: 5.2.2 + '@solana/rpc-spec-types@2.3.0(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + '@solana/rpc-spec-types@5.5.1(typescript@5.2.2)': optionalDependencies: typescript: 5.2.2 + '@solana/rpc-spec-types@5.5.1(typescript@5.8.2)': + optionalDependencies: + typescript: 5.8.2 + '@solana/rpc-spec@2.3.0(typescript@5.2.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) '@solana/rpc-spec-types': 2.3.0(typescript@5.2.2) typescript: 5.2.2 + '@solana/rpc-spec@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + '@solana/rpc-spec@5.5.1(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) @@ -25542,6 +26756,13 @@ snapshots: optionalDependencies: typescript: 5.2.2 + '@solana/rpc-spec@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + '@solana/rpc-subscriptions-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25555,6 +26776,19 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc-subscriptions-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.2) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/rpc-subscriptions-api@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25569,6 +26803,21 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc-subscriptions-api@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.8.2) + '@solana/rpc-transformers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) @@ -25578,13 +26827,13 @@ snapshots: typescript: 5.2.2 ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: - '@solana/errors': 2.3.0(typescript@5.2.2) - '@solana/functional': 2.3.0(typescript@5.2.2) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.2.2) - '@solana/subscribable': 2.3.0(typescript@5.2.2) - typescript: 5.2.2 + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/functional': 2.3.0(typescript@5.8.2) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.2) + '@solana/subscribable': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)': @@ -25614,6 +26863,20 @@ snapshots: - bufferutil - utf-8-validate + '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/functional': 5.5.1(typescript@5.8.2) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.8.2) + '@solana/subscribable': 5.5.1(typescript@5.8.2) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + optional: true + '@solana/rpc-subscriptions-spec@2.3.0(typescript@5.2.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) @@ -25622,6 +26885,14 @@ snapshots: '@solana/subscribable': 2.3.0(typescript@5.2.2) typescript: 5.2.2 + '@solana/rpc-subscriptions-spec@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/promises': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.2) + '@solana/subscribable': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + '@solana/rpc-subscriptions-spec@5.5.1(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) @@ -25631,6 +26902,16 @@ snapshots: optionalDependencies: typescript: 5.2.2 + '@solana/rpc-subscriptions-spec@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/promises': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.8.2) + '@solana/subscribable': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) @@ -25649,20 +26930,20 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': - dependencies: - '@solana/errors': 2.3.0(typescript@5.2.2) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.2.2) - '@solana/functional': 2.3.0(typescript@5.2.2) - '@solana/promises': 2.3.0(typescript@5.2.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.2.2) - '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.2.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/subscribable': 2.3.0(typescript@5.2.2) - typescript: 5.2.2 + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.8.2) + '@solana/functional': 2.3.0(typescript@5.8.2) + '@solana/promises': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.2) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/subscribable': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws @@ -25708,6 +26989,27 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate + '@solana/rpc-subscriptions@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.8.2) + '@solana/functional': 5.5.1(typescript@5.8.2) + '@solana/promises': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-subscriptions-api': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-subscriptions-channel-websocket': 5.5.1(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.8.2) + '@solana/rpc-transformers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/subscribable': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + '@solana/rpc-transformers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) @@ -25719,6 +27021,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc-transformers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/functional': 2.3.0(typescript@5.8.2) + '@solana/nominal-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/rpc-transformers@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) @@ -25731,6 +27044,19 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc-transformers@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/functional': 5.5.1(typescript@5.8.2) + '@solana/nominal-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/rpc-transport-http@2.3.0(typescript@5.2.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) @@ -25739,6 +27065,14 @@ snapshots: typescript: 5.2.2 undici-types: 7.22.0 + '@solana/rpc-transport-http@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + undici-types: 7.22.0 + '@solana/rpc-transport-http@5.5.1(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) @@ -25748,6 +27082,16 @@ snapshots: optionalDependencies: typescript: 5.2.2 + '@solana/rpc-transport-http@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.8.2) + undici-types: 7.22.0 + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/rpc-types@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25760,6 +27104,18 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc-types@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/nominal-types': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/rpc-types@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25773,6 +27129,19 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc-types@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-numbers': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/nominal-types': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/rpc@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.2.2) @@ -25788,6 +27157,21 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.8.2) + '@solana/functional': 2.3.0(typescript@5.8.2) + '@solana/rpc-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-spec': 2.3.0(typescript@5.8.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-transport-http': 2.3.0(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/rpc@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) @@ -25804,6 +27188,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/rpc@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.8.2) + '@solana/functional': 5.5.1(typescript@5.8.2) + '@solana/rpc-api': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-spec': 5.5.1(typescript@5.8.2) + '@solana/rpc-spec-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-transformers': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-transport-http': 5.5.1(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/signers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25818,6 +27219,20 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/signers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/instructions': 2.3.0(typescript@5.8.2) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/nominal-types': 2.3.0(typescript@5.8.2) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/signers@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25834,6 +27249,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/signers@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/instructions': 5.5.1(typescript@5.8.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/nominal-types': 5.5.1(typescript@5.8.2) + '@solana/offchain-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/spl-account-compression@0.1.10(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@metaplex-foundation/beet': 0.7.1 @@ -25849,9 +27281,9 @@ snapshots: - supports-color - utf-8-validate - '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -25873,9 +27305,9 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -25932,12 +27364,12 @@ snapshots: - typescript - utf-8-validate - '@solana/spl-token@0.4.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(utf-8-validate@5.0.10)': + '@solana/spl-token@0.4.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) '@solana/web3.js': 1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: @@ -25982,12 +27414,24 @@ snapshots: '@solana/errors': 2.3.0(typescript@5.2.2) typescript: 5.2.2 + '@solana/subscribable@2.3.0(typescript@5.8.2)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.2) + typescript: 5.8.2 + '@solana/subscribable@5.5.1(typescript@5.2.2)': dependencies: '@solana/errors': 5.5.1(typescript@5.2.2) optionalDependencies: typescript: 5.2.2 + '@solana/subscribable@5.5.1(typescript@5.8.2)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + optional: true + '@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -25998,6 +27442,16 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/accounts': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -26009,6 +27463,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/accounts': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -26026,19 +27491,19 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/errors': 2.3.0(typescript@5.2.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/promises': 2.3.0(typescript@5.2.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) - typescript: 5.2.2 + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/promises': 2.3.0(typescript@5.8.2) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws @@ -26082,6 +27547,26 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate + '@solana/transaction-confirmation@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/promises': 5.5.1(typescript@5.8.2) + '@solana/rpc': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transactions': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + '@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -26097,6 +27582,21 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-data-structures': 2.3.0(typescript@5.8.2) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/functional': 2.3.0(typescript@5.8.2) + '@solana/instructions': 2.3.0(typescript@5.8.2) + '@solana/nominal-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/transaction-messages@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -26113,6 +27613,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/transaction-messages@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-data-structures': 5.5.1(typescript@5.8.2) + '@solana/codecs-numbers': 5.5.1(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/functional': 5.5.1(typescript@5.8.2) + '@solana/instructions': 5.5.1(typescript@5.8.2) + '@solana/nominal-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/transactions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -26131,6 +27648,24 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/transactions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.2) + '@solana/codecs-data-structures': 2.3.0(typescript@5.8.2) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 2.3.0(typescript@5.8.2) + '@solana/functional': 2.3.0(typescript@5.8.2) + '@solana/instructions': 2.3.0(typescript@5.8.2) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/nominal-types': 2.3.0(typescript@5.8.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/transactions@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)': dependencies: '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) @@ -26150,6 +27685,26 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/transactions@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)': + dependencies: + '@solana/addresses': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/codecs-core': 5.5.1(typescript@5.8.2) + '@solana/codecs-data-structures': 5.5.1(typescript@5.8.2) + '@solana/codecs-numbers': 5.5.1(typescript@5.8.2) + '@solana/codecs-strings': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/errors': 5.5.1(typescript@5.8.2) + '@solana/functional': 5.5.1(typescript@5.8.2) + '@solana/instructions': 5.5.1(typescript@5.8.2) + '@solana/keys': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/nominal-types': 5.5.1(typescript@5.8.2) + '@solana/rpc-types': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + '@solana/transaction-messages': 5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@solana/wallet-adapter-alpha@0.1.14(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) @@ -26638,6 +28193,30 @@ snapshots: - typescript - utf-8-validate + '@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)': + dependencies: + '@babel/runtime': 7.28.6 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@solana/buffer-layout': 4.0.1 + '@solana/codecs-numbers': 2.3.0(typescript@5.8.2) + agentkeepalive: 4.6.0 + bn.js: 5.2.3 + borsh: 0.7.0 + bs58: 4.0.1 + buffer: 6.0.3 + fast-stable-stringify: 1.0.0 + jayson: 4.3.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + node-fetch: 2.7.0 + rpc-websockets: 9.3.5 + superstruct: 2.0.2 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + optional: true + '@solflare-wallet/metamask-sdk@1.0.3(@solana/web3.js@1.98.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: '@solana/wallet-standard-features': 1.3.0 @@ -26670,6 +28249,16 @@ snapshots: - typescript - zod + '@starknet-io/get-starknet-wallet-standard@5.0.0(typescript@5.8.2)(zod@3.25.76)': + dependencies: + '@starknet-io/types-js': 0.7.10 + '@wallet-standard/base': 1.1.0 + '@wallet-standard/features': 1.1.0 + ox: 0.4.4(typescript@5.8.2)(zod@3.25.76) + transitivePeerDependencies: + - typescript + - zod + '@starknet-io/types-js@0.10.0': {} '@starknet-io/types-js@0.7.10': {} @@ -27021,14 +28610,14 @@ snapshots: - react-native - utf-8-validate - '@trezor/blockchain-link@2.6.1(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.2.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@trezor/blockchain-link@2.6.1(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: - '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) - '@solana-program/stake': 0.2.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) - '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) - '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)) - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2) + '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) + '@solana-program/stake': 0.2.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) + '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) + '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2) '@stellar/stellar-sdk': 14.2.0 '@trezor/blockchain-link-types': 1.5.0(tslib@2.8.1) '@trezor/blockchain-link-utils': 1.5.1(bufferutil@4.1.0)(tslib@2.8.1)(utf-8-validate@6.0.6) @@ -27109,9 +28698,9 @@ snapshots: - expo-localization - react-native - '@trezor/connect-web@9.7.2(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.2.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@trezor/connect-web@9.7.2(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: - '@trezor/connect': 9.7.2(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.2.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@trezor/connect': 9.7.2(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@trezor/connect-common': 0.5.1(tslib@2.8.1) '@trezor/utils': 9.5.0(tslib@2.8.1) '@trezor/websocket-client': 1.3.0(bufferutil@4.1.0)(tslib@2.8.1)(utf-8-validate@6.0.6) @@ -27151,7 +28740,7 @@ snapshots: - utf-8-validate - ws - '@trezor/connect@9.7.2(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.2.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@trezor/connect@9.7.2(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: '@ethereumjs/common': 10.1.1 '@ethereumjs/tx': 10.1.1 @@ -27159,12 +28748,12 @@ snapshots: '@mobily/ts-belt': 3.13.1 '@noble/hashes': 1.8.0 '@scure/bip39': 1.6.0 - '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) - '@solana-program/system': 0.7.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) - '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) - '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)) - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@trezor/blockchain-link': 2.6.1(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.2.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.2.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) + '@solana-program/system': 0.7.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) + '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) + '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@trezor/blockchain-link': 2.6.1(@solana/sysvars@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@6.0.6)(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@trezor/blockchain-link-types': 1.5.1(tslib@2.8.1) '@trezor/blockchain-link-utils': 1.5.2(bufferutil@4.1.0)(tslib@2.8.1)(utf-8-validate@6.0.6) '@trezor/connect-analytics': 1.4.0(tslib@2.8.1) @@ -27394,6 +28983,8 @@ snapshots: tslib: 2.8.1 optional: true + '@types/argparse@1.0.38': {} + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -27718,6 +29309,7 @@ snapshots: '@types/node@25.3.5': dependencies: undici-types: 7.18.2 + optional: true '@types/normalize-package-data@2.4.4': {} @@ -27909,6 +29501,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/type-utils': 8.56.1(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.56.1 + eslint: 8.57.1 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.1)(typescript@5.2.2)': dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.2.2) @@ -27917,27 +29525,56 @@ snapshots: - supports-color - typescript + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.8.2) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + '@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.2.2)': dependencies: '@typescript-eslint/scope-manager': 8.56.1 '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.2.2) '@typescript-eslint/visitor-keys': 8.56.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) eslint: 8.57.1 typescript: 5.2.2 transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/project-service@8.56.1(typescript@5.2.2)': dependencies: '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.2.2) '@typescript-eslint/types': 8.56.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) typescript: 5.2.2 transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.56.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.8.2) + '@typescript-eslint/types': 8.56.1 + debug: 4.4.3(supports-color@8.1.1) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 @@ -27952,18 +29589,34 @@ snapshots: dependencies: typescript: 5.2.2 + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + '@typescript-eslint/type-utils@8.56.1(eslint@8.57.1)(typescript@5.2.2)': dependencies: '@typescript-eslint/types': 8.56.1 '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.2.2) '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.2.2) - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) eslint: 8.57.1 ts-api-utils: 2.4.0(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.56.1(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + ts-api-utils: 2.4.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@5.62.0': {} '@typescript-eslint/types@8.56.1': {} @@ -27972,7 +29625,7 @@ snapshots: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.7.4 @@ -27982,13 +29635,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.8.2)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.4.3(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.7.4 + tsutils: 3.21.0(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.2.2)': dependencies: '@typescript-eslint/project-service': 8.56.1(typescript@5.2.2) '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.2.2) '@typescript-eslint/types': 8.56.1 '@typescript-eslint/visitor-keys': 8.56.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) minimatch: 10.2.4 semver: 7.7.4 tinyglobby: 0.2.15 @@ -27997,6 +29664,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@5.8.2) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.8.2) + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@5.2.2)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) @@ -28012,6 +29694,21 @@ snapshots: - supports-color - typescript + '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.8.2) + eslint: 8.57.1 + eslint-scope: 5.1.1 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + '@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.2.2)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) @@ -28023,6 +29720,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + eslint: 8.57.1 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 @@ -28076,14 +29784,14 @@ snapshots: tiny-warning: 1.0.3 toformat: 2.0.0 - '@uniswap/swap-router-contracts@1.3.1(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10))': + '@uniswap/swap-router-contracts@1.3.1(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))': dependencies: '@openzeppelin/contracts': 3.4.2-solc-0.7 '@uniswap/v2-core': 1.0.1 '@uniswap/v3-core': 1.0.1 '@uniswap/v3-periphery': 1.4.4 dotenv: 14.3.2 - hardhat-watcher: 2.5.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) + hardhat-watcher: 2.5.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)) transitivePeerDependencies: - hardhat @@ -28101,12 +29809,12 @@ snapshots: '@uniswap/v3-core': 1.0.1 base64-sol: 1.0.1 - '@uniswap/v3-sdk@3.28.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10))': + '@uniswap/v3-sdk@3.28.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10))': dependencies: '@ethersproject/abi': 5.8.0 '@ethersproject/solidity': 5.8.0 '@uniswap/sdk-core': 7.11.0 - '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)) + '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)) '@uniswap/v3-periphery': 1.4.4 '@uniswap/v3-staker': 1.0.0 tiny-invariant: 1.3.3 @@ -28428,23 +30136,23 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@3.0.9(msw@0.27.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@3.0.9(msw@0.27.2)(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0))': dependencies: '@vitest/spy': 3.0.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 0.27.2 - vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 5.4.21(@types/node@25.3.5)(terser@5.46.0) - '@vitest/mocker@3.0.9(msw@0.36.8)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@3.0.9(msw@0.36.8)(vite@5.4.21(@types/node@22.19.13)(terser@5.46.0))': dependencies: '@vitest/spy': 3.0.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 0.36.8 - vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 5.4.21(@types/node@22.19.13)(terser@5.46.0) '@vitest/mocker@4.0.18(msw@0.36.8)(vite@6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: @@ -28506,6 +30214,51 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/compiler-core@3.5.30': + dependencies: + '@babel/parser': 7.29.0 + '@vue/shared': 3.5.30 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.30': + dependencies: + '@vue/compiler-core': 3.5.30 + '@vue/shared': 3.5.30 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/language-core@2.2.0(typescript@5.2.2)': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.30 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.30 + alien-signals: 0.4.14 + minimatch: 9.0.9 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.2.2 + + '@vue/shared@3.5.30': {} + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.69.0)(@types/react@19.1.2)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)))(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.69.0)(@tanstack/react-query@5.69.0(react@19.2.4))(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(utf-8-validate@6.0.6)(viem@2.43.5(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': dependencies: '@base-org/account': 2.4.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) @@ -28939,6 +30692,50 @@ snapshots: - utf-8-validate - zod + '@walletconnect/core@2.23.2(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.2 + '@walletconnect/utils': 2.23.2(typescript@5.8.2)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.39.3 + events: 3.3.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/core@2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 @@ -29026,14 +30823,59 @@ snapshots: - uploadthing - utf-8-validate - zod + optional: true + + '@walletconnect/core@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.44.0 + events: 3.3.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod - '@walletconnect/core@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/core@2.23.7(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 '@walletconnect/relay-api': 1.0.11 @@ -29041,7 +30883,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.23.7 - '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) + '@walletconnect/utils': 2.23.7(typescript@5.8.2)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.44.0 events: 3.3.0 @@ -29176,6 +31018,7 @@ snapshots: - use-sync-external-store - utf-8-validate - zod + optional: true '@walletconnect/ethereum-provider@2.23.7(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.2.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: @@ -29223,6 +31066,52 @@ snapshots: - utf-8-validate - zod + '@walletconnect/ethereum-provider@2.23.7(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit': 1.8.17-wc-circular-dependencies-fix.0(@types/react@19.1.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(immer@9.0.21)(react@19.2.4)(typescript@5.8.2)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/sign-client': 2.23.7(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.23.7 + '@walletconnect/universal-provider': 2.23.7(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.23.7(typescript@5.8.2)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + '@walletconnect/events@1.0.1': dependencies: keyvaluestorage-interface: 1.0.0 @@ -29634,6 +31523,42 @@ snapshots: - utf-8-validate - zod + '@walletconnect/sign-client@2.23.2(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.23.2(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 3.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.2 + '@walletconnect/utils': 2.23.2(typescript@5.8.2)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/sign-client@2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/core': 2.23.6(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) @@ -29705,6 +31630,7 @@ snapshots: - uploadthing - utf-8-validate - zod + optional: true '@walletconnect/sign-client@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: @@ -29742,6 +31668,42 @@ snapshots: - utf-8-validate - zod + '@walletconnect/sign-client@2.23.7(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.23.7(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 3.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.8.2)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + '@walletconnect/socket-transport@1.8.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: '@walletconnect/types': 1.8.0 @@ -29851,14 +31813,165 @@ snapshots: - ioredis - uploadthing - '@walletconnect/types@2.21.0': + '@walletconnect/types@2.21.0': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/types@2.21.1': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/types@2.23.2': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/types@2.23.6': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/types@2.23.7': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/universal-provider@2.19.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.19.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.0 + '@walletconnect/utils': 2.19.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 + lodash: 4.17.21 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -29876,17 +31989,28 @@ snapshots: - '@vercel/functions' - '@vercel/kv' - aws4fetch + - bufferutil - db0 + - encoding - ioredis + - typescript - uploadthing + - utf-8-validate + - zod - '@walletconnect/types@2.21.1': + '@walletconnect/universal-provider@2.19.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.19.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.1 + '@walletconnect/utils': 2.19.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -29905,98 +32029,16 @@ snapshots: - '@vercel/functions' - '@vercel/kv' - aws4fetch + - bufferutil - db0 + - encoding - ioredis + - typescript - uploadthing + - utf-8-validate + - zod - '@walletconnect/types@2.23.2': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 3.0.2 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - - '@walletconnect/types@2.23.6': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 3.0.2 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - - '@walletconnect/types@2.23.7': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 3.0.2 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - - '@walletconnect/universal-provider@2.19.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30005,11 +32047,11 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.19.0 - '@walletconnect/utils': 2.19.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + es-toolkit: 1.33.0 events: 3.3.0 - lodash: 4.17.21 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -30036,7 +32078,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.19.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30045,9 +32087,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.19.1 - '@walletconnect/utils': 2.19.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -30076,7 +32118,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30084,11 +32126,11 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - es-toolkit: 1.33.0 + '@walletconnect/logger': 3.0.2 + '@walletconnect/sign-client': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.23.2 + '@walletconnect/utils': 2.23.2(typescript@5.2.2)(zod@3.25.76) + es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -30116,7 +32158,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30124,11 +32166,11 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - es-toolkit: 1.33.0 + '@walletconnect/logger': 3.0.2 + '@walletconnect/sign-client': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.23.2 + '@walletconnect/utils': 2.23.2(typescript@5.2.2)(zod@3.25.76) + es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -30156,7 +32198,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.2(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30165,9 +32207,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 - '@walletconnect/sign-client': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.23.2(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.23.2 - '@walletconnect/utils': 2.23.2(typescript@5.2.2)(zod@3.25.76) + '@walletconnect/utils': 2.23.2(typescript@5.8.2)(zod@3.25.76) es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: @@ -30196,7 +32238,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30205,10 +32247,10 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 - '@walletconnect/sign-client': 2.23.2(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/types': 2.23.2 - '@walletconnect/utils': 2.23.2(typescript@5.2.2)(zod@3.25.76) - es-toolkit: 1.39.3 + '@walletconnect/sign-client': 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) + es-toolkit: 1.44.0 events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -30235,8 +32277,9 @@ snapshots: - uploadthing - utf-8-validate - zod + optional: true - '@walletconnect/universal-provider@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30245,7 +32288,7 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 - '@walletconnect/sign-client': 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/types': 2.23.7 '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) es-toolkit: 1.44.0 @@ -30276,7 +32319,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.7(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -30285,9 +32328,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 - '@walletconnect/sign-client': 2.23.7(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.23.7(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.23.7 - '@walletconnect/utils': 2.23.7(typescript@5.2.2)(zod@3.25.76) + '@walletconnect/utils': 2.23.7(typescript@5.8.2)(zod@3.25.76) es-toolkit: 1.44.0 events: 3.3.0 transitivePeerDependencies: @@ -30548,7 +32591,96 @@ snapshots: - uploadthing - zod - '@walletconnect/utils@2.23.6(typescript@5.2.2)(zod@3.25.76)': + '@walletconnect/utils@2.23.2(typescript@5.8.2)(zod@3.25.76)': + dependencies: + '@msgpack/msgpack': 3.1.2 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.2 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + blakejs: 1.2.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + ox: 0.9.3(typescript@5.8.2)(zod@3.25.76) + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - typescript + - uploadthing + - zod + + '@walletconnect/utils@2.23.6(typescript@5.2.2)(zod@3.25.76)': + dependencies: + '@msgpack/msgpack': 3.1.3 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.6 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + blakejs: 1.2.1 + detect-browser: 5.3.0 + ox: 0.9.3(typescript@5.2.2)(zod@3.25.76) + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - typescript + - uploadthing + - zod + + '@walletconnect/utils@2.23.7(typescript@5.2.2)(zod@3.25.76)': dependencies: '@msgpack/msgpack': 3.1.3 '@noble/ciphers': 1.3.0 @@ -30562,7 +32694,7 @@ snapshots: '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.23.6 + '@walletconnect/types': 2.23.7 '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 blakejs: 1.2.1 @@ -30592,7 +32724,7 @@ snapshots: - uploadthing - zod - '@walletconnect/utils@2.23.7(typescript@5.2.2)(zod@3.25.76)': + '@walletconnect/utils@2.23.7(typescript@5.8.2)(zod@3.25.76)': dependencies: '@msgpack/msgpack': 3.1.3 '@noble/ciphers': 1.3.0 @@ -30611,7 +32743,7 @@ snapshots: '@walletconnect/window-metadata': 1.0.1 blakejs: 1.2.1 detect-browser: 5.3.0 - ox: 0.9.3(typescript@5.2.2)(zod@3.25.76) + ox: 0.9.3(typescript@5.8.2)(zod@3.25.76) uint8arrays: 3.1.1 transitivePeerDependencies: - '@azure/app-configuration' @@ -30839,9 +32971,9 @@ snapshots: fs-extra: 10.1.0 yargs: 17.7.2 - abitype@0.7.1(typescript@5.2.2)(zod@3.25.76): + abitype@0.7.1(typescript@5.8.2)(zod@3.25.76): dependencies: - typescript: 5.2.2 + typescript: 5.8.2 optionalDependencies: zod: 3.25.76 @@ -30850,6 +32982,12 @@ snapshots: typescript: 5.2.2 zod: 3.25.76 + abitype@1.0.6(typescript@5.8.2)(zod@3.25.76): + optionalDependencies: + typescript: 5.8.2 + zod: 3.25.76 + optional: true + abitype@1.0.8(typescript@5.2.2)(zod@3.25.76): optionalDependencies: typescript: 5.2.2 @@ -30870,6 +33008,11 @@ snapshots: typescript: 5.2.2 zod: 4.3.6 + abitype@1.2.3(typescript@5.8.2)(zod@3.25.76): + optionalDependencies: + typescript: 5.8.2 + zod: 3.25.76 + abstract-leveldown@2.6.3: dependencies: xtend: 4.0.2 @@ -30930,6 +33073,10 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 3.25.76 + ajv-draft-04@1.0.0(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv-formats@2.1.1(ajv@8.11.2): optionalDependencies: ajv: 8.11.2 @@ -30938,6 +33085,10 @@ snapshots: optionalDependencies: ajv: 8.18.0 + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv-keywords@5.1.0(ajv@8.18.0): dependencies: ajv: 8.18.0 @@ -31005,6 +33156,8 @@ snapshots: transitivePeerDependencies: - encoding + alien-signals@0.4.14: {} + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -32278,6 +34431,10 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 + confbox@0.1.8: {} + + confbox@0.2.4: {} + confusing-browser-globals@1.0.11: {} consola@2.15.3: {} @@ -32642,6 +34799,8 @@ snapshots: dayjs@1.11.19: {} + de-indent@1.0.2: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -32654,10 +34813,6 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.4.3: - dependencies: - ms: 2.1.3 - debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 @@ -32800,6 +34955,8 @@ snapshots: diff@5.2.2: {} + diff@8.0.3: {} + diffie-hellman@5.0.3: dependencies: bn.js: 4.12.3 @@ -32928,6 +35085,14 @@ snapshots: transitivePeerDependencies: - typescript + ecpair@3.0.1(typescript@5.8.2): + dependencies: + uint8array-tools: 0.0.8 + valibot: 1.2.0(typescript@5.8.2) + wif: 5.0.0 + transitivePeerDependencies: + - typescript + ecurve@1.0.6: dependencies: bigi: 1.4.2 @@ -33020,7 +35185,7 @@ snapshots: engine.io-client@6.6.4(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) engine.io-parser: 5.2.3 ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) xmlhttprequest-ssl: 2.1.2 @@ -33392,7 +35557,7 @@ snapshots: lodash: 4.17.23 string-natural-compare: 3.0.1 - eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): dependencies: '@typescript-eslint/types': 8.56.1 comment-parser: 1.4.5 @@ -33405,7 +35570,7 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.2.2) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@5.8.2) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color @@ -33449,6 +35614,16 @@ snapshots: - supports-color - typescript + eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2): + dependencies: + '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.1)(typescript@5.8.2) + eslint: 8.57.1 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2) + transitivePeerDependencies: + - supports-color + - typescript + eslint-plugin-jsdoc@50.8.0(eslint@8.57.1): dependencies: '@es-joy/jsdoccomment': 0.50.2 @@ -33574,7 +35749,7 @@ snapshots: ajv: 6.14.0 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -34121,6 +36296,8 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.0.8: {} + ext@1.7.0: dependencies: type: 2.7.3 @@ -34314,7 +36491,7 @@ snapshots: follow-redirects@1.15.11(debug@4.4.3): optionalDependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) for-each@0.3.5: dependencies: @@ -34606,6 +36783,18 @@ snapshots: - '@gql.tada/vue-support' - graphql + gql.tada@1.9.0(graphql@16.13.0)(typescript@5.8.2): + dependencies: + '@0no-co/graphql.web': 1.2.0(graphql@16.13.0) + '@0no-co/graphqlsp': 1.15.2(graphql@16.13.0)(typescript@5.8.2) + '@gql.tada/cli-utils': 1.7.2(@0no-co/graphqlsp@1.15.2(graphql@16.13.0)(typescript@5.2.2))(graphql@16.13.0)(typescript@5.8.2) + '@gql.tada/internal': 1.0.8(graphql@16.13.0)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - '@gql.tada/svelte-support' + - '@gql.tada/vue-support' + - graphql + graceful-fs@4.2.11: {} graceful-readlink@1.0.1: {} @@ -34708,12 +36897,12 @@ snapshots: hard-rejection@2.1.0: {} - hardhat-watcher@2.5.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10)): + hardhat-watcher@2.5.0(hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10)): dependencies: chokidar: 3.6.0 - hardhat: 2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10) + hardhat: 2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10) - hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2))(typescript@5.2.2)(utf-8-validate@5.0.10): + hardhat@2.28.6(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2))(typescript@5.8.2)(utf-8-validate@5.0.10): dependencies: '@ethereumjs/util': 9.1.0 '@ethersproject/abi': 5.8.0 @@ -34755,8 +36944,8 @@ snapshots: uuid: 8.3.2 ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: - ts-node: 10.9.2(@types/node@25.3.5)(typescript@5.2.2) - typescript: 5.2.2 + ts-node: 10.9.2(@types/node@25.3.5)(typescript@5.8.2) + typescript: 5.8.2 transitivePeerDependencies: - bufferutil - supports-color @@ -34927,7 +37116,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -34976,7 +37165,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -35019,6 +37208,8 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-lazy@4.0.0: {} + imurmurhash@0.1.4: {} indent-string@4.0.0: {} @@ -35467,10 +37658,12 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.3.5 + '@types/node': 22.19.13 merge-stream: 2.0.0 supports-color: 8.1.1 + jju@1.4.0: {} + jose@4.15.9: {} jose@6.1.3: {} @@ -35697,6 +37890,8 @@ snapshots: kleur@4.1.5: {} + kolorist@1.8.0: {} + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -35802,6 +37997,12 @@ snapshots: loader-runner@4.3.1: {} + local-pkg@1.1.2: + dependencies: + mlly: 1.8.1 + pkg-types: 2.3.0 + quansync: 0.2.11 + localforage@1.10.0: dependencies: lie: 3.1.1 @@ -36355,7 +38556,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -36410,6 +38611,10 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} + minimatch@10.2.3: + dependencies: + brace-expansion: 5.0.4 + minimatch@10.2.4: dependencies: brace-expansion: 5.0.4 @@ -36422,6 +38627,10 @@ snapshots: dependencies: brace-expansion: 2.0.2 + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.2 + minimist-options@4.1.0: dependencies: arrify: 1.0.1 @@ -36440,6 +38649,10 @@ snapshots: optionalDependencies: typescript: 5.2.2 + mipd@0.0.7(typescript@5.8.2): + optionalDependencies: + typescript: 5.8.2 + mitt@2.1.0: {} mitt@3.0.1: {} @@ -36458,6 +38671,13 @@ snapshots: mkdirp@1.0.4: {} + mlly@1.8.1: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.3 + mnemonist@0.38.5: dependencies: obliterator: 2.0.5 @@ -36582,6 +38802,8 @@ snapshots: - encoding - supports-color + muggle-string@0.4.1: {} + multibase@4.0.6: dependencies: '@multiformats/base-x': 4.0.1 @@ -37046,6 +39268,21 @@ snapshots: transitivePeerDependencies: - zod + ox@0.11.1(typescript@5.8.2)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.8.2)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - zod + ox@0.12.4(typescript@5.2.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -37075,11 +39312,25 @@ snapshots: transitivePeerDependencies: - zod + ox@0.4.4(typescript@5.8.2)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.8.2)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - zod + ox@0.6.7(typescript@5.2.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 abitype: 1.0.8(typescript@5.2.2)(zod@3.25.76) @@ -37103,6 +39354,21 @@ snapshots: transitivePeerDependencies: - zod + ox@0.6.9(typescript@5.8.2)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.8.2)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - zod + optional: true + ox@0.9.17(typescript@5.2.2)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -37133,6 +39399,21 @@ snapshots: transitivePeerDependencies: - zod + ox@0.9.3(typescript@5.8.2)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.8.2)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - zod + ox@0.9.6(typescript@5.2.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -37392,6 +39673,18 @@ snapshots: dependencies: find-up: 5.0.0 + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.1 + pathe: 2.0.3 + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.4 + exsolve: 1.0.8 + pathe: 2.0.3 + playwright-core@1.58.2: {} playwright@1.58.2: @@ -37690,6 +39983,8 @@ snapshots: qs@6.5.5: {} + quansync@0.2.11: {} + query-string@6.13.5: dependencies: decode-uri-component: 0.2.2 @@ -38573,6 +40868,10 @@ snapshots: dependencies: lru-cache: 6.0.0 + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + semver@7.7.1: {} semver@7.7.2: {} @@ -38761,7 +41060,7 @@ snapshots: dependencies: '@kwsites/file-exists': 1.1.1 '@kwsites/promise-deferred': 1.1.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -38798,7 +41097,7 @@ snapshots: socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) engine.io-client: 6.6.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) socket.io-parser: 4.2.5 transitivePeerDependencies: @@ -38809,7 +41108,7 @@ snapshots: socket.io-parser@4.2.5: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -38962,6 +41261,23 @@ snapshots: - typescript - zod + starknet@9.4.0(typescript@5.8.2)(zod@3.25.76): + dependencies: + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/base': 1.2.6 + '@scure/starknet': 1.1.0 + '@starknet-io/get-starknet-wallet-standard': 5.0.0(typescript@5.8.2)(zod@3.25.76) + '@starknet-io/starknet-types-010': '@starknet-io/types-js@0.10.0' + '@starknet-io/starknet-types-09': '@starknet-io/types-js@0.9.2' + abi-wan-kanabi: 2.2.4 + lossless-json: 4.3.0 + pako: 2.1.0 + ts-mixer: 6.0.4 + transitivePeerDependencies: + - typescript + - zod + statuses@1.5.0: {} statuses@2.0.2: {} @@ -39005,6 +41321,8 @@ snapshots: strict-uri-encode@2.0.0: {} + string-argv@0.3.2: {} + string-natural-compare@3.0.1: {} string-width@3.1.0: @@ -39221,6 +41539,26 @@ snapshots: - typescript - utf-8-validate + tendermint-tx-builder@1.0.16(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10): + dependencies: + '@bithighlander/bitcoin-cash-js-lib': 5.2.1(patch_hash=4214515c36a0bb955f62f20fefed1ccf057aa1d452ca41df28201db0f9b83441) + '@pioneer-platform/loggerdog': 8.11.0(@types/node@15.14.9) + '@pioneer-platform/pioneer-coins': 8.1.90(@types/node@15.14.9) + '@shapeshiftoss/hdwallet-core': 1.62.41(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10) + '@types/node': 15.14.9 + bip39: 3.1.0 + codeclimate-test-reporter: 0.5.1 + fiosdk-offline: 1.2.21 + google-protobuf: 3.21.4 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - bufferutil + - debug + - encoding + - typescript + - utf-8-validate + terser-webpack-plugin@5.3.17(webpack@5.105.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -39401,6 +41739,10 @@ snapshots: dependencies: typescript: 5.2.2 + ts-api-utils@2.4.0(typescript@5.8.2): + dependencies: + typescript: 5.8.2 + ts-mixer@6.0.4: {} ts-morph@13.0.3: @@ -39426,7 +41768,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-node@10.9.2(@types/node@25.3.5)(typescript@5.2.2): + ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 @@ -39440,7 +41782,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.4 make-error: 1.3.6 - typescript: 5.2.2 + typescript: 5.8.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optional: true @@ -39506,6 +41848,11 @@ snapshots: tslib: 1.14.1 typescript: 5.2.2 + tsutils@3.21.0(typescript@5.8.2): + dependencies: + tslib: 1.14.1 + typescript: 5.8.2 + tsx@4.21.0: dependencies: esbuild: 0.27.3 @@ -39614,6 +41961,8 @@ snapshots: typescript@5.2.2: {} + typescript@5.8.2: {} + typeson-registry@1.0.0-alpha.39: dependencies: base64-arraybuffer-es6: 0.7.0 @@ -39674,7 +42023,8 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.18.2: {} + undici-types@7.18.2: + optional: true undici-types@7.22.0: {} @@ -39915,6 +42265,10 @@ snapshots: optionalDependencies: typescript: 5.2.2 + valibot@0.38.0(typescript@5.8.2): + optionalDependencies: + typescript: 5.8.2 + valibot@0.42.1(typescript@5.2.2): optionalDependencies: typescript: 5.2.2 @@ -39923,6 +42277,10 @@ snapshots: optionalDependencies: typescript: 5.2.2 + valibot@1.2.0(typescript@5.8.2): + optionalDependencies: + typescript: 5.8.2 + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -40088,6 +42446,40 @@ snapshots: - utf-8-validate - zod + viem@2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.8.2)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.11.1(typescript@5.8.2)(zod@3.25.76) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.43.5(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.8.2)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + ox: 0.11.1(typescript@5.8.2)(zod@3.25.76) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.46.3(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 @@ -40107,16 +42499,15 @@ snapshots: vite-bundle-analyzer@0.18.1: {} - vite-node@3.0.9(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.0.9(@types/node@22.19.13)(terser@5.46.0): dependencies: cac: 6.7.14 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 5.4.21(@types/node@22.19.13)(terser@5.46.0) transitivePeerDependencies: - '@types/node' - - jiti - less - lightningcss - sass @@ -40125,19 +42516,16 @@ snapshots: - sugarss - supports-color - terser - - tsx - - yaml - vite-node@3.0.9(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.0.9(@types/node@25.3.5)(terser@5.46.0): dependencies: cac: 6.7.14 debug: 4.4.3(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 5.4.21(@types/node@25.3.5)(terser@5.46.0) transitivePeerDependencies: - '@types/node' - - jiti - less - lightningcss - sass @@ -40146,8 +42534,6 @@ snapshots: - sugarss - supports-color - terser - - tsx - - yaml vite-plugin-checker@0.9.3(eslint@8.57.1)(optionator@0.9.4)(typescript@5.2.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): dependencies: @@ -40166,6 +42552,25 @@ snapshots: optionator: 0.9.4 typescript: 5.2.2 + vite-plugin-dts@4.5.4(@types/node@25.3.5)(rollup@4.59.0)(typescript@5.2.2)(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)): + dependencies: + '@microsoft/api-extractor': 7.57.7(@types/node@25.3.5) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@volar/typescript': 2.4.28 + '@vue/language-core': 2.2.0(typescript@5.2.2) + compare-versions: 6.1.1 + debug: 4.4.3(supports-color@8.1.1) + kolorist: 1.8.0 + local-pkg: 1.1.2 + magic-string: 0.30.21 + typescript: 5.2.2 + optionalDependencies: + vite: 5.4.21(@types/node@25.3.5)(terser@5.46.0) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + vite-plugin-node-polyfills@0.23.0(rollup@4.59.0)(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.59.0) @@ -40192,7 +42597,7 @@ snapshots: vite-tsconfig-paths@5.1.4(typescript@5.2.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.2.2) optionalDependencies: @@ -40201,6 +42606,16 @@ snapshots: - supports-color - typescript + vite@5.4.21(@types/node@22.19.13)(terser@5.46.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.59.0 + optionalDependencies: + '@types/node': 22.19.13 + fsevents: 2.3.3 + terser: 5.46.0 + vite@5.4.21(@types/node@25.3.5)(terser@5.46.0): dependencies: esbuild: 0.21.5 @@ -40241,17 +42656,17 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.19.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0): dependencies: '@vitest/expect': 3.0.9 - '@vitest/mocker': 3.0.9(msw@0.36.8)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.0.9(msw@0.36.8)(vite@5.4.21(@types/node@22.19.13)(terser@5.46.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.0.9 '@vitest/snapshot': 3.0.9 '@vitest/spy': 3.0.9 '@vitest/utils': 3.0.9 chai: 5.3.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -40260,8 +42675,8 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.0.9(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 5.4.21(@types/node@22.19.13)(terser@5.46.0) + vite-node: 3.0.9(@types/node@22.19.13)(terser@5.46.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -40269,7 +42684,6 @@ snapshots: happy-dom: 20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) jsdom: 28.0.0(@noble/hashes@2.0.1) transitivePeerDependencies: - - jiti - less - lightningcss - msw @@ -40279,13 +42693,11 @@ snapshots: - sugarss - supports-color - terser - - tsx - - yaml - vitest@3.0.9(@types/debug@4.1.12)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.27.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.0.9(@types/debug@4.1.12)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.27.2)(terser@5.46.0): dependencies: '@vitest/expect': 3.0.9 - '@vitest/mocker': 3.0.9(msw@0.27.2)(vite@6.4.1(@types/node@22.19.13)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.0.9(msw@0.27.2)(vite@5.4.21(@types/node@25.3.5)(terser@5.46.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.0.9 '@vitest/snapshot': 3.0.9 @@ -40301,8 +42713,8 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.4.1(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.0.9(@types/node@25.3.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 5.4.21(@types/node@25.3.5)(terser@5.46.0) + vite-node: 3.0.9(@types/node@25.3.5)(terser@5.46.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -40310,7 +42722,6 @@ snapshots: happy-dom: 20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) jsdom: 28.0.0(@noble/hashes@2.0.1) transitivePeerDependencies: - - jiti - less - lightningcss - msw @@ -40320,8 +42731,6 @@ snapshots: - sugarss - supports-color - terser - - tsx - - yaml vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(jsdom@28.0.0(@noble/hashes@2.0.1))(msw@0.36.8)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: @@ -40484,9 +42893,9 @@ snapshots: '@ethersproject/abi': 5.8.0 web3-utils: 1.10.4 - web3-eth-abi@4.1.4-dev.a0d6730.0(typescript@5.2.2)(zod@3.25.76): + web3-eth-abi@4.1.4-dev.a0d6730.0(typescript@5.8.2)(zod@3.25.76): dependencies: - abitype: 0.7.1(typescript@5.2.2)(zod@3.25.76) + abitype: 0.7.1(typescript@5.8.2)(zod@3.25.76) web3-errors: 1.1.4-dev.a0d6730.0 web3-types: 1.3.1-dev.a0d6730.0 web3-utils: 4.0.8-dev.a0d6730.0 @@ -40505,12 +42914,12 @@ snapshots: web3-utils: 4.0.8-dev.a0d6730.0 web3-validator: 2.0.4-dev.a0d6730.0 - web3-eth-contract@4.1.2-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76): + web3-eth-contract@4.1.2-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76): dependencies: web3-core: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) web3-errors: 1.1.4-dev.a0d6730.0 - web3-eth: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - web3-eth-abi: 4.1.4-dev.a0d6730.0(typescript@5.2.2)(zod@3.25.76) + web3-eth: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) + web3-eth-abi: 4.1.4-dev.a0d6730.0(typescript@5.8.2)(zod@3.25.76) web3-types: 1.3.1-dev.a0d6730.0 web3-utils: 4.0.8-dev.a0d6730.0 web3-validator: 2.0.4-dev.a0d6730.0 @@ -40521,13 +42930,13 @@ snapshots: - utf-8-validate - zod - web3-eth-ens@4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76): + web3-eth-ens@4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 web3-core: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) web3-errors: 1.1.4-dev.a0d6730.0 - web3-eth: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - web3-eth-contract: 4.1.2-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + web3-eth: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) + web3-eth-contract: 4.1.2-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) web3-net: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) web3-types: 1.3.1-dev.a0d6730.0 web3-utils: 4.0.8-dev.a0d6730.0 @@ -40546,10 +42955,10 @@ snapshots: web3-utils: 4.0.8-dev.a0d6730.0 web3-validator: 2.0.4-dev.a0d6730.0 - web3-eth-personal@4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76): + web3-eth-personal@4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76): dependencies: web3-core: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) - web3-eth: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + web3-eth: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) web3-rpc-methods: 1.1.4-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) web3-types: 1.3.1-dev.a0d6730.0 web3-utils: 4.0.8-dev.a0d6730.0 @@ -40561,12 +42970,12 @@ snapshots: - utf-8-validate - zod - web3-eth@4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76): + web3-eth@4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76): dependencies: setimmediate: 1.0.5 web3-core: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) web3-errors: 1.1.4-dev.a0d6730.0 - web3-eth-abi: 4.1.4-dev.a0d6730.0(typescript@5.2.2)(zod@3.25.76) + web3-eth-abi: 4.1.4-dev.a0d6730.0(typescript@5.8.2)(zod@3.25.76) web3-eth-accounts: 4.1.1-dev.a0d6730.0 web3-net: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) web3-providers-ws: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) @@ -40689,17 +43098,17 @@ snapshots: web3-types: 1.3.1-dev.a0d6730.0 zod: 3.25.76 - web3@4.2.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76): + web3@4.2.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76): dependencies: web3-core: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) web3-errors: 1.1.4-dev.a0d6730.0 - web3-eth: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - web3-eth-abi: 4.1.4-dev.a0d6730.0(typescript@5.2.2)(zod@3.25.76) + web3-eth: 4.3.1-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) + web3-eth-abi: 4.1.4-dev.a0d6730.0(typescript@5.8.2)(zod@3.25.76) web3-eth-accounts: 4.1.1-dev.a0d6730.0 - web3-eth-contract: 4.1.2-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) - web3-eth-ens: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + web3-eth-contract: 4.1.2-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) + web3-eth-ens: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) web3-eth-iban: 4.0.8-dev.a0d6730.0 - web3-eth-personal: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.2.2)(utf-8-validate@6.0.6)(zod@3.25.76) + web3-eth-personal: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(typescript@5.8.2)(utf-8-validate@6.0.6)(zod@3.25.76) web3-net: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) web3-providers-http: 4.1.1-dev.a0d6730.0 web3-providers-ws: 4.0.8-dev.a0d6730.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 06ccf60e070..f5a933b4b78 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ nodeLinker: hoisted shamefullyHoist: true +childConcurrency: 1 strictPeerDependencies: true autoInstallPeers: true packages: diff --git a/scripts/release.ts b/scripts/release.ts index 464266221fb..0d643891fd0 100644 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -239,7 +239,7 @@ const isCcrAvailable = (): Promise => { }) } -const CLAUDE_ARGS = ['-p', '--model', 'opus', '--max-turns', '3'] +const CLAUDE_ARGS = ['-p', '--model', 'opus', '--max-turns', '10'] const runClaude = async (promptPath: string): Promise => { try { diff --git a/src/Routes/RoutesCommon.tsx b/src/Routes/RoutesCommon.tsx index d7d1e92e162..9fffb7aa82a 100644 --- a/src/Routes/RoutesCommon.tsx +++ b/src/Routes/RoutesCommon.tsx @@ -18,6 +18,7 @@ import { LimitOrderRoutePaths } from '@/components/MultiHopTrade/components/Limi import { TradeRoutePaths } from '@/components/MultiHopTrade/types' import { getConfig } from '@/config' import { assetIdPaths } from '@/hooks/useRouteAssetId/useRouteAssetId' +import { isLocalDev } from '@/lib/isLocalDev' import { Accounts } from '@/pages/Accounts/Accounts' import { ExploreCategory } from '@/pages/Explore/ExploreCategory' import { FoxEcosystemPage } from '@/pages/Fox/FoxEcosystemPage' @@ -411,7 +412,7 @@ export const routes: Route[] = [ path: '/flags', label: 'navBar.featureFlags', icon: , - hide: window.location.hostname !== 'localhost', + hide: !isLocalDev(), main: Flags, }, { diff --git a/src/assets/translations/en/main.json b/src/assets/translations/en/main.json index c128ad3c49a..3b4d7a3618d 100644 --- a/src/assets/translations/en/main.json +++ b/src/assets/translations/en/main.json @@ -1820,6 +1820,22 @@ "button": "Pair" } }, + "nativeMultichain": { + "title": "MetaMask now natively supports %{chains}", + "subtitle": "You can now use %{chains} directly through MetaMask without the ShapeShift Multichain Snap.", + "useNative": "Use native multichain", + "useNativeRecommended": "Recommended", + "keepSnap": "Keep using Snap", + "snapChainWarning": "Choosing native multichain will replace snap-provided chain accounts. You can switch back in wallet settings anytime.", + "switchWarning": "Switching will re-derive your non-EVM accounts. Your EVM accounts and balances are not affected.", + "nativeActive": "Native Multichain", + "snapActive": "Multichain Snap", + "switchToNative": "Switch to native multichain", + "switchToSnap": "Switch back to Snap", + "switchConfirmTitle": "Switch multichain mode", + "switchConfirmBody": "This will re-derive your non-EVM accounts using the new mode. Your EVM accounts are not affected.", + "switchConfirmAction": "Switch" + }, "metaMaskSnap": { "title": "Multichain support is now available for MetaMask!", "subtitle": "Add the Multichain Snap on Metamask to send, receive, track, trade, and earn with the following chains:", @@ -2422,6 +2438,8 @@ } }, "chainflipLending": { + "awaitingConfirmTitle": "Awaiting Confirmation", + "awaitingConfirmDescription": "Confirm the transaction in your wallet to proceed", "headerDescription": "Supply assets to earn yield or borrow against collateral on Chainflip.", "overview": "Overview", "myDashboard": "My Dashboard", @@ -2441,6 +2459,7 @@ "successTitle": "Supply Successful", "successDescription": "Your %{asset} is now earning yield", "supplied": "Supplied", + "transactionId": "Transaction ID", "errorTitle": "Supply Failed", "errorDescription": "Something went wrong while supplying", "steps": { @@ -2464,6 +2483,7 @@ "successTitle": "Borrow Successful", "successDescription": "You borrowed %{amount} %{asset}", "borrowed": "Borrowed", + "transactionId": "Transaction ID", "errorTitle": "Borrow Failed", "errorDescription": "Something went wrong while borrowing", "steps": { @@ -2496,8 +2516,7 @@ "currentLtv": "Current LTV", "borrowCapacity": "Borrow Capacity", "borrowPowerUsed": "Borrow Power Used", - "availableToBorrow": "Available to Borrow", - "actionPaused": "This action is temporarily paused on Chainflip" + "availableToBorrow": "Available to Borrow" }, "supplyApy": "Supply APY", "supplyApyTooltip": "Annual percentage yield earned by supplying assets to this pool.", @@ -2531,6 +2550,7 @@ "successRemoveDescription": "%{asset} collateral removed successfully", "added": "Added", "removed": "Removed", + "transactionId": "Transaction ID", "errorTitle": "Collateral Update Failed", "errorDescription": "Something went wrong", "steps": { @@ -2558,14 +2578,16 @@ "explainer": "Assets will be deposited to your Chainflip State Chain account. Once deposited, you can supply to lending pools to earn yield.", "openChannel": "Open Deposit Channel", "confirmTitle": "Confirm Deposit", - "confirmDescription": "Deposit %{amount} %{asset} to your Chainflip State Chain account. You will be prompted to sign twice: once to open the deposit channel, and once to send the transaction.", + "confirmDescription": "Deposit %{amount} %{asset} to your Chainflip State Chain account. You will be guided through each required step and prompted to sign transactions as needed.", "confirmingTitle": "Depositing...", "confirmingDescription": "Please confirm the transactions in your wallet.", "confirmAndDeposit": "Confirm & Deposit", "successTitle": "Deposit Confirmed", "successDescription": "Your %{asset} has been deposited to your Chainflip State Chain account.", "deposited": "Deposited", + "transactionId": "Transaction ID", "minimumDeposit": "Min. Deposit", + "belowMinimumDeposit": "Min. %{amount} %{symbol}", "steps": { "approvingFlip": "Approve FLIP", "fundingAccount": "Fund Account", @@ -2625,7 +2647,8 @@ "fullWithdrawalOnlyTooltip": "Your position is below the minimum required for partial withdrawal. Only full withdrawal is available.", "partialBelowMinimum": "Minimum withdrawal is %{amount}", "remainingBelowMinimum": "Remaining position must be at least %{amount} or withdraw the full amount", - "destinationAddress": "Destination address" + "destinationAddress": "Destination address", + "transactionId": "Transaction ID" }, "withdrawFromChainflip": { "title": "Withdraw from Chainflip", @@ -2660,6 +2683,7 @@ "successTitle": "Repayment Successful", "successDescription": "You repaid %{amount} %{asset}", "repaid": "Repaid", + "transactionId": "Transaction ID", "errorTitle": "Repayment Failed", "errorDescription": "Something went wrong while repaying", "steps": { @@ -2698,6 +2722,7 @@ "successTitle": "Withdrawal Submitted", "successDescription": "Your withdrawal of %{asset} has been submitted and will arrive in your wallet shortly", "withdrawn": "Withdrawn", + "receiveAddress": "Receive Address", "steps": { "signing": "Signing withdrawal transaction", "confirming": "Confirming withdrawal" diff --git a/src/components/FormHeader.tsx b/src/components/FormHeader.tsx index 4678ae0bf09..baaf785233c 100644 --- a/src/components/FormHeader.tsx +++ b/src/components/FormHeader.tsx @@ -1,4 +1,4 @@ -import { Flex } from '@chakra-ui/react' +import { Box, Flex } from '@chakra-ui/react' import { useCallback } from 'react' import { useTranslate } from 'react-polyglot' @@ -13,8 +13,14 @@ type FormHeaderProps = { items: FormHeaderItem[] setStepIndex: (index: number) => void activeIndex: number + rightElement?: React.ReactNode } -export const FormHeader: React.FC = ({ items, setStepIndex, activeIndex }) => { +export const FormHeader: React.FC = ({ + items, + setStepIndex, + activeIndex, + rightElement, +}) => { const translate = useTranslate() const handleClick = useCallback( (index: number) => { @@ -23,17 +29,20 @@ export const FormHeader: React.FC = ({ items, setStepIndex, act [setStepIndex], ) return ( - - {items.map(item => ( - - {translate(item.label)} - - ))} + + + {items.map(item => ( + + {translate(item.label)} + + ))} + + {rightElement != null && {rightElement}} ) } diff --git a/src/components/Layout/Header/ActionCenter/components/ChainflipLendingActionCard.tsx b/src/components/Layout/Header/ActionCenter/components/ChainflipLendingActionCard.tsx index b6222a88c46..979a28960f8 100644 --- a/src/components/Layout/Header/ActionCenter/components/ChainflipLendingActionCard.tsx +++ b/src/components/Layout/Header/ActionCenter/components/ChainflipLendingActionCard.tsx @@ -117,6 +117,11 @@ export const ChainflipLendingActionCard = ({ action }: ChainflipLendingActionCar } }, [chainflipLendingMetadata.operationType, translate]) + const txHashLink = useMemo(() => { + if (!chainflipLendingMetadata.txHash || !asset?.explorerTxLink) return undefined + return `${asset.explorerTxLink}${chainflipLendingMetadata.txHash}` + }, [chainflipLendingMetadata.txHash, asset?.explorerTxLink]) + const egressTxLink = useMemo(() => { if (!chainflipLendingMetadata.egressTxRef || !asset?.explorerTxLink) return undefined return `${asset.explorerTxLink}${chainflipLendingMetadata.egressTxRef}` @@ -164,9 +169,22 @@ export const ChainflipLendingActionCard = ({ action }: ChainflipLendingActionCar {translate('actionCenter.chainflipLending.details.transactionId')} - - {middleEllipsis(chainflipLendingMetadata.txHash)} - + {txHashLink ? ( + + {middleEllipsis(chainflipLendingMetadata.txHash)} + + ) : ( + + {middleEllipsis(chainflipLendingMetadata.txHash)} + + )} )} {chainflipLendingMetadata.egressTxRef && ( @@ -205,6 +223,7 @@ export const ChainflipLendingActionCard = ({ action }: ChainflipLendingActionCar chainflipLendingMetadata.egressTxRef, chainflipLendingMetadata.txHash, egressTxLink, + txHashLink, operationLabel, operationTestIdSuffix, translate, diff --git a/src/components/Layout/Header/ActionCenter/components/Notifications/ChainflipLendingNotification.tsx b/src/components/Layout/Header/ActionCenter/components/Notifications/ChainflipLendingNotification.tsx index c04a6522697..ecf7e9acaca 100644 --- a/src/components/Layout/Header/ActionCenter/components/Notifications/ChainflipLendingNotification.tsx +++ b/src/components/Layout/Header/ActionCenter/components/Notifications/ChainflipLendingNotification.tsx @@ -22,7 +22,6 @@ export const ChainflipLendingNotification = ({ handleClick, actionId, onClose, - status, }: ChainflipLendingNotificationProps) => { const actionsById = useAppSelector(actionSlice.selectors.selectActionsById) @@ -80,15 +79,5 @@ export const ChainflipLendingNotification = ({ if (!action || !icon || !title) return null - const toastStatus = status === 'loading' ? 'info' : status - - return ( - - ) + return } diff --git a/src/components/Modals/ChainflipLending/ChainflipLendingModal.tsx b/src/components/Modals/ChainflipLending/ChainflipLendingModal.tsx index bbcd9b734a1..f509f9d8b53 100644 --- a/src/components/Modals/ChainflipLending/ChainflipLendingModal.tsx +++ b/src/components/Modals/ChainflipLending/ChainflipLendingModal.tsx @@ -1,11 +1,19 @@ -import { Card } from '@chakra-ui/react' +import { Card, Flex } from '@chakra-ui/react' import { lazy, Suspense, useMemo } from 'react' +import { useTranslate } from 'react-polyglot' import type { ChainflipLendingModalProps } from './types' import { CircularProgress } from '@/components/CircularProgress/CircularProgress' import { Dialog } from '@/components/Modal/components/Dialog' +import { DialogBody } from '@/components/Modal/components/DialogBody' import { DialogCloseButton } from '@/components/Modal/components/DialogCloseButton' +import { + DialogHeader, + DialogHeaderMiddle, + DialogHeaderRight, +} from '@/components/Modal/components/DialogHeader' +import { RawText } from '@/components/Text' import { useModal } from '@/hooks/useModal/useModal' import { ChainflipLendingAccountProvider } from '@/pages/ChainflipLending/ChainflipLendingAccountContext' @@ -59,7 +67,23 @@ const VoluntaryLiquidation = lazy(() => ), ) -const suspenseFallback = +const suspenseFallback = ( + + + +) + +const MODAL_TITLE_KEYS: Record = { + supply: 'chainflipLending.supply.title', + withdrawSupply: 'chainflipLending.withdraw.title', + deposit: 'chainflipLending.depositToChainflip', + withdrawFromChainflip: 'chainflipLending.egress.title', + addCollateral: 'chainflipLending.collateral.title', + removeCollateral: 'chainflipLending.collateral.title', + borrow: 'chainflipLending.borrow.title', + repay: 'chainflipLending.repay.title', + voluntaryLiquidation: 'chainflipLending.manageLoan', +} const ChainflipLendingModalContent = ({ mode, @@ -68,6 +92,8 @@ const ChainflipLendingModalContent = ({ liquidationAction, onClose, }: ChainflipLendingModalProps & { onClose: () => void }) => { + const translate = useTranslate() + const content = useMemo(() => { switch (mode) { case 'supply': @@ -94,10 +120,21 @@ const ChainflipLendingModalContent = ({ }, [mode, assetId, loanId, liquidationAction, onClose]) return ( - - - {content} - + <> + + + {translate(MODAL_TITLE_KEYS[mode])} + + + + + + + + {content} + + + ) } diff --git a/src/components/Modals/ManageAccounts/components/ImportAccounts.tsx b/src/components/Modals/ManageAccounts/components/ImportAccounts.tsx index 2720e29b03b..d137f5f9e7f 100644 --- a/src/components/Modals/ManageAccounts/components/ImportAccounts.tsx +++ b/src/components/Modals/ManageAccounts/components/ImportAccounts.tsx @@ -18,6 +18,7 @@ import type { AccountId, ChainId } from '@shapeshiftoss/caip' import { fromAccountId } from '@shapeshiftoss/caip' import { isMetaMask } from '@shapeshiftoss/hdwallet-core/wallet' import { isLedger } from '@shapeshiftoss/hdwallet-ledger' +import { isMetaMaskNativeMultichain } from '@shapeshiftoss/hdwallet-metamask-multichain' import type { Asset } from '@shapeshiftoss/types' import { BigAmount } from '@shapeshiftoss/utils' import { useInfiniteQuery, useQueries, useQuery, useQueryClient } from '@tanstack/react-query' @@ -308,7 +309,8 @@ export const ImportAccounts = forwardRef useEffect(() => { if (queryEnabled) return - if (isMetaMaskMultichainWallet && !isSnapInstalled) return + if (isMetaMaskMultichainWallet && !isSnapInstalled && !isMetaMaskNativeMultichain(wallet)) + return if (!isLedgerWallet) { setIsAutoDiscovering(true) @@ -323,7 +325,14 @@ export const ImportAccounts = forwardRef setIsAutoDiscovering(true) setQueryEnabled(true) }) - }, [queryEnabled, isLedgerWallet, isMetaMaskMultichainWallet, isSnapInstalled, queryClient]) + }, [ + queryEnabled, + isLedgerWallet, + isMetaMaskMultichainWallet, + isSnapInstalled, + queryClient, + wallet, + ]) const accountIdsByChainId = useAppSelector(selectAccountIdsByChainId) const existingAccountIdsForChain = accountIdsByChainId[chainId] diff --git a/src/components/Modals/NativeMultichain/NativeMultichainModal.tsx b/src/components/Modals/NativeMultichain/NativeMultichainModal.tsx new file mode 100644 index 00000000000..0b0ab7d6846 --- /dev/null +++ b/src/components/Modals/NativeMultichain/NativeMultichainModal.tsx @@ -0,0 +1,38 @@ +import { Modal, ModalCloseButton, ModalContent, ModalOverlay } from '@chakra-ui/react' + +import { useModalRegistration } from '@/context/ModalStackProvider' +import { NativeMultichainContent } from '@/context/WalletProvider/MetaMask/components/NativeMultichainContent' +import { useNativeMultichainChoice } from '@/context/WalletProvider/MetaMask/hooks/useNativeMultichainChoice' +import { useModal } from '@/hooks/useModal/useModal' + +export type NativeMultichainModalProps = Record + +export const NativeMultichainModal: React.FC = () => { + const { close, isOpen } = useModal('nativeMultichain') + + const { modalProps, overlayProps, modalContentProps } = useModalRegistration({ + isOpen, + onClose: close, + }) + + const { hasSnap, chainAssets, handleUseNative, handleKeepSnap, isKeepSnapLoading } = + useNativeMultichainChoice({ + onDismiss: close, + }) + + return ( + + + + + + + + ) +} diff --git a/src/components/Modals/Snaps/Snaps.tsx b/src/components/Modals/Snaps/Snaps.tsx index 84e4ac038e3..7461b9eecd4 100644 --- a/src/components/Modals/Snaps/Snaps.tsx +++ b/src/components/Modals/Snaps/Snaps.tsx @@ -4,6 +4,7 @@ import { useCallback, useEffect } from 'react' import { SnapContentRouter } from './SnapContent' import { useModalRegistration } from '@/context/ModalStackProvider' +import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag' import { useIsSnapInstalled } from '@/hooks/useIsSnapInstalled/useIsSnapInstalled' import { useModal } from '@/hooks/useModal/useModal' @@ -12,6 +13,7 @@ export type SnapsModalProps = { } export const Snaps: React.FC = ({ isRemoved }) => { + const isMmNativeMultichain = useFeatureFlag('MmNativeMultichain') const { close, isOpen } = useModal('snaps') const { isSnapInstalled, isCorrectVersion } = useIsSnapInstalled() const { modalProps, overlayProps, modalContentProps } = useModalRegistration({ @@ -20,15 +22,20 @@ export const Snaps: React.FC = ({ isRemoved }) => { }) useEffect(() => { + if (isMmNativeMultichain) { + close() + return + } if (isSnapInstalled && isCorrectVersion) { close() } - }, [close, isCorrectVersion, isSnapInstalled]) + }, [close, isCorrectVersion, isMmNativeMultichain, isSnapInstalled]) const handleClose = useCallback(() => { close() }, [close]) + if (isMmNativeMultichain) return null if (isSnapInstalled === null) return null if (isCorrectVersion === null) return null diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx index 460b7e56137..3160cfc2d69 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx @@ -621,6 +621,9 @@ export const useTradeExecution = ( trackMixpanelEventOnExecute() return output }, + signTransaction: (txToSign: SolanaSignTx) => { + return adapter.signTransaction({ txToSign, wallet }) + }, }) cancelPollingRef.current = output?.cancelPolling return diff --git a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteOrRateInput.ts b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteOrRateInput.ts index 9795b816ce1..f6ef1ae4e95 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteOrRateInput.ts +++ b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteOrRateInput.ts @@ -34,6 +34,7 @@ export type GetTradeQuoteOrRateInputArgs = { sellAmountBeforeFeesCryptoPrecision: string allowMultiHop: boolean affiliateBps: string + affiliateAddress?: string isSnapInstalled?: boolean pubKey?: string | undefined quoteOrRate: 'quote' | 'rate' @@ -53,6 +54,7 @@ export const getTradeQuoteOrRateInput = async ({ sellAmountBeforeFeesCryptoPrecision, allowMultiHop, affiliateBps, + affiliateAddress, slippageTolerancePercentageDecimal, pubKey, }: GetTradeQuoteOrRateInputArgs): Promise => { @@ -68,6 +70,7 @@ export const getTradeQuoteOrRateInput = async ({ receiveAddress, accountNumber: sellAccountNumber, affiliateBps, + affiliateAddress, allowMultiHop, slippageTolerancePercentageDecimal, quoteOrRate: 'quote', @@ -82,6 +85,7 @@ export const getTradeQuoteOrRateInput = async ({ receiveAddress, accountNumber: sellAccountNumber, affiliateBps, + affiliateAddress, allowMultiHop, slippageTolerancePercentageDecimal, quoteOrRate: 'rate', diff --git a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx index 68d4a3f7226..35d05a12313 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx +++ b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx @@ -23,6 +23,7 @@ import { useGetSwapperTradeQuoteOrRate } from './hooks/useGetSwapperTradeQuoteOr import { useTradeReceiveAddress } from '@/components/MultiHopTrade/components/TradeInput/hooks/useTradeReceiveAddress' import { getTradeQuoteOrRateInput } from '@/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteOrRateInput' +import { useAffiliateTracking } from '@/hooks/useAffiliateTracking/useAffiliateTracking' import { useHasFocus } from '@/hooks/useHasFocus' import { useWallet } from '@/hooks/useWallet/useWallet' import { useWalletSupportsChain } from '@/hooks/useWalletSupportsChain/useWalletSupportsChain' @@ -104,6 +105,7 @@ export const useGetTradeQuotes = () => { const { manualReceiveAddress, walletReceiveAddress } = useTradeReceiveAddress() const receiveAddress = manualReceiveAddress ?? walletReceiveAddress const sellAmountCryptoPrecision = useAppSelector(selectInputSellAmountCryptoPrecision) + const affiliateAddress = useAffiliateTracking() const sellAccountId = useAppSelector(selectFirstHopSellAccountId) const buyAccountId = useAppSelector(selectLastHopBuyAccountId) @@ -205,7 +207,7 @@ export const useGetTradeQuotes = () => { sellAmountBeforeFeesCryptoPrecision: sellAmountCryptoPrecision, allowMultiHop: true, affiliateBps: getAffiliateBps(sellAsset, buyAsset), - // Pass in the user's slippage preference if it's set, else let the swapper use its default + affiliateAddress: affiliateAddress ?? undefined, slippageTolerancePercentageDecimal: userSlippageTolerancePercentageDecimal, pubKey: skipDeviceDerivation && sellAccountId @@ -217,6 +219,7 @@ export const useGetTradeQuotes = () => { } }, [ activeTrade, + affiliateAddress, buyAsset, dispatch, isFetchStep, @@ -247,6 +250,7 @@ export const useGetTradeQuotes = () => { isBuyAssetChainSupported, hopExecutionMetadata, activeTrade, + affiliateAddress, }, ], queryFn: queryFnOrSkip, diff --git a/src/components/MultiHopTrade/hooks/useGetTradeRateInput.ts b/src/components/MultiHopTrade/hooks/useGetTradeRateInput.ts index 94d75674001..9b3f3c1375e 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeRateInput.ts +++ b/src/components/MultiHopTrade/hooks/useGetTradeRateInput.ts @@ -9,6 +9,7 @@ import type { GetTradeQuoteOrRateInputArgs } from './useGetTradeQuotes/getTradeQ import { getTradeQuoteOrRateInput } from './useGetTradeQuotes/getTradeQuoteOrRateInput' import { KeyManager } from '@/context/WalletProvider/KeyManager' +import { useAffiliateTracking } from '@/hooks/useAffiliateTracking/useAffiliateTracking' import { useWallet } from '@/hooks/useWallet/useWallet' import { useWalletSupportsChain } from '@/hooks/useWalletSupportsChain/useWalletSupportsChain' import { getAffiliateBps } from '@/lib/fees/utils' @@ -79,6 +80,7 @@ export const useGetTradeRateInput = ({ const sellAccountNumber = sellAccountMetadata?.bip44Params?.accountNumber const affiliateBps = useMemo(() => getAffiliateBps(sellAsset, buyAsset), [sellAsset, buyAsset]) + const affiliateAddress = useAffiliateTracking() const walletType = useAppSelector(selectWalletType) @@ -106,11 +108,12 @@ export const useGetTradeRateInput = ({ sellAmountBeforeFeesCryptoPrecision: sellAmountCryptoPrecision, allowMultiHop: true, affiliateBps, - // Pass in the user's slippage preference if it's set, else let the swapper use its default + affiliateAddress: affiliateAddress ?? undefined, slippageTolerancePercentageDecimal: userSlippageTolerancePercentageDecimal, pubKey, }), [ + affiliateAddress, affiliateBps, buyAsset, pubKey, @@ -126,6 +129,7 @@ export const useGetTradeRateInput = ({ const tradeInputQueryKey = useMemo( () => ({ + affiliateAddress, buyAsset, sellAmountCryptoPrecision, sellAsset, @@ -140,6 +144,7 @@ export const useGetTradeRateInput = ({ receiveAddress, }), [ + affiliateAddress, buyAsset, isBuyAssetChainSupported, receiveAccountMetadata, diff --git a/src/components/QrCodeScanner/QrCodeScanner.tsx b/src/components/QrCodeScanner/QrCodeScanner.tsx index b62b61597e8..efffcdf7fac 100644 --- a/src/components/QrCodeScanner/QrCodeScanner.tsx +++ b/src/components/QrCodeScanner/QrCodeScanner.tsx @@ -1,4 +1,4 @@ -import { Alert, AlertIcon, Box, Button, Flex } from '@chakra-ui/react' +import { Alert, AlertIcon, Box, Button, Center, Flex, Spinner } from '@chakra-ui/react' import type { Html5QrcodeError, Html5QrcodeResult, @@ -6,7 +6,7 @@ import type { QrcodeSuccessCallback, } from 'html5-qrcode/cjs/core' import { Html5QrcodeErrorTypes } from 'html5-qrcode/cjs/core' -import { useCallback, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslate } from 'react-polyglot' import { DialogBackButton } from '../Modal/components/DialogBackButton' @@ -17,6 +17,12 @@ import { DialogHeader } from '@/components/Modal/components/DialogHeader' import { DialogTitle } from '@/components/Modal/components/DialogTitle' import { SlideTransition } from '@/components/SlideTransition' import { Text } from '@/components/Text' +import { openNativeQRScanner } from '@/context/WalletProvider/MobileWallet/mobileMessageHandlers' +import { + MobileFeature, + useMobileFeaturesCompatibility, +} from '@/hooks/useMobileFeaturesCompatibility' +import { isMobile } from '@/lib/globals' export type DOMExceptionCallback = (errorMessage: string) => void @@ -47,9 +53,46 @@ export const QrCodeScanner = ({ }) => { const translate = useTranslate() const [scanError, setScanError] = useState(null) + const mobileFeaturesCompatibility = useMobileFeaturesCompatibility() + const isNativeQrScannerCompatible = + mobileFeaturesCompatibility[MobileFeature.NativeQrScanner]?.isCompatible + const useNativeScanner = isMobile && isNativeQrScannerCompatible + const [isNativeScannerLoading, setIsNativeScannerLoading] = useState(useNativeScanner) const error = addressError ?? scanError + // Use native scanner when in mobile app with compatible version + useEffect(() => { + if (!useNativeScanner) return + + let isCancelled = false + setIsNativeScannerLoading(true) + + openNativeQRScanner() + .then(data => { + if (isCancelled) return + // Create a minimal result object for compatibility + const mockResult = { decodedText: data } as Html5QrcodeResult + onSuccess(data, mockResult) + }) + .catch(e => { + if (isCancelled) return + setIsNativeScannerLoading(false) + if (e.message === 'QR scan cancelled') { + onBack() + } else { + setScanError(e.message) + if (onError) { + onError(e.message, { type: Html5QrcodeErrorTypes.UNKWOWN_ERROR } as Html5QrcodeError) + } + } + }) + + return () => { + isCancelled = true + } + }, [useNativeScanner, onSuccess, onBack, onError]) + const handleScanSuccess: QrcodeSuccessCallback = useCallback( (decodedText, _result) => { onSuccess(decodedText, _result) @@ -73,6 +116,27 @@ export const QrCodeScanner = ({ const handlePermissionsButtonClick = useCallback(() => setScanError(null), []) + // Show loading state while waiting for native scanner + if (useNativeScanner && isNativeScannerLoading) { + return ( + + + + + + + {translate('modals.send.scanQrCode')} + + + +
+ +
+
+
+ ) + } + return ( diff --git a/src/config.ts b/src/config.ts index 6d7c24e5d39..3b0c41ce090 100644 --- a/src/config.ts +++ b/src/config.ts @@ -89,6 +89,7 @@ const validators = { VITE_MODE_NODE_URL: url(), VITE_SONEIUM_NODE_URL: url(), VITE_SOLANA_NODE_URL: url(), + VITE_JITO_BLOCK_ENGINE_URL: url(), VITE_STARKNET_NODE_URL: url(), VITE_TRON_NODE_URL: url(), VITE_SUI_NODE_URL: url(), @@ -307,6 +308,7 @@ const validators = { VITE_FEATURE_YIELD_MULTI_ACCOUNT: bool({ default: false }), VITE_FEATURE_PERFORMANCE_PROFILER: bool({ default: false }), VITE_FEATURE_AGENTIC_CHAT: bool({ default: false }), + VITE_FEATURE_MM_NATIVE_MULTICHAIN: bool({ default: false }), VITE_AGENTIC_SERVER_BASE_URL: url({ default: 'https://shapeshiftossagentic-server-production.up.railway.app', }), diff --git a/src/context/AppProvider/AppContext.tsx b/src/context/AppProvider/AppContext.tsx index 4b257bc26b3..4c9b215b27d 100644 --- a/src/context/AppProvider/AppContext.tsx +++ b/src/context/AppProvider/AppContext.tsx @@ -10,6 +10,7 @@ import { useTranslate } from 'react-polyglot' import { matchPath, useLocation } from 'react-router-dom' import { useDiscoverAccounts } from './hooks/useDiscoverAccounts' +import { useNativeMultichainAutoOpen } from './hooks/useNativeMultichainAutoOpen' import { usePortfolioFetch } from './hooks/usePortfolioFetch' import { useSnapStatusHandler } from './hooks/useSnapStatusHandler' @@ -93,6 +94,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => { useTransactionsSubscriber() useActionCenterSubscribers() useSnapStatusHandler() + useNativeMultichainAutoOpen() // Handle Ledger device connection state and wallet disconnection useLedgerConnectionState() diff --git a/src/context/AppProvider/hooks/useAccountMigration.ts b/src/context/AppProvider/hooks/useAccountMigration.ts new file mode 100644 index 00000000000..eeb5945b3e9 --- /dev/null +++ b/src/context/AppProvider/hooks/useAccountMigration.ts @@ -0,0 +1,28 @@ +import { useQueryClient } from '@tanstack/react-query' +import { useCallback } from 'react' + +import { portfolio } from '@/state/slices/portfolioSlice/portfolioSlice' +import type { WalletId } from '@/state/slices/portfolioSlice/portfolioSliceCommon' +import { useAppDispatch } from '@/state/store' + +export const useAccountMigration = () => { + const queryClient = useQueryClient() + const dispatch = useAppDispatch() + + const migrateAccounts = useCallback( + (walletId: WalletId) => { + // Clear all non-EVM accounts so they can be re-derived with the new derivation mode + dispatch(portfolio.actions.clearNonEvmAccountsForWallet(walletId)) + + // Invalidate the discover accounts query to trigger re-derivation + queryClient.invalidateQueries({ + queryKey: ['useDiscoverAccounts'], + exact: false, + refetchType: 'all', + }) + }, + [dispatch, queryClient], + ) + + return { migrateAccounts } +} diff --git a/src/context/AppProvider/hooks/useDiscoverAccounts.tsx b/src/context/AppProvider/hooks/useDiscoverAccounts.tsx index ed7a0301328..2cb5507d060 100644 --- a/src/context/AppProvider/hooks/useDiscoverAccounts.tsx +++ b/src/context/AppProvider/hooks/useDiscoverAccounts.tsx @@ -1,5 +1,6 @@ import { isGridPlus, isMetaMask } from '@shapeshiftoss/hdwallet-core/wallet' import { isLedger } from '@shapeshiftoss/hdwallet-ledger' +import { isMetaMaskNativeMultichain } from '@shapeshiftoss/hdwallet-metamask-multichain' import { isTrezor } from '@shapeshiftoss/hdwallet-trezor' import type { AccountMetadataById } from '@shapeshiftoss/types' import { useQueries } from '@tanstack/react-query' @@ -50,7 +51,10 @@ export const useDiscoverAccounts = () => { !wallet || shouldSkipAutoDiscovery || // Before connecting to MetaMask, isSnapInstalled is null then switch to false when the hook reacts, we would run the discovery 2 times - (connectedRdns === METAMASK_RDNS && isSnapInstalled === null) + // Native multichain wallets don't use snaps, so skip this guard for them + (connectedRdns === METAMASK_RDNS && + isSnapInstalled === null && + !isMetaMaskNativeMultichain(wallet)) ) { return { accountMetadataByAccountId: {}, hasActivity: false } } @@ -68,7 +72,13 @@ export const useDiscoverAccounts = () => { while (hasActivity) { if (accountNumber > 0) { if (!isMultiAccountWallet) break - if (isMetaMaskMultichainWallet && !isSnapInstalled) break + // Native multichain supports multi-account for BTC/SOL without snaps + if ( + isMetaMaskMultichainWallet && + !isSnapInstalled && + !isMetaMaskNativeMultichain(wallet) + ) + break } try { diff --git a/src/context/AppProvider/hooks/useNativeMultichainAutoOpen.ts b/src/context/AppProvider/hooks/useNativeMultichainAutoOpen.ts new file mode 100644 index 00000000000..e643ef067ce --- /dev/null +++ b/src/context/AppProvider/hooks/useNativeMultichainAutoOpen.ts @@ -0,0 +1,28 @@ +import { useEffect, useRef } from 'react' + +import { useModal } from '@/hooks/useModal/useModal' +import { useNativeMultichainPreference } from '@/hooks/useNativeMultichainPreference' +import { useWallet } from '@/hooks/useWallet/useWallet' + +export const useNativeMultichainAutoOpen = () => { + const { + state: { deviceId, isConnected, modal }, + } = useWallet() + const { shouldShowDeprecationModal } = useNativeMultichainPreference(deviceId) + const nativeMultichainModal = useModal('nativeMultichain') + const hasOpened = useRef(false) + + useEffect(() => { + if (!isConnected) { + hasOpened.current = false + return + } + if (!shouldShowDeprecationModal) return + if (hasOpened.current) return + // Don't auto-open if the wallet connect modal is open (the connect flow handles it via route) + if (modal) return + + hasOpened.current = true + nativeMultichainModal.open({}) + }, [isConnected, shouldShowDeprecationModal, nativeMultichainModal, modal, deviceId]) +} diff --git a/src/context/AppProvider/hooks/useSnapStatusHandler.tsx b/src/context/AppProvider/hooks/useSnapStatusHandler.tsx index f1b92c1d86b..917d47053cc 100644 --- a/src/context/AppProvider/hooks/useSnapStatusHandler.tsx +++ b/src/context/AppProvider/hooks/useSnapStatusHandler.tsx @@ -8,6 +8,7 @@ import { useNavigate } from 'react-router' import { useDiscoverAccounts } from './useDiscoverAccounts' import { WalletActions } from '@/context/WalletProvider/actions' +import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag' import { useIsSnapInstalled } from '@/hooks/useIsSnapInstalled/useIsSnapInstalled' import { useModal } from '@/hooks/useModal/useModal' import { useNotificationToast } from '@/hooks/useNotificationToast' @@ -20,6 +21,8 @@ import { portfolio } from '@/state/slices/portfolioSlice/portfolioSlice' import { useAppDispatch, useAppSelector } from '@/state/store' export const useSnapStatusHandler = () => { + const isMmNativeMultichain = useFeatureFlag('MmNativeMultichain') + const queryClient = useQueryClient() const appDispatch = useAppDispatch() const translate = useTranslate() @@ -39,6 +42,7 @@ export const useSnapStatusHandler = () => { const { isFetching: isDiscoveringAccounts } = useDiscoverAccounts() useEffect(() => { + if (isMmNativeMultichain) return if (isDiscoveringAccounts) return if (!isCorrectVersion && isSnapInstalled) return if (!currentWalletId) return @@ -104,5 +108,6 @@ export const useSnapStatusHandler = () => { navigate, enabledWalletAccountIds, isDiscoveringAccounts, + isMmNativeMultichain, ]) } diff --git a/src/context/ModalProvider/ModalContainer.tsx b/src/context/ModalProvider/ModalContainer.tsx index f0baf4cd29b..caff34bcd5a 100644 --- a/src/context/ModalProvider/ModalContainer.tsx +++ b/src/context/ModalProvider/ModalContainer.tsx @@ -231,6 +231,14 @@ const ManageHiddenAssetsModal = makeSuspenseful( ), ) +const NativeMultichainModal = makeSuspenseful( + lazy(() => + import('@/components/Modals/NativeMultichain/NativeMultichainModal').then( + ({ NativeMultichainModal }) => ({ default: NativeMultichainModal }), + ), + ), +) + const ChainflipLendingModal = makeSuspenseful( lazy(() => import('@/components/Modals/ChainflipLending/ChainflipLendingModal').then( @@ -269,6 +277,7 @@ export const MODALS: Modals = { walletDrawer: WalletDrawer, addressBookSave: AddressBookSaveModal, manageHiddenAssets: ManageHiddenAssetsModal, + nativeMultichain: NativeMultichainModal, chainflipLending: ChainflipLendingModal, } as const diff --git a/src/context/ModalProvider/types.ts b/src/context/ModalProvider/types.ts index c91debae451..fc027d2ead0 100644 --- a/src/context/ModalProvider/types.ts +++ b/src/context/ModalProvider/types.ts @@ -9,6 +9,7 @@ import type { AssetSearchModalProps } from '@/components/Modals/AssetSearch/Asse import type { ChainflipLendingModalProps } from '@/components/Modals/ChainflipLending/types' import type { FiatRampsModalProps } from '@/components/Modals/FiatRamps/FiatRampsModal' import type { LedgerOpenAppModalProps } from '@/components/Modals/LedgerOpenApp/LedgerOpenAppModal' +import type { NativeMultichainModalProps } from '@/components/Modals/NativeMultichain/NativeMultichainModal' import type { NativeOnboardingModalProps } from '@/components/Modals/NativeOnboarding/NativeOnboarding' import type { PopupWindowModalProps } from '@/components/Modals/PopupWindowModal' import type { QrCodeModalProps } from '@/components/Modals/QrCode/QrCode' @@ -46,6 +47,7 @@ export type Modals = { walletDrawer: FC addressBookSave: FC manageHiddenAssets: FC + nativeMultichain: FC chainflipLending: FC } diff --git a/src/context/WalletProvider/MetaMask/components/Connect.tsx b/src/context/WalletProvider/MetaMask/components/Connect.tsx index b32c754c36f..67cadc84b7d 100644 --- a/src/context/WalletProvider/MetaMask/components/Connect.tsx +++ b/src/context/WalletProvider/MetaMask/components/Connect.tsx @@ -14,6 +14,7 @@ import { getConfig } from '@/config' import { WalletActions } from '@/context/WalletProvider/actions' import { KeyManager } from '@/context/WalletProvider/KeyManager' import { useLocalWallet } from '@/context/WalletProvider/local-wallet' +import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag' import { checkIsMetaMaskDesktop, checkIsMetaMaskMobileWebView, @@ -37,6 +38,7 @@ export const MetaMaskConnect = () => { const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const showSnapModal = useSelector(preferences.selectors.selectShowSnapsModal) + const isMmNativeMultichain = useFeatureFlag('MmNativeMultichain') const detectedMipdProviders = useMipdProviders() const mipdProviders = useMemo( @@ -121,6 +123,18 @@ export const MetaMaskConnect = () => { // Wallets other than MM desktop don't support MM snaps if (!isMetaMaskDesktop || isMetaMaskMobileWebView) return dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) + + // With native multichain, show the native multichain choice step instead of snap install + // but only if the user hasn't already made a choice (stored preference) + if (isMmNativeMultichain) { + const storedPref = localStorage.getItem(`nativeMultichainPreference_${deviceId}`) + if (!storedPref) return navigate('/metamask/native-multichain') + if (storedPref === 'native') { + return dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) + } + // storedPref === 'snap' - fall through to existing snap install/update flow below + } + const isSnapInstalled = await checkIsSnapInstalled() const snapVersion = await getSnapVersion() @@ -162,6 +176,7 @@ export const MetaMaskConnect = () => { setErrorLoading, translate, isMetaMaskMobileWebView, + isMmNativeMultichain, showSnapModal, navigate, ]) diff --git a/src/context/WalletProvider/MetaMask/components/MetaMaskMenu.tsx b/src/context/WalletProvider/MetaMask/components/MetaMaskMenu.tsx index b23cbad8f6b..91b7adcdd31 100644 --- a/src/context/WalletProvider/MetaMask/components/MetaMaskMenu.tsx +++ b/src/context/WalletProvider/MetaMask/components/MetaMaskMenu.tsx @@ -3,27 +3,37 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslate } from 'react-polyglot' import { ManageAccountsMenuItem } from '@/components/Layout/Header/NavBar/ManageAccountsMenuItem' +import { useAccountMigration } from '@/context/AppProvider/hooks/useAccountMigration' +import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag' import { checkIsMetaMaskDesktop, useIsSnapInstalled, } from '@/hooks/useIsSnapInstalled/useIsSnapInstalled' import { useModal } from '@/hooks/useModal/useModal' +import { useNativeMultichainPreference } from '@/hooks/useNativeMultichainPreference' import { useWallet } from '@/hooks/useWallet/useWallet' +import { selectWalletId } from '@/state/slices/common-selectors' +import { useAppSelector } from '@/state/store' type MetaMaskMenuProps = { onClose?: () => void } export const MetaMaskMenu: React.FC = ({ onClose }) => { + const isMmNativeMultichain = useFeatureFlag('MmNativeMultichain') const { isSnapInstalled, isCorrectVersion } = useIsSnapInstalled() const translate = useTranslate() const snapModal = useModal('snaps') const [isMetaMask, setIsMetaMask] = useState(null) + const walletId = useAppSelector(selectWalletId) + const { migrateAccounts } = useAccountMigration() const { - state: { wallet }, + state: { wallet, deviceId }, } = useWallet() + const { isNativeMode, setPreference } = useNativeMultichainPreference(deviceId) + useEffect(() => { if (!wallet) return const isMetaMaskDesktop = checkIsMetaMaskDesktop(wallet) @@ -36,6 +46,15 @@ export const MetaMaskMenu: React.FC = ({ onClose }) => { } }, [isCorrectVersion, isSnapInstalled, snapModal]) + const handleSwitchMode = useCallback(() => { + const newMode = isNativeMode ? 'snap' : 'native' + setPreference(newMode) + if (walletId) { + migrateAccounts(walletId) + } + onClose?.() + }, [isNativeMode, setPreference, walletId, migrateAccounts, onClose]) + const renderSnapStatus = useMemo(() => { if (isSnapInstalled) { return isCorrectVersion ? ( @@ -52,15 +71,34 @@ export const MetaMaskMenu: React.FC = ({ onClose }) => { return isMetaMask ? ( <> - {isSnapInstalled && isCorrectVersion && } - - {translate('walletProvider.metaMaskSnap.multiChainSnap')} - {renderSnapStatus} - + {isMmNativeMultichain ? ( + <> + + + {isNativeMode + ? translate('walletProvider.nativeMultichain.nativeActive') + : translate('walletProvider.nativeMultichain.snapActive')} + {translate('walletProvider.metaMaskSnap.active')} + + + {isNativeMode + ? translate('walletProvider.nativeMultichain.switchToSnap') + : translate('walletProvider.nativeMultichain.switchToNative')} + + + ) : ( + <> + {isSnapInstalled && isCorrectVersion && } + + {translate('walletProvider.metaMaskSnap.multiChainSnap')} + {renderSnapStatus} + + + )} ) : null } diff --git a/src/context/WalletProvider/MetaMask/components/NativeMultichainContent.tsx b/src/context/WalletProvider/MetaMask/components/NativeMultichainContent.tsx new file mode 100644 index 00000000000..539ea82780f --- /dev/null +++ b/src/context/WalletProvider/MetaMask/components/NativeMultichainContent.tsx @@ -0,0 +1,86 @@ +import { WarningIcon } from '@chakra-ui/icons' +import { Button, Center, Flex, Heading, HStack, Tag, Text, VStack } from '@chakra-ui/react' +import type { Asset } from '@shapeshiftoss/types' +import { useTranslate } from 'react-polyglot' + +import { AssetIcon } from '@/components/AssetIcon' +import { MetaMaskIcon } from '@/components/Icons/MetaMaskIcon' + +type NativeMultichainContentProps = { + hasSnap: boolean | null + chainAssets: Asset[] + onUseNative: () => void + onKeepSnap: () => void + isKeepSnapLoading: boolean +} + +export const NativeMultichainContent: React.FC = ({ + hasSnap, + chainAssets, + onUseNative, + onKeepSnap, + isKeepSnapLoading, +}) => { + const translate = useTranslate() + + const chainNames = chainAssets.map(a => a.name).join(' and ') + + return ( + <> + +
+ +
+ + {translate('walletProvider.nativeMultichain.title', { chains: chainNames })} + + + {translate('walletProvider.nativeMultichain.subtitle', { chains: chainNames })} + + + {chainAssets.map(asset => ( + + ))} + + + + {hasSnap && ( + + )} + +
+ {hasSnap && ( + + + + {translate('walletProvider.nativeMultichain.snapChainWarning')} + + + )} + + ) +} diff --git a/src/context/WalletProvider/MetaMask/components/NativeMultichainStep.tsx b/src/context/WalletProvider/MetaMask/components/NativeMultichainStep.tsx new file mode 100644 index 00000000000..ed208a53d01 --- /dev/null +++ b/src/context/WalletProvider/MetaMask/components/NativeMultichainStep.tsx @@ -0,0 +1,30 @@ +import { useCallback } from 'react' + +import { NativeMultichainContent } from './NativeMultichainContent' + +import { WalletActions } from '@/context/WalletProvider/actions' +import { useNativeMultichainChoice } from '@/context/WalletProvider/MetaMask/hooks/useNativeMultichainChoice' +import { useWallet } from '@/hooks/useWallet/useWallet' + +export const NativeMultichainStep: React.FC = () => { + const { dispatch } = useWallet() + + const closeModal = useCallback(() => { + dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) + }, [dispatch]) + + const { hasSnap, chainAssets, handleUseNative, handleKeepSnap, isKeepSnapLoading } = + useNativeMultichainChoice({ + onDismiss: closeModal, + }) + + return ( + + ) +} diff --git a/src/context/WalletProvider/MetaMask/hooks/useNativeMultichainChoice.ts b/src/context/WalletProvider/MetaMask/hooks/useNativeMultichainChoice.ts new file mode 100644 index 00000000000..57103d3024a --- /dev/null +++ b/src/context/WalletProvider/MetaMask/hooks/useNativeMultichainChoice.ts @@ -0,0 +1,109 @@ +import type { ChainId } from '@shapeshiftoss/caip' +import { btcChainId, solanaChainId } from '@shapeshiftoss/caip' +import type { Asset } from '@shapeshiftoss/types' +import { getWallets } from '@wallet-standard/app' +import { useCallback, useEffect, useMemo, useState } from 'react' + +import { useAccountMigration } from '@/context/AppProvider/hooks/useAccountMigration' +import { getChainAdapterManager } from '@/context/PluginProvider/chainAdapterSingleton' +import { WalletActions } from '@/context/WalletProvider/actions' +import { KeyManager } from '@/context/WalletProvider/KeyManager' +import { MetaMaskConfig } from '@/context/WalletProvider/MetaMask/config' +import { checkIsSnapInstalled } from '@/hooks/useIsSnapInstalled/useIsSnapInstalled' +import { useNativeMultichainPreference } from '@/hooks/useNativeMultichainPreference' +import { useWallet } from '@/hooks/useWallet/useWallet' +import { isSome } from '@/lib/utils' +import { selectWalletId } from '@/state/slices/common-selectors' +import { selectAssetById } from '@/state/slices/selectors' +import { store, useAppSelector } from '@/state/store' +import { enableShapeShiftSnap } from '@/utils/snaps' + +// Detect which non-EVM chains MetaMask actually exposes via Wallet Standard. +// This runs at module scope since Wallet Standard registration happens early via content script. +const getAvailableChainIds = (): ChainId[] => { + const wallets = getWallets().get() + const mmWallets = wallets.filter(w => w.name === 'MetaMask') + const chains: ChainId[] = [] + for (const w of mmWallets) { + if (w.chains.some(c => c.startsWith('bitcoin:'))) chains.push(btcChainId) + if (w.chains.some(c => c.startsWith('solana:'))) chains.push(solanaChainId) + } + return chains +} + +type UseNativeMultichainChoiceArgs = { + onDismiss: () => void +} + +export const useNativeMultichainChoice = ({ onDismiss }: UseNativeMultichainChoiceArgs) => { + const { + dispatch, + getAdapter, + state: { deviceId }, + } = useWallet() + const walletId = useAppSelector(selectWalletId) + const { setPreference } = useNativeMultichainPreference(deviceId) + const { migrateAccounts } = useAccountMigration() + const [isKeepSnapLoading, setIsKeepSnapLoading] = useState(false) + + // Check snap status directly - useIsSnapInstalled returns null when flag is ON, + // but we need the real value here to show the "Keep using snap" option. + const [hasSnap, setHasSnap] = useState(null) + useEffect(() => { + checkIsSnapInstalled() + .then(setHasSnap) + .catch(() => setHasSnap(false)) + }, []) + + const chainAssets = useMemo(() => { + return getAvailableChainIds() + .map(chainId => { + const assetId = getChainAdapterManager().get(chainId)?.getFeeAssetId() + if (!assetId) return undefined + return selectAssetById(store.getState(), assetId) + }) + .filter(isSome) + }, []) + + const handleUseNative = useCallback(async () => { + setPreference('native') + if (walletId) { + migrateAccounts(walletId) + } + onDismiss() + // Silently re-pair to swap the wallet instance to the native multichain class. + // getAdapter will now read the 'native' preference and create the native multichain wallet. + try { + const adapter = await getAdapter(KeyManager.MetaMask) + if (!adapter) return + const wallet = await adapter.pairDevice() + if (!wallet) return + await wallet.initialize() + const newDeviceId = await wallet.getDeviceID() + const { name, icon } = MetaMaskConfig + dispatch({ + type: WalletActions.SET_WALLET, + payload: { wallet, name, icon, deviceId: newDeviceId, connectedType: KeyManager.MetaMask }, + }) + dispatch({ type: WalletActions.SET_IS_CONNECTED, payload: true }) + } catch (e) { + console.error('Failed to re-pair with native multichain class', e) + } + }, [setPreference, walletId, migrateAccounts, onDismiss, getAdapter, dispatch]) + + const handleKeepSnap = useCallback(async () => { + setIsKeepSnapLoading(true) + try { + // Install/connect the snap if not already connected to this origin + await enableShapeShiftSnap() + setPreference('snap') + onDismiss() + } catch (e) { + console.error('Failed to enable ShapeShift snap', e) + } finally { + setIsKeepSnapLoading(false) + } + }, [setPreference, onDismiss]) + + return { hasSnap, chainAssets, handleUseNative, handleKeepSnap, isKeepSnapLoading } +} diff --git a/src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts b/src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts index e9b40e69133..40365c19486 100644 --- a/src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts +++ b/src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts @@ -258,3 +258,40 @@ export const getAppleAttributionData = (): Promise => { return postMessage({ cmd: 'console', ...params }) } + +/** + * Open the native QR scanner in the mobile app. + * Returns a Promise that resolves with the scanned data, or rejects if cancelled/timeout. + */ +export const openNativeQRScanner = (): Promise => { + return new Promise((resolve, reject) => { + let timeoutId: ReturnType + + const eventListener = (event: MessageEvent) => { + if (event.data?.type === 'qrScanResult') { + clearTimeout(timeoutId) + window.removeEventListener('message', eventListener) + resolve(event.data.data) + } else if (event.data?.type === 'qrScanCancelled') { + clearTimeout(timeoutId) + window.removeEventListener('message', eventListener) + reject(new Error('QR scan cancelled')) + } + } + + // 60 second timeout for scanning + timeoutId = setTimeout(() => { + window.removeEventListener('message', eventListener) + reject(new Error('QR scan timed out')) + }, 60000) + + window.addEventListener('message', eventListener) + + window.ReactNativeWebView?.postMessage( + JSON.stringify({ + cmd: 'openNativeQRScanner', + id: `${Date.now()}-openNativeQRScanner`, + }), + ) + }) +} diff --git a/src/context/WalletProvider/NewWalletViews/routes/MipdRoutes.tsx b/src/context/WalletProvider/NewWalletViews/routes/MipdRoutes.tsx index b5a244fe616..224eb8ca5a4 100644 --- a/src/context/WalletProvider/NewWalletViews/routes/MipdRoutes.tsx +++ b/src/context/WalletProvider/NewWalletViews/routes/MipdRoutes.tsx @@ -1,6 +1,7 @@ import { useMemo } from 'react' import { Route, Routes } from 'react-router-dom' +import { NativeMultichainStep } from '../../MetaMask/components/NativeMultichainStep' import { SnapInstall } from '../../MetaMask/components/SnapInstall' import { SnapUpdate } from '../../MetaMask/components/SnapUpdate' import { RDNS_TO_FIRST_CLASS_KEYMANAGER } from '../constants' @@ -70,6 +71,7 @@ export const MipdRoutes = ({ const snapInstallElement = useMemo(() => , []) const snapUpdateElement = useMemo(() => , []) + const nativeMultichainElement = useMemo(() => , []) if (!modalType) return null @@ -80,6 +82,7 @@ export const MipdRoutes = ({ + ) } diff --git a/src/context/WalletProvider/NewWalletViews/wallets/mipd/MipdBody.tsx b/src/context/WalletProvider/NewWalletViews/wallets/mipd/MipdBody.tsx index e70ec7c7ec2..5711d994f9d 100644 --- a/src/context/WalletProvider/NewWalletViews/wallets/mipd/MipdBody.tsx +++ b/src/context/WalletProvider/NewWalletViews/wallets/mipd/MipdBody.tsx @@ -14,6 +14,7 @@ import { WalletActions } from '@/context/WalletProvider/actions' import { KeyManager } from '@/context/WalletProvider/KeyManager' import { useLocalWallet } from '@/context/WalletProvider/local-wallet' import { MetaMaskConfig } from '@/context/WalletProvider/MetaMask/config' +import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag' import { checkIsMetaMaskDesktop, checkIsMetaMaskMobileWebView, @@ -42,6 +43,7 @@ export const MipdBody = ({ rdns, isLoading, error, setIsLoading, setError }: Mip [mipdProviders, rdns], ) const showSnapModal = useSelector(preferences.selectors.selectShowSnapsModal) + const isMmNativeMultichain = useFeatureFlag('MmNativeMultichain') const { dispatch, getAdapter } = useWallet() const localWallet = useLocalWallet() @@ -100,6 +102,14 @@ export const MipdBody = ({ rdns, isLoading, error, setIsLoading, setError }: Mip if (!isMetaMaskDesktop || isMetaMaskMobileWebView) return dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) + // With native multichain, show the native multichain choice step instead of snap install + // but only if the user hasn't already made a choice (stored preference) + if (isMmNativeMultichain) { + const storedPref = localStorage.getItem(`nativeMultichainPreference_${deviceId}`) + if (!storedPref) return navigate('/metamask/native-multichain') + return dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: false }) + } + const isSnapInstalled = await checkIsSnapInstalled() const snapVersion = await getSnapVersion() const isCorrectVersion = snapVersion === getConfig().VITE_SNAP_VERSION @@ -131,6 +141,7 @@ export const MipdBody = ({ rdns, isLoading, error, setIsLoading, setError }: Mip getAdapter, navigate, isMetaMaskMobileWebView, + isMmNativeMultichain, localWallet, maybeMipdProvider?.info.icon, maybeMipdProvider?.info.name, diff --git a/src/context/WalletProvider/WalletProvider.tsx b/src/context/WalletProvider/WalletProvider.tsx index 2235841d614..fe8b196bcd6 100644 --- a/src/context/WalletProvider/WalletProvider.tsx +++ b/src/context/WalletProvider/WalletProvider.tsx @@ -54,6 +54,7 @@ import { } from '@/state/slices/localWalletSlice/selectors' import { portfolio as portfolioSlice } from '@/state/slices/portfolioSlice/portfolioSlice' import { preferences } from '@/state/slices/preferencesSlice/preferencesSlice' +import { selectFeatureFlag } from '@/state/slices/preferencesSlice/selectors' import { store } from '@/state/store' export type WalletInfo = { @@ -418,10 +419,29 @@ export const WalletProvider = ({ children }: { children: React.ReactNode }): JSX return METAMASK_RDNS return getKeyManagerOptions(keyManager, isDarkMode) })() + // For MetaMask, determine if native multichain should be used based on feature flag + user preference + const mmNativeMultichainOptions = (() => { + if (keyManager !== KeyManager.MetaMask) return undefined + const isFlagOn = selectFeatureFlag(store.getState(), 'MmNativeMultichain') + if (!isFlagOn) return undefined + // Read preference from localStorage - matches useNativeMultichainPreference logic + // Use localWalletDeviceId as fallback since state.deviceId is null during bootstrap + const preferenceDeviceId = state.deviceId ?? localWalletDeviceId + const storedPref = preferenceDeviceId + ? localStorage.getItem(`nativeMultichainPreference_${preferenceDeviceId}`) + : null + const isNativeMode = storedPref === 'native' + return { useNativeMultichain: isNativeMode } + })() + // @ts-ignore tsc is drunk as well, not narrowing to the specific adapter and its KeyManager options here // eslint is drunk, this isn't a hook // eslint-disable-next-line react-hooks/rules-of-hooks - adapterInstance = Adapter.useKeyring(state.keyring, keyManagerOptions) + adapterInstance = Adapter.useKeyring( + state.keyring, + keyManagerOptions, + mmNativeMultichainOptions, + ) if (adapterInstance) { currentStateAdapters[keyManager] = adapterInstance @@ -438,7 +458,15 @@ export const WalletProvider = ({ children }: { children: React.ReactNode }): JSX return adapterInstance }, - [isDarkMode, mipdProviders, state.adapters, state.keyring, state.modalType], + [ + isDarkMode, + localWalletDeviceId, + mipdProviders, + state.adapters, + state.deviceId, + state.keyring, + state.modalType, + ], ) const disconnect = useCallback(() => { diff --git a/src/context/WalletProvider/config.ts b/src/context/WalletProvider/config.ts index 054ed867c18..dff3769d9ed 100644 --- a/src/context/WalletProvider/config.ts +++ b/src/context/WalletProvider/config.ts @@ -143,6 +143,12 @@ const SnapUpdate = lazy(() => import('./MetaMask/components/SnapUpdate').then(({ SnapUpdate }) => ({ default: SnapUpdate })), ) +const NativeMultichainStep = lazy(() => + import('./MetaMask/components/NativeMultichainStep').then(({ NativeMultichainStep }) => ({ + default: NativeMultichainStep, + })), +) + const ChangeLabel = lazy(() => import('@/components/Layout/Header/NavBar/KeepKey/ChangeLabel').then(({ ChangeLabel }) => ({ default: ChangeLabel, @@ -459,6 +465,7 @@ export const SUPPORTED_WALLETS: SupportedWalletInfoByKeyManager = { { path: '/metamask/connect', component: MetaMaskConnect }, { path: '/metamask/snap/install', component: SnapInstall }, { path: '/metamask/snap/update', component: SnapUpdate }, + { path: '/metamask/native-multichain', component: NativeMultichainStep }, { path: '/metamask/failure', component: MetaMaskFailure }, ], connectedMenuComponent: MetaMaskMenu, diff --git a/src/hooks/useAffiliateTracking/index.ts b/src/hooks/useAffiliateTracking/index.ts new file mode 100644 index 00000000000..87d16f99386 --- /dev/null +++ b/src/hooks/useAffiliateTracking/index.ts @@ -0,0 +1 @@ +export { useAffiliateTracking, AFFILIATE_STORAGE_KEY } from './useAffiliateTracking' diff --git a/src/hooks/useAffiliateTracking/useAffiliateTracking.ts b/src/hooks/useAffiliateTracking/useAffiliateTracking.ts new file mode 100644 index 00000000000..1e34b636028 --- /dev/null +++ b/src/hooks/useAffiliateTracking/useAffiliateTracking.ts @@ -0,0 +1,89 @@ +import { useEffect, useState } from 'react' +import { isAddress } from 'viem' + +const AFFILIATE_STORAGE_KEY = 'shapeshift_affiliate_address' +const AFFILIATE_TIMESTAMP_KEY = 'shapeshift_affiliate_timestamp' +const AFFILIATE_TTL_MS = 30 * 24 * 60 * 60 * 1000 // 30 days + +const isAffiliateExpired = (timestamp: string | null): boolean => { + if (!timestamp) return true + const storedTime = Number(timestamp) + if (Number.isNaN(storedTime)) return true + return Date.now() - storedTime > AFFILIATE_TTL_MS +} + +const clearAffiliateStorage = (): void => { + try { + window.localStorage.removeItem(AFFILIATE_STORAGE_KEY) + window.localStorage.removeItem(AFFILIATE_TIMESTAMP_KEY) + } catch (error) { + console.warn('Error clearing affiliate data from localStorage:', error) + } +} + +export const readStoredAffiliate = (): string | null => { + if (typeof window === 'undefined') return null + + try { + const address = window.localStorage.getItem(AFFILIATE_STORAGE_KEY) + const timestamp = window.localStorage.getItem(AFFILIATE_TIMESTAMP_KEY) + + if (!address) return null + + if (!isAddress(address)) { + clearAffiliateStorage() + return null + } + + if (isAffiliateExpired(timestamp)) { + clearAffiliateStorage() + return null + } + + return address + } catch (error) { + console.warn('Error reading affiliate address from localStorage:', error) + return null + } +} + +export const useAffiliateTracking = (): string | null => { + const [storedAffiliateAddress, setStoredAffiliateAddress] = useState( + readStoredAffiliate, + ) + + useEffect(() => { + if (typeof window === 'undefined') return + + const hash = window.location.hash + const hashQueryIdx = hash.indexOf('?') + const searchStr = hashQueryIdx !== -1 ? hash.substring(hashQueryIdx) : window.location.search + const params = new URLSearchParams(searchStr) + const affiliateParam = params.get('affiliate') + + if (!affiliateParam) return + + const isValidEvmAddress = isAddress(affiliateParam) + if (!isValidEvmAddress) return + + // If we already have a non-expired affiliate stored, only override if it's different AND expired + if (storedAffiliateAddress) { + if (affiliateParam === storedAffiliateAddress) return + + const timestamp = window.localStorage.getItem(AFFILIATE_TIMESTAMP_KEY) + if (!isAffiliateExpired(timestamp)) return + } + + try { + window.localStorage.setItem(AFFILIATE_STORAGE_KEY, affiliateParam) + window.localStorage.setItem(AFFILIATE_TIMESTAMP_KEY, String(Date.now())) + setStoredAffiliateAddress(affiliateParam) + } catch (error) { + console.warn('Error storing affiliate address to localStorage:', error) + } + }, [storedAffiliateAddress]) + + return storedAffiliateAddress +} + +export { AFFILIATE_STORAGE_KEY } diff --git a/src/hooks/useIsNativeMultichainAvailable/index.ts b/src/hooks/useIsNativeMultichainAvailable/index.ts new file mode 100644 index 00000000000..c7f23c2860e --- /dev/null +++ b/src/hooks/useIsNativeMultichainAvailable/index.ts @@ -0,0 +1 @@ +export { useIsNativeMultichainAvailable } from './useIsNativeMultichainAvailable' diff --git a/src/hooks/useIsNativeMultichainAvailable/useIsNativeMultichainAvailable.ts b/src/hooks/useIsNativeMultichainAvailable/useIsNativeMultichainAvailable.ts new file mode 100644 index 00000000000..0cf5e310c41 --- /dev/null +++ b/src/hooks/useIsNativeMultichainAvailable/useIsNativeMultichainAvailable.ts @@ -0,0 +1,44 @@ +import type { Wallet } from '@wallet-standard/base' +import { useEffect, useMemo, useState } from 'react' + +import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag' +import { getWalletStandardStore } from '@/lib/walletStandard' + +const MM_WALLET_NAME = 'MetaMask' + +const hasMmWalletForChain = (wallets: readonly Wallet[], chainPrefix: string): boolean => + wallets.some( + w => w.name === MM_WALLET_NAME && w.chains.some(c => c.startsWith(`${chainPrefix}:`)), + ) + +const disabledResult = { isBtcAvailable: false, isSolAvailable: false, isAnyAvailable: false } + +export const useIsNativeMultichainAvailable = () => { + const isMmNativeMultichainEnabled = useFeatureFlag('MmNativeMultichain') + + const [state, setState] = useState({ isBtcAvailable: false, isSolAvailable: false }) + + useEffect(() => { + if (!isMmNativeMultichainEnabled) return + + const walletsApi = getWalletStandardStore() + + const check = () => { + const wallets = walletsApi.get() + setState({ + isBtcAvailable: hasMmWalletForChain(wallets, 'bitcoin'), + isSolAvailable: hasMmWalletForChain(wallets, 'solana'), + }) + } + + check() + + const unsubscribe = walletsApi.on('register', check) + return unsubscribe + }, [isMmNativeMultichainEnabled]) + + return useMemo(() => { + if (!isMmNativeMultichainEnabled) return disabledResult + return { ...state, isAnyAvailable: state.isBtcAvailable || state.isSolAvailable } + }, [isMmNativeMultichainEnabled, state]) +} diff --git a/src/hooks/useIsSnapInstalled/canAddMetaMaskAccount.test.ts b/src/hooks/useIsSnapInstalled/canAddMetaMaskAccount.test.ts new file mode 100644 index 00000000000..eeb3b8bfd64 --- /dev/null +++ b/src/hooks/useIsSnapInstalled/canAddMetaMaskAccount.test.ts @@ -0,0 +1,181 @@ +// @vitest-environment jsdom +import { btcChainId, cosmosChainId, ethChainId, solanaChainId } from '@shapeshiftoss/caip' +import type { HDWallet } from '@shapeshiftoss/hdwallet-core' +import { describe, expect, it, vi } from 'vitest' + +import { canAddMetaMaskAccount } from './useIsSnapInstalled' + +// Mock isMetaMask - returns true by default (canAddMetaMaskAccount throws otherwise) +const mockIsMetaMask = vi.fn().mockReturnValue(true) +vi.mock('@shapeshiftoss/hdwallet-core/wallet', () => ({ + isMetaMask: (...args: unknown[]) => mockIsMetaMask(...args), +})) + +// Default: not native multichain +const mockIsMetaMaskNativeMultichain = vi.fn().mockReturnValue(false) +vi.mock('@shapeshiftoss/hdwallet-metamask-multichain', () => ({ + isMetaMaskNativeMultichain: (...args: unknown[]) => mockIsMetaMaskNativeMultichain(...args), +})) + +// Mock isEvmChainId to match real behavior for known chain IDs +vi.mock('@shapeshiftoss/chain-adapters', () => ({ + isEvmChainId: (chainId: string) => chainId === ethChainId, +})) + +const makeWallet = (overrides: Record = {}): HDWallet => + ({ + _isMetaMask: true, + ...overrides, + }) as unknown as HDWallet + +describe('canAddMetaMaskAccount', () => { + describe('account 0', () => { + it('should always return true regardless of chain or snap', () => { + const wallet = makeWallet() + expect( + canAddMetaMaskAccount({ + accountNumber: 0, + chainId: ethChainId, + wallet, + isSnapInstalled: false, + }), + ).toBe(true) + expect( + canAddMetaMaskAccount({ + accountNumber: 0, + chainId: btcChainId, + wallet, + isSnapInstalled: false, + }), + ).toBe(true) + }) + }) + + describe('native multichain wallet', () => { + it('should return false for EVM chain with account > 0', () => { + mockIsMetaMaskNativeMultichain.mockReturnValue(true) + const wallet = makeWallet({ _isMetaMaskNativeMultichain: true }) + + expect( + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: ethChainId, + wallet, + isSnapInstalled: false, + }), + ).toBe(false) + }) + + it('should return true for BTC chain with account > 0', () => { + mockIsMetaMaskNativeMultichain.mockReturnValue(true) + const wallet = makeWallet({ _isMetaMaskNativeMultichain: true }) + + expect( + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: btcChainId, + wallet, + isSnapInstalled: false, + }), + ).toBe(true) + }) + + it('should return true for Solana chain with account > 0', () => { + mockIsMetaMaskNativeMultichain.mockReturnValue(true) + const wallet = makeWallet({ _isMetaMaskNativeMultichain: true }) + + expect( + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: solanaChainId, + wallet, + isSnapInstalled: false, + }), + ).toBe(true) + }) + }) + + describe('non-native MM without snap', () => { + it('should return false for account > 0 regardless of chain', () => { + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + const wallet = makeWallet() + + expect( + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: ethChainId, + wallet, + isSnapInstalled: false, + }), + ).toBe(false) + expect( + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: btcChainId, + wallet, + isSnapInstalled: false, + }), + ).toBe(false) + }) + }) + + describe('non-native MM with snap installed', () => { + it('should return false for EVM chain with account > 0', () => { + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + const wallet = makeWallet() + + expect( + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: ethChainId, + wallet, + isSnapInstalled: true, + }), + ).toBe(false) + }) + + it('should return false for Cosmos chain with account > 0', () => { + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + const wallet = makeWallet() + + expect( + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: cosmosChainId, + wallet, + isSnapInstalled: true, + }), + ).toBe(false) + }) + + it('should return true for UTXO chain (BTC) with account > 0', () => { + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + const wallet = makeWallet() + + expect( + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: btcChainId, + wallet, + isSnapInstalled: true, + }), + ).toBe(true) + }) + }) + + describe('non-MetaMask wallet', () => { + it('should throw if wallet is not MetaMask', () => { + mockIsMetaMask.mockReturnValueOnce(false) + const wallet = { _isMetaMask: false } as unknown as HDWallet + + expect(() => + canAddMetaMaskAccount({ + accountNumber: 1, + chainId: ethChainId, + wallet, + isSnapInstalled: false, + }), + ).toThrow('canAddMetaMaskAccount should only be called in the context of a MetaMask adapter') + }) + }) +}) diff --git a/src/hooks/useIsSnapInstalled/useIsSnapInstalled.tsx b/src/hooks/useIsSnapInstalled/useIsSnapInstalled.tsx index aeb1861b8b7..de8323cbc61 100644 --- a/src/hooks/useIsSnapInstalled/useIsSnapInstalled.tsx +++ b/src/hooks/useIsSnapInstalled/useIsSnapInstalled.tsx @@ -3,11 +3,13 @@ import { CHAIN_NAMESPACE, fromChainId } from '@shapeshiftoss/caip' import { isEvmChainId } from '@shapeshiftoss/chain-adapters' import type { HDWallet } from '@shapeshiftoss/hdwallet-core' import { isMetaMask } from '@shapeshiftoss/hdwallet-core/wallet' +import { isMetaMaskNativeMultichain } from '@shapeshiftoss/hdwallet-metamask-multichain' import { useQuery } from '@tanstack/react-query' import pDebounce from 'p-debounce' import { useCallback } from 'react' import { getConfig } from '@/config' +import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag' import { useWallet } from '@/hooks/useWallet/useWallet' import { METAMASK_RDNS } from '@/lib/mipd' import { selectWalletRdns } from '@/state/slices/localWalletSlice/selectors' @@ -43,6 +45,8 @@ export const useIsSnapInstalled = (): { isSnapInstalled: boolean | null isCorrectVersion: boolean | null } => { + const isMmNativeMultichain = useFeatureFlag('MmNativeMultichain') + const { state: { wallet, isConnected }, } = useWallet() @@ -50,6 +54,12 @@ export const useIsSnapInstalled = (): { const connectedRdns = useAppSelector(selectWalletRdns) const checkSnapInstallation = useCallback(async () => { + if (isMmNativeMultichain) + return { + isCorrectVersion: null, + isSnapInstalled: null, + } + if (connectedRdns !== METAMASK_RDNS || !isConnected) return { isCorrectVersion: null, @@ -70,12 +80,12 @@ export const useIsSnapInstalled = (): { isCorrectVersion: version === snapVersion, isSnapInstalled: _isSnapInstalled, } - }, [connectedRdns, isConnected, wallet]) + }, [isMmNativeMultichain, connectedRdns, isConnected, wallet]) const { data: snapInstallation = { isCorrectVersion: null, isSnapInstalled: null } } = useQuery({ - queryKey: ['snapInstallation', connectedRdns, isConnected], + queryKey: ['snapInstallation', connectedRdns, isConnected, isMmNativeMultichain], queryFn: checkSnapInstallation, - refetchInterval: POLL_INTERVAL, + refetchInterval: isMmNativeMultichain ? false : POLL_INTERVAL, staleTime: 0, gcTime: 0, }) @@ -104,6 +114,15 @@ export const canAddMetaMaskAccount = ({ // Can always add 0th account regardless of chain/snap installation if (accountNumber === 0) return true + // Native multichain: multi-account for BTC/SOL via Wallet Standard, not for EVM + if (isMetaMaskNativeMultichain(wallet)) { + // EVM multi-account still not supported in native mode - MM returns the same address for all EVM account indices + if (isEvmChainId(chainId)) return false + // For BTC/SOL, we support multiple accounts via Wallet Standard + // The actual account count depends on what MM exposes + return true + } + // MM without snaps never support multi-account, regardless of chain if (!isSnapInstalled) return false diff --git a/src/hooks/useLocaleFormatter/useLocaleFormatter.test.tsx b/src/hooks/useLocaleFormatter/useLocaleFormatter.test.tsx index 20c58e96227..8b6ff579c25 100644 --- a/src/hooks/useLocaleFormatter/useLocaleFormatter.test.tsx +++ b/src/hooks/useLocaleFormatter/useLocaleFormatter.test.tsx @@ -198,12 +198,14 @@ describe('useLocaleFormatter', () => { expect(result.current.number.toFiat(1234.456)).toBe(expected[1]) expect(result.current.number.toFiat(123456.456)).toBe(expected[2]) expect(result.current.number.toFiat(123456.656)).toBe(expected[3]) - // Case-insensitive for compact notation - ICU data differs across platforms (M vs m for millions) - expect(result.current.number.toFiat(123456789.456).toLowerCase()).toBe( - expected[4].toLowerCase(), + // Normalize only the compact suffix - ICU data differs across platforms (M vs m for millions, B vs bn for billions) + const normalizeCompact = (value: string) => + value.replace(/([0-9])[Mm]\b/u, '$1m').replace(/([0-9])[Bb]n?\b/u, '$1b') + expect(normalizeCompact(result.current.number.toFiat(123456789.456))).toBe( + normalizeCompact(expected[4]), ) - expect(result.current.number.toFiat(123456789876.456).toLowerCase()).toBe( - expected[5].toLowerCase(), + expect(normalizeCompact(result.current.number.toFiat(123456789876.456))).toBe( + normalizeCompact(expected[5]), ) expect(result.current.number.toFiat(123456789.456, { notation: 'standard' })).toBe(expected[6]) // using ...1239 here to prove truncation instead of rounding diff --git a/src/hooks/useMobileFeaturesCompatibility.ts b/src/hooks/useMobileFeaturesCompatibility.ts index a05fd104f28..53e5aab3377 100644 --- a/src/hooks/useMobileFeaturesCompatibility.ts +++ b/src/hooks/useMobileFeaturesCompatibility.ts @@ -7,10 +7,12 @@ import { isMobile } from '@/lib/globals' export enum MobileFeature { RatingModal = 'rating-modal', + NativeQrScanner = 'native-qr-scanner', } export const MOBILE_FEATURE_MINIMUM_VERSIONS: Record = { [MobileFeature.RatingModal]: '3.3.1', + [MobileFeature.NativeQrScanner]: '3.7.2', } type MobileFeatureInfo = { diff --git a/src/hooks/useNativeMultichainPreference/index.ts b/src/hooks/useNativeMultichainPreference/index.ts new file mode 100644 index 00000000000..6a992e6b996 --- /dev/null +++ b/src/hooks/useNativeMultichainPreference/index.ts @@ -0,0 +1 @@ +export { useNativeMultichainPreference } from './useNativeMultichainPreference' diff --git a/src/hooks/useNativeMultichainPreference/useNativeMultichainPreference.ts b/src/hooks/useNativeMultichainPreference/useNativeMultichainPreference.ts new file mode 100644 index 00000000000..83242cb7dfd --- /dev/null +++ b/src/hooks/useNativeMultichainPreference/useNativeMultichainPreference.ts @@ -0,0 +1,75 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' + +import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag' +import { useIsSnapInstalled } from '@/hooks/useIsSnapInstalled/useIsSnapInstalled' +import { METAMASK_RDNS } from '@/lib/mipd' +import { selectWalletRdns } from '@/state/slices/localWalletSlice/selectors' +import { useAppSelector } from '@/state/store' + +const STORAGE_KEY = 'nativeMultichainPreference' + +type Preference = 'native' | 'snap' | null + +const getStorageKey = (deviceId: string) => `${STORAGE_KEY}_${deviceId}` + +const getPreference = (deviceId: string | null | undefined): Preference => { + if (!deviceId) return null + const stored = localStorage.getItem(getStorageKey(deviceId)) + if (stored === 'native' || stored === 'snap') return stored + return null +} + +export const useNativeMultichainPreference = (deviceId: string | null | undefined) => { + const isMmNativeMultichain = useFeatureFlag('MmNativeMultichain') + const connectedRdns = useAppSelector(selectWalletRdns) + const { isSnapInstalled } = useIsSnapInstalled() + + const [preference, setPreferenceState] = useState(() => getPreference(deviceId)) + + // Re-read from localStorage when deviceId becomes available. + // useState initializer only runs once - if deviceId was null on mount, preference stays null. + useEffect(() => { + const stored = getPreference(deviceId) + setPreferenceState(stored) + }, [deviceId]) + + const setPreference = useCallback( + (pref: 'native' | 'snap') => { + if (!deviceId) return + localStorage.setItem(getStorageKey(deviceId), pref) + setPreferenceState(pref) + }, + [deviceId], + ) + + const shouldShowDeprecationModal = useMemo(() => { + if (!isMmNativeMultichain) return false + if (connectedRdns !== METAMASK_RDNS) return false + // Check both React state AND localStorage directly to avoid race condition: + // useEffect updates preference asynchronously, but this useMemo runs during render. + // Without the direct localStorage check, there's a window where preference is stale (null) + // and the modal opens before the effect fires. + const storedPreference = getPreference(deviceId) + if (preference !== null || storedPreference !== null) return false + return true + }, [isMmNativeMultichain, connectedRdns, preference, deviceId]) + + const isNativeMode = useMemo(() => { + if (!isMmNativeMultichain) return false + // If preference is explicitly 'native', use native mode + if (preference === 'native') return true + // If snap is not installed and flag is on, default to native + if (isSnapInstalled === false && preference !== 'snap') return true + return false + }, [isMmNativeMultichain, preference, isSnapInstalled]) + + return useMemo( + () => ({ + preference, + setPreference, + shouldShowDeprecationModal, + isNativeMode, + }), + [preference, setPreference, shouldShowDeprecationModal, isNativeMode], + ) +} diff --git a/src/hooks/useWalletSupportsChain/checkWalletHasRuntimeSupport.test.ts b/src/hooks/useWalletSupportsChain/checkWalletHasRuntimeSupport.test.ts new file mode 100644 index 00000000000..da677ad1be1 --- /dev/null +++ b/src/hooks/useWalletSupportsChain/checkWalletHasRuntimeSupport.test.ts @@ -0,0 +1,222 @@ +// @vitest-environment jsdom +import { btcChainId, ethChainId, solanaChainId } from '@shapeshiftoss/caip' +import type { HDWallet } from '@shapeshiftoss/hdwallet-core' +import { describe, expect, it, vi } from 'vitest' + +// Mock isMetaMask +const mockIsMetaMask = vi.fn() +vi.mock('@shapeshiftoss/hdwallet-core/wallet', () => ({ + isMetaMask: (...args: unknown[]) => mockIsMetaMask(...args), + // Stub out all the supports* functions - we only care about runtime support checks + supportsETH: () => true, + supportsBTC: () => true, + supportsSolana: () => true, + supportsArbitrum: () => true, + supportsAvalanche: () => true, + supportsBase: () => true, + supportsBerachain: () => true, + supportsBlast: () => true, + supportsBob: () => true, + supportsBSC: () => true, + supportsCelo: () => true, + supportsCosmos: () => true, + supportsCronos: () => true, + supportsEthereal: () => true, + supportsFlowEvm: () => true, + supportsGnosis: () => true, + supportsHemi: () => true, + supportsHyperEvm: () => true, + supportsInk: () => true, + supportsKatana: () => true, + supportsLinea: () => true, + supportsMantle: () => true, + supportsMayachain: () => true, + supportsMegaEth: () => true, + supportsMode: () => true, + supportsMonad: () => true, + supportsOptimism: () => true, + supportsPlasma: () => true, + supportsPlume: () => true, + supportsPolygon: () => true, + supportsScroll: () => true, + supportsSei: () => true, + supportsSoneium: () => true, + supportsSonic: () => true, + supportsStarknet: () => true, + supportsStory: () => true, + supportsSui: () => true, + supportsThorchain: () => true, + supportsTron: () => true, + supportsUnichain: () => true, + supportsWorldChain: () => true, + supportsZkSyncEra: () => true, + isPhantom: () => false, + isGridPlus: () => false, + isVultisig: () => false, +})) + +const mockIsMetaMaskNativeMultichain = vi.fn() +vi.mock('@shapeshiftoss/hdwallet-metamask-multichain', () => ({ + isMetaMaskNativeMultichain: (...args: unknown[]) => mockIsMetaMaskNativeMultichain(...args), +})) + +vi.mock('@shapeshiftoss/chain-adapters', () => ({ + isEvmChainId: (chainId: string) => chainId === ethChainId, +})) + +vi.mock('@/lib/mipd', () => ({ + METAMASK_RDNS: 'io.metamask', +})) + +vi.mock('@/lib/utils', () => ({ + isNativeHDWallet: () => false, + isLedgerHDWallet: () => false, + isTrezorHDWallet: () => false, +})) + +vi.mock('@/lib/utils/near', () => ({ + supportsNear: () => true, +})) + +vi.mock('@/lib/utils/ton', () => ({ + supportsTon: () => true, +})) + +// Mock the store to return all feature flags as true +vi.mock('@/state/store', () => ({ + store: { + getState: () => ({}), + }, + useAppSelector: vi.fn(), +})) + +vi.mock('@/state/slices/selectors', () => ({ + selectFeatureFlag: () => true, +})) + +const { walletSupportsChain } = await import('./useWalletSupportsChain') + +const makeWallet = (overrides: Record = {}): HDWallet => + ({ + _isMetaMask: true, + providerRdns: 'io.metamask', + ...overrides, + }) as unknown as HDWallet + +describe('checkWalletHasRuntimeSupport (via walletSupportsChain)', () => { + describe('MetaMask non-native + no snap + non-EVM chain', () => { + it('should return false - no runtime support for non-EVM without snap or native multichain', () => { + mockIsMetaMask.mockReturnValue(true) + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + + const wallet = makeWallet() + const result = walletSupportsChain({ + chainId: btcChainId, + wallet, + isSnapInstalled: false, + checkConnectedAccountIds: false, + }) + + expect(result).toBe(false) + }) + }) + + describe('MetaMask native multichain + non-EVM chain', () => { + it('should return true - native multichain bypasses snap requirement', () => { + mockIsMetaMask.mockReturnValue(true) + mockIsMetaMaskNativeMultichain.mockReturnValue(true) + + const wallet = makeWallet({ _isMetaMaskNativeMultichain: true }) + const result = walletSupportsChain({ + chainId: btcChainId, + wallet, + isSnapInstalled: false, + checkConnectedAccountIds: false, + }) + + expect(result).toBe(true) + }) + + it('should return true for Solana chain', () => { + mockIsMetaMask.mockReturnValue(true) + mockIsMetaMaskNativeMultichain.mockReturnValue(true) + + const wallet = makeWallet({ _isMetaMaskNativeMultichain: true }) + const result = walletSupportsChain({ + chainId: solanaChainId, + wallet, + isSnapInstalled: false, + checkConnectedAccountIds: false, + }) + + expect(result).toBe(true) + }) + }) + + describe('non-MetaMask wallet', () => { + it('should not be affected by MetaMask checks', () => { + mockIsMetaMask.mockReturnValue(false) + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + + const wallet = makeWallet({ _isMetaMask: false }) + // A non-MM wallet with BTC support should return true + const result = walletSupportsChain({ + chainId: btcChainId, + wallet, + isSnapInstalled: false, + checkConnectedAccountIds: false, + }) + + expect(result).toBe(true) + }) + + it('should return true for EVM chain', () => { + mockIsMetaMask.mockReturnValue(false) + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + + const wallet = makeWallet({ _isMetaMask: false }) + const result = walletSupportsChain({ + chainId: ethChainId, + wallet, + isSnapInstalled: false, + checkConnectedAccountIds: false, + }) + + expect(result).toBe(true) + }) + }) + + describe('MetaMask with snap installed + non-EVM', () => { + it('should return true when snap is installed and rdns matches', () => { + mockIsMetaMask.mockReturnValue(true) + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + + const wallet = makeWallet() + const result = walletSupportsChain({ + chainId: btcChainId, + wallet, + isSnapInstalled: true, + checkConnectedAccountIds: false, + }) + + expect(result).toBe(true) + }) + }) + + describe('MetaMask with snap installed but wrong rdns', () => { + it('should return false - snap installed but not actual MM', () => { + mockIsMetaMask.mockReturnValue(true) + mockIsMetaMaskNativeMultichain.mockReturnValue(false) + + const wallet = makeWallet({ providerRdns: 'io.rabby' }) + const result = walletSupportsChain({ + chainId: btcChainId, + wallet, + isSnapInstalled: true, + checkConnectedAccountIds: false, + }) + + expect(result).toBe(false) + }) + }) +}) diff --git a/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts b/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts index dc9472cf7e6..026f4851475 100644 --- a/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts +++ b/src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts @@ -99,6 +99,7 @@ import { supportsWorldChain, supportsZkSyncEra, } from '@shapeshiftoss/hdwallet-core/wallet' +import { isMetaMaskNativeMultichain } from '@shapeshiftoss/hdwallet-metamask-multichain' import { useMemo } from 'react' import { KeyManager } from '@/context/WalletProvider/KeyManager' @@ -129,12 +130,14 @@ const checkWalletHasRuntimeSupport = ({ if (!wallet) return false - // Non-EVM ChainIds are only supported with the MM multichain snap installed + // Non-EVM ChainIds are only supported with the MM multichain snap installed or native multichain if ( isMetaMask(wallet) && + // Native multichain wallet supports non-EVM chains directly + !isMetaMaskNativeMultichain(wallet) && // snap installation checks may take a render or two too many to kick in after switching from MM with snaps to another mipd wallet // however, we get a new wallet ref instantly, so this ensures we don't wrongly derive non-EVM accounts for another EIP1193 wallet - (!isSnapInstalled || (wallet as any).providerRdns !== METAMASK_RDNS) && + (!isSnapInstalled || (wallet as { providerRdns?: string }).providerRdns !== METAMASK_RDNS) && !isEvmChainId(chainId) ) return false diff --git a/src/index.tsx b/src/index.tsx index 3185fc025c5..566c59eadc7 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -21,6 +21,7 @@ import { AppProviders } from './AppProviders' import { PerformanceProfiler } from './components/PerformanceProfiler' import { getConfig } from './config' import { renderConsoleArt } from './lib/consoleArt' +import { isLocalDev } from './lib/isLocalDev' import { profiler } from './lib/performanceProfiler' import { reportWebVitals } from './lib/reportWebVitals' import { httpClientIntegration } from './utils/sentry/httpclient' @@ -35,12 +36,12 @@ if (enablePerformanceProfiler) { const SENTRY_ENABLED = true scan({ - enabled: window.location.hostname === 'localhost' && enableReactScan, + enabled: isLocalDev() && enableReactScan, }) // Sentry is disabled on localhost by default // To test locally, set VITE_ENABLE_SENTRY_LOCALHOST=true in your .env.local file -const isLocalhost = window.location.hostname === 'localhost' +const isLocalhost = isLocalDev() const enableSentryOnLocalhost = import.meta.env.VITE_ENABLE_SENTRY_LOCALHOST === 'true' const shouldEnableSentry = SENTRY_ENABLED && (!isLocalhost || enableSentryOnLocalhost) @@ -60,6 +61,7 @@ if (shouldEnableSentry) { ] as const const environment = (() => { + if (isLocalhost) return 'localhost' if (window.location.hostname.includes('app')) return 'production' if (VALID_ENVS.some(env => window.location.hostname.includes(env))) diff --git a/src/lib/account/utxo.ts b/src/lib/account/utxo.ts index 7a99b4509cb..693071a8ff3 100644 --- a/src/lib/account/utxo.ts +++ b/src/lib/account/utxo.ts @@ -9,6 +9,7 @@ import { isWalletConnectV2, supportsBTC, } from '@shapeshiftoss/hdwallet-core/wallet' +import { isMetaMaskNativeMultichain } from '@shapeshiftoss/hdwallet-metamask-multichain' import { isTrezor } from '@shapeshiftoss/hdwallet-trezor' import type { AccountMetadataById, UtxoChainId } from '@shapeshiftoss/types' import { UtxoAccountType } from '@shapeshiftoss/types' @@ -112,7 +113,10 @@ export const deriveUtxoAccountIdsAndMetadata: DeriveAccountIdsAndMetadata = asyn const adapter = assertGetUtxoChainAdapter(chainId) let supportedAccountTypes = adapter.getSupportedAccountTypes() - if (isMetaMask(wallet)) { + if (isMetaMaskNativeMultichain(wallet)) { + // Native multichain uses native segwit (bip84) via Bitcoin Wallet Standard + supportedAccountTypes = [UtxoAccountType.SegwitNative] + } else if (isMetaMask(wallet)) { // MetaMask snaps adapter only supports legacy for BTC and LTC supportedAccountTypes = [UtxoAccountType.P2pkh] } diff --git a/src/lib/isLocalDev.ts b/src/lib/isLocalDev.ts new file mode 100644 index 00000000000..da79cb975f0 --- /dev/null +++ b/src/lib/isLocalDev.ts @@ -0,0 +1,8 @@ +/** + * Checks if the app is running in a local development environment. + * Handles both plain localhost and Portless subdomains (*.localhost). + */ +export const isLocalDev = (): boolean => { + const hostname = window.location.hostname + return hostname === 'localhost' || hostname.endsWith('.localhost') +} diff --git a/src/lib/solanaJitoBundle.ts b/src/lib/solanaJitoBundle.ts new file mode 100644 index 00000000000..b6c9b1b0c1b --- /dev/null +++ b/src/lib/solanaJitoBundle.ts @@ -0,0 +1,153 @@ +import { solana } from '@shapeshiftoss/chain-adapters' +import type { SolanaSignTx } from '@shapeshiftoss/hdwallet-core' +import { bnOrZero } from '@shapeshiftoss/utils' +import type { TransactionInstruction } from '@solana/web3.js' +import { PublicKey } from '@solana/web3.js' + +import { getConfig } from '@/config' +import { assertGetSolanaChainAdapter } from '@/lib/utils/solana' + +const JITO_BUNDLE_POLL_INTERVAL_MS = 2_000 +const JITO_BUNDLE_MAX_POLLS = 30 +const COMPUTE_UNIT_MARGIN_MULTIPLIER = 1.6 + +/** + * Execute a Jito bundle for oversized Solana transactions. + * + * Splits instructions into 2 transactions, adds a tip to the last one, + * signs each sequentially, and submits as an atomic Jito bundle. + */ +export const execSolanaJitoBundle = async ({ + instructions, + addressLookupTableAddresses, + from, + accountNumber, + sellAssetChainId, + signTransaction, +}: { + instructions: TransactionInstruction[] + addressLookupTableAddresses: string[] + from: string + accountNumber: number + sellAssetChainId: string + signTransaction: (txToSign: SolanaSignTx) => Promise +}): Promise => { + const config = getConfig() + const adapter = assertGetSolanaChainAdapter(sellAssetChainId) + const jitoService = solana.createJitoService(config.VITE_JITO_BLOCK_ENGINE_URL) + + // Split instructions roughly in half + const midpoint = Math.ceil(instructions.length / 2) + const group1 = instructions.slice(0, midpoint) + const group2 = instructions.slice(midpoint) + + // Build tip instruction for the last tx + const tipInstruction = await solana.buildJitoTipInstruction({ + jitoService, + fromPubkey: new PublicKey(from), + }) + + const group2WithTip = [...group2, tipInstruction] + + // Build both unsigned txs + const [unsignedTx1, unsignedTx2] = await buildBundleUnsignedTxs({ + adapter, + from, + accountNumber, + instructionGroups: [group1, group2WithTip], + addressLookupTableAddresses, + }) + + // Sign both txs sequentially (hdwallet only supports single-tx signing) + const signedTx1 = await signTransaction(unsignedTx1) + const signedTx2 = await signTransaction(unsignedTx2) + + // Submit as Jito bundle + const bundleId = await jitoService.sendBundle([signedTx1, signedTx2]) + + // Poll for bundle landing + return await pollBundleLanding(jitoService, bundleId) +} + +const buildBundleUnsignedTxs = async ({ + adapter, + from, + accountNumber, + instructionGroups, + addressLookupTableAddresses, +}: { + adapter: ReturnType + from: string + accountNumber: number + instructionGroups: TransactionInstruction[][] + addressLookupTableAddresses: string[] +}): Promise => { + const unsignedTxs: SolanaSignTx[] = [] + + for (const instructions of instructionGroups) { + const { fast } = await adapter.getFeeData({ + to: '', + value: '0', + chainSpecific: { + from, + instructions, + addressLookupTableAccounts: addressLookupTableAddresses, + }, + }) + + const convertedInstructions = instructions.map(ix => adapter.convertInstruction(ix)) + + const unsignedTx = await adapter.buildSendApiTransaction({ + from, + to: '', + value: '0', + accountNumber, + chainSpecific: { + instructions: convertedInstructions, + addressLookupTableAccounts: addressLookupTableAddresses, + computeUnitLimit: bnOrZero(fast.chainSpecific.computeUnits) + .times(COMPUTE_UNIT_MARGIN_MULTIPLIER) + .toFixed(0), + computeUnitPrice: fast.chainSpecific.priorityFee, + }, + }) + + unsignedTxs.push(unsignedTx) + } + + return unsignedTxs +} + +const pollBundleLanding = async ( + jitoService: solana.JitoService, + bundleId: string, +): Promise => { + for (let i = 0; i < JITO_BUNDLE_MAX_POLLS; i++) { + await new Promise(resolve => setTimeout(resolve, JITO_BUNDLE_POLL_INTERVAL_MS)) + + const statuses = await jitoService.getInflightBundleStatuses([bundleId]) + const status = statuses.value[0] + + if (status?.status === 'Landed') { + // Bundle landed - get the tx hash from getBundleStatuses + const bundleStatuses = await jitoService.getBundleStatuses([bundleId]) + const bundleStatus = bundleStatuses.value[0] + // Return the last tx hash (the one with the swap + tip) + const txs = bundleStatus?.transactions + if (!txs?.length) { + throw new Error(`Jito bundle landed but no transaction hash returned: ${bundleId}`) + } + return txs[txs.length - 1] + } + + if (status?.status === 'Failed' || status?.status === 'Invalid') { + throw new Error(`Jito bundle ${status.status}: ${bundleId}`) + } + } + + throw new Error( + `Jito bundle timed out after ${ + (JITO_BUNDLE_MAX_POLLS * JITO_BUNDLE_POLL_INTERVAL_MS) / 1000 + }s: ${bundleId}`, + ) +} diff --git a/src/lib/tradeExecution.ts b/src/lib/tradeExecution.ts index 9d3149d0519..f946412fe67 100644 --- a/src/lib/tradeExecution.ts +++ b/src/lib/tradeExecution.ts @@ -23,6 +23,7 @@ import type { UtxoTransactionExecutionInput, } from '@shapeshiftoss/swapper' import { + getExecutableTradeStep, getHopByIndex, isExecutableTradeQuote, swappers, @@ -46,7 +47,9 @@ import { assertGetUtxoChainAdapter } from './utils/utxo' import { getConfig } from '@/config' import { queryClient } from '@/context/QueryClientProvider/queryClient' +import { readStoredAffiliate } from '@/hooks/useAffiliateTracking/useAffiliateTracking' import { fetchIsSmartContractAddressQuery } from '@/hooks/useIsSmartContractAddress/useIsSmartContractAddress' +import { getAffiliateBps } from '@/lib/fees/utils' import { poll } from '@/lib/poll/poll' import { getOrCreateUser } from '@/lib/user/api' import { selectCurrentSwap, selectWalletEnabledAccountIds } from '@/state/slices/selectors' @@ -210,10 +213,15 @@ export class TradeExecution { queryClient.fetchQuery({ queryKey: ['createSwap', swap.id], queryFn: () => { + const affiliateAddress = readStoredAffiliate() ?? undefined + const affiliateBps = getAffiliateBps(updatedSwap.sellAsset, updatedSwap.buyAsset) return axios.post(`${import.meta.env.VITE_SWAPS_SERVER_URL}/swaps`, { swapId: swap.id, sellTxHash, userId: userData?.id, + affiliateAddress, + affiliateBps, + origin: 'web', sellAsset: updatedSwap.sellAsset, buyAsset: updatedSwap.buyAsset, sellAmountCryptoBaseUnit: updatedSwap.sellAmountCryptoBaseUnit, @@ -526,6 +534,7 @@ export class TradeExecution { slippageTolerancePercentageDecimal, from, signAndBroadcastTransaction, + signTransaction, }: SolanaTransactionExecutionInput) { const buildSignBroadcast = async ( swapper: Swapper & SwapperApi, @@ -537,6 +546,33 @@ export class TradeExecution { config, }: CommonGetUnsignedTransactionArgs, ) => { + if (!isExecutableTradeQuote(tradeQuote)) { + throw new Error('Unable to execute a trade rate quote') + } + + const step = getHopByIndex(tradeQuote, stepIndex) + const metadata = step?.solanaTransactionMetadata + + // Jito bundle path for oversized Butter Solana transactions + if (metadata?.isOversized) { + if (!metadata.instructions?.length || !signTransaction) { + throw new Error( + 'Oversized Solana transaction requires instructions and signTransaction for Jito bundle execution', + ) + } + const executableStep = getExecutableTradeStep(tradeQuote, stepIndex) + const { execSolanaJitoBundle } = await import('@/lib/solanaJitoBundle') + return await execSolanaJitoBundle({ + instructions: metadata.instructions, + addressLookupTableAddresses: metadata.addressLookupTableAddresses ?? [], + from, + accountNumber: executableStep.accountNumber, + sellAssetChainId: chainId, + signTransaction, + }) + } + + // Standard single-tx path if (!swapper.getUnsignedSolanaTransaction) { throw Error('missing implementation for getUnsignedSolanaTransaction') } diff --git a/src/lib/walletStandard.ts b/src/lib/walletStandard.ts new file mode 100644 index 00000000000..e1ce2ec8044 --- /dev/null +++ b/src/lib/walletStandard.ts @@ -0,0 +1,22 @@ +import { getWallets } from '@wallet-standard/app' +import type { Wallet } from '@wallet-standard/base' + +// Singleton - getWallets() is safe to call multiple times, returns same instance +const walletsApi = getWallets() + +/** + * Find a Wallet Standard wallet by name and chain prefix. + * MetaMask registers SEPARATE wallet objects for Bitcoin and Solana. + * Example: findWalletStandardWallet('MetaMask', 'bitcoin') finds MM's BTC wallet + */ +export const findWalletStandardWallet = (name: string, chainPrefix: string): Wallet | undefined => { + return walletsApi + .get() + .find(w => w.name === name && w.chains.some(c => c.startsWith(`${chainPrefix}:`))) +} + +/** + * Get the raw Wallet Standard API for event subscriptions. + * Use walletsApi.on('register', callback) to listen for new wallets. + */ +export const getWalletStandardStore = () => walletsApi diff --git a/src/pages/Accounts/Accounts.tsx b/src/pages/Accounts/Accounts.tsx index 4edef93d692..c542061cdf6 100644 --- a/src/pages/Accounts/Accounts.tsx +++ b/src/pages/Accounts/Accounts.tsx @@ -1,6 +1,7 @@ import { EditIcon } from '@chakra-ui/icons' import { Button, Heading, Skeleton, Stack } from '@chakra-ui/react' import { isMetaMask } from '@shapeshiftoss/hdwallet-core/wallet' +import { isMetaMaskNativeMultichain } from '@shapeshiftoss/hdwallet-metamask-multichain' import { useEffect, useMemo, useState } from 'react' import { useTranslate } from 'react-polyglot' import { useSelector } from 'react-redux' @@ -36,7 +37,8 @@ const AccountHeader = ({ isLoading }: { isLoading?: boolean }) => { const isMetaMaskMultichainWallet = isMetaMask(wallet) useEffect(() => { if (!wallet) return - if (isMetaMaskMultichainWallet && !isSnapInstalled) return setIsMultiAccountWallet(false) + if (isMetaMaskMultichainWallet && !isSnapInstalled && !isMetaMaskNativeMultichain(wallet)) + return setIsMultiAccountWallet(false) setIsMultiAccountWallet(wallet.supportsBip44Accounts()) }, [isMetaMaskMultichainWallet, isSnapInstalled, wallet]) diff --git a/src/pages/ChainflipLending/Pool/Pool.tsx b/src/pages/ChainflipLending/Pool/Pool.tsx index 247626169b0..604fbd821d1 100644 --- a/src/pages/ChainflipLending/Pool/Pool.tsx +++ b/src/pages/ChainflipLending/Pool/Pool.tsx @@ -16,7 +16,7 @@ import { Tooltip, VStack, } from '@chakra-ui/react' -import type { AssetId } from '@shapeshiftoss/caip' +import type { AccountId, AssetId } from '@shapeshiftoss/caip' import { ethAssetId, fromAssetId } from '@shapeshiftoss/caip' import { BigAmount } from '@shapeshiftoss/utils' import type { Property } from 'csstype' @@ -48,6 +48,7 @@ import { useChainflipLtvThresholds } from '@/pages/ChainflipLending/hooks/useCha import { useChainflipSafeModeStatuses } from '@/pages/ChainflipLending/hooks/useChainflipSafeModeStatuses' import { useChainflipSupplyPositions } from '@/pages/ChainflipLending/hooks/useChainflipSupplyPositions' import { selectAssetById } from '@/state/slices/assetsSlice/selectors' +import { selectMarketDataByAssetIdUserCurrency } from '@/state/slices/marketDataSlice/selectors' import { selectAccountIdsByAccountNumberAndChainId } from '@/state/slices/portfolioSlice/selectors' import { selectPortfolioCryptoBalanceByFilter } from '@/state/slices/selectors' import { useAppSelector } from '@/state/store' @@ -56,24 +57,26 @@ const flexDirPool: ResponsiveValue = { base: 'column', l const actionColumnMaxWidth = { base: '100%', lg: '500px' } enum PoolTabIndex { - Supply = 0, - Deposit = 1, + Deposit = 0, + Supply = 1, Collateral = 2, ManageLoan = 3, } const ACTION_TAB_ITEMS = [ - { label: 'chainflipLending.supply.title', index: PoolTabIndex.Supply }, { label: 'chainflipLending.depositToChainflip', index: PoolTabIndex.Deposit }, + { label: 'chainflipLending.supply.title', index: PoolTabIndex.Supply }, { label: 'chainflipLending.collateral.title', index: PoolTabIndex.Collateral }, { label: 'chainflipLending.manageLoan', index: PoolTabIndex.ManageLoan }, ] type PoolHeaderProps = { assetId: AssetId + accountId: AccountId | undefined + setAccountId: (accountId: AccountId | undefined) => void } -const PoolHeader: React.FC = ({ assetId }) => { +const PoolHeader: React.FC = ({ assetId, accountId, setAccountId }) => { const asset = useAppSelector(state => selectAssetById(state, assetId)) const translate = useTranslate() const navigate = useNavigate() @@ -104,6 +107,17 @@ const PoolHeader: React.FC = ({ assetId }) => { + {accountId && ( + + + + )} ) } @@ -147,7 +161,7 @@ export const Pool = () => { const location = useLocation() const { dispatch: walletDispatch } = useWallet() const { accountId, accountNumber, setAccountId } = useChainflipLendingAccount() - const [actionTabIndex, setActionTabIndex] = useState(PoolTabIndex.Supply) + const [actionTabIndex, setActionTabIndex] = useState(PoolTabIndex.Deposit) const chainflipLendingModal = useModal('chainflipLending') const handleConnectWallet = useCallback( @@ -242,12 +256,29 @@ export const Pool = () => { }).toPrecision() }, [freeBalances, cfAsset, asset]) + const marketData = useAppSelector(state => + selectMarketDataByAssetIdUserCurrency(state, poolAssetId), + ) + + const freeBalanceFiat = useMemo(() => { + if (!marketData?.price) return undefined + return bnOrZero(freeBalanceCryptoPrecision).times(marketData.price).toFixed(2) + }, [freeBalanceCryptoPrecision, marketData?.price]) + + const walletBalanceFiat = useMemo(() => { + if (!marketData?.price) return undefined + return bnOrZero(walletBalanceCryptoPrecision).times(marketData.price).toFixed(2) + }, [walletBalanceCryptoPrecision, marketData?.price]) + const hasSupplyPosition = useMemo( () => bnOrZero(supplyPosition?.totalAmountCryptoPrecision).gt(0), [supplyPosition], ) - const headerComponent = useMemo(() => , [poolAssetId]) + const headerComponent = useMemo( + () => , + [poolAssetId, accountId, setAccountId], + ) const actionTabHeader = useMemo( () => ( @@ -534,85 +565,85 @@ export const Pool = () => { > - {accountId && ( - - - - - - )} {actionTabHeader} - + {translate('chainflipLending.pool.freeBalance')} - + + {freeBalanceFiat !== undefined && ( + + )} + + - {translate('chainflipLending.supplied')} + {translate('chainflipLending.pool.walletBalance')} - + + {walletBalanceFiat !== undefined && ( + + )} - +
- + span': { display: 'block' } }}> - + span': { display: 'block' } }}> - + span': { display: 'block' } }}> - + span': { display: 'block' } }}> { {translate('chainflipLending.pool.freeBalance')} - + + {freeBalanceFiat !== undefined && ( + + )} + + - + span': { display: 'block' } }}> { - + span': { display: 'block' } }}> import('./BorrowConfirm').then(({ BorrowConfirm }) => ({ default: BorrowConfirm })), ) -const suspenseFallback = +const suspenseFallback = ( + + + +) type BorrowProps = { assetId: AssetId diff --git a/src/pages/ChainflipLending/Pool/components/Borrow/BorrowConfirm.tsx b/src/pages/ChainflipLending/Pool/components/Borrow/BorrowConfirm.tsx index 9f9a287a7f8..974b7a5f61c 100644 --- a/src/pages/ChainflipLending/Pool/components/Borrow/BorrowConfirm.tsx +++ b/src/pages/ChainflipLending/Pool/components/Borrow/BorrowConfirm.tsx @@ -1,6 +1,7 @@ -import { CheckCircleIcon } from '@chakra-ui/icons' -import { Button, CardBody, CardFooter, Flex, VStack } from '@chakra-ui/react' +import { ArrowForwardIcon, CheckCircleIcon } from '@chakra-ui/icons' +import { Button, CardBody, CardFooter, Flex, HStack, VStack } from '@chakra-ui/react' import type { AssetId } from '@shapeshiftoss/caip' +import { flipAssetId } from '@shapeshiftoss/caip' import { useQueryClient } from '@tanstack/react-query' import { memo, useCallback, useMemo } from 'react' import { useTranslate } from 'react-polyglot' @@ -16,6 +17,7 @@ import { AssetIcon } from '@/components/AssetIcon' import { CircularProgress } from '@/components/CircularProgress/CircularProgress' import { SlideTransition } from '@/components/SlideTransition' import { RawText } from '@/components/Text' +import { useModal } from '@/hooks/useModal/useModal' import { useChainflipLendingAccount } from '@/pages/ChainflipLending/ChainflipLendingAccountContext' import { useChainflipLtvThresholds } from '@/pages/ChainflipLending/hooks/useChainflipLtvThresholds' import { reactQueries } from '@/react-queries' @@ -45,8 +47,8 @@ export const BorrowConfirm = memo(({ assetId }: BorrowConfirmProps) => { const isNativeWallet = BorrowMachineCtx.useSelector(s => s.context.isNativeWallet) const stepConfirmed = BorrowMachineCtx.useSelector(s => s.context.stepConfirmed) const isConfirming = BorrowMachineCtx.useSelector(s => s.matches('confirming')) - const { thresholds } = useChainflipLtvThresholds() + const { close: closeModal } = useModal('chainflipLending') useBorrowSign() useBorrowConfirmation() @@ -84,8 +86,8 @@ export const BorrowConfirm = memo(({ assetId }: BorrowConfirmProps) => { await queryClient.invalidateQueries(reactQueries.chainflipLending.freeBalances(scAccount)) await queryClient.invalidateQueries(reactQueries.chainflipLending.accountInfo(scAccount)) } - actorRef.send({ type: 'DONE' }) - }, [scAccount, queryClient, actorRef]) + closeModal() + }, [scAccount, queryClient, closeModal]) const handleBack = useCallback(() => { actorRef.send({ type: 'BACK' }) @@ -110,16 +112,18 @@ export const BorrowConfirm = memo(({ assetId }: BorrowConfirmProps) => { })} - - - {translate('chainflipLending.borrow.borrowed')} - - + + + + {translate('chainflipLending.borrow.borrowed')} + + + @@ -152,7 +156,11 @@ export const BorrowConfirm = memo(({ assetId }: BorrowConfirmProps) => { - + + + + + {translate('chainflipLending.borrow.errorTitle')} @@ -191,18 +199,29 @@ export const BorrowConfirm = memo(({ assetId }: BorrowConfirmProps) => { ) } + const isAwaitingNativeConfirm = isNativeWallet && !isConfirming && !stepConfirmed + if (!isConfirm) { return ( - + + + + + + {!isAwaitingNativeConfirm && } - {translate('chainflipLending.borrow.executingTitle')} + {isAwaitingNativeConfirm + ? translate('chainflipLending.awaitingConfirmTitle') + : translate('chainflipLending.borrow.executingTitle')} - {translate('chainflipLending.borrow.executingDescription')} + {isAwaitingNativeConfirm + ? translate('chainflipLending.awaitingConfirmDescription') + : translate('chainflipLending.borrow.executingDescription')} @@ -240,18 +259,14 @@ export const BorrowConfirm = memo(({ assetId }: BorrowConfirmProps) => { - - - - {translate('chainflipLending.borrow.confirmTitle')} - - - {translate('chainflipLending.borrow.confirmDescription', { - amount: borrowAmountCryptoPrecision, - asset: asset.symbol, - })} - - + + + + + + + {translate('chainflipLending.borrow.confirmTitle')} + { const actorRef = BorrowMachineCtx.useActorRef() const currentLtvBps = BorrowMachineCtx.useSelector(s => s.context.currentLtvBps) + const savedBorrowAmount = BorrowMachineCtx.useSelector(s => s.context.borrowAmountCryptoPrecision) const { totalCollateralFiat, totalBorrowedFiat, loansWithFiat } = useChainflipLoanAccount() const { thresholds } = useChainflipLtvThresholds() @@ -55,7 +57,7 @@ export const BorrowInput = ({ assetId, onAssetChange }: BorrowInputProps) => { [hasExistingLoans, minimumUpdateLoanAmountUsd, minimumLoanAmountUsd], ) - const [inputValue, setInputValue] = useState('') + const [inputValue, setInputValue] = useState(savedBorrowAmount || '') const hasCollateral = useMemo(() => bnOrZero(totalCollateralFiat).gt(0), [totalCollateralFiat]) @@ -77,6 +79,11 @@ export const BorrowInput = ({ assetId, onAssetChange }: BorrowInputProps) => { const inputFiat = useMemo(() => bnOrZero(inputValue).times(assetPrice), [inputValue, assetPrice]) + const availableFiat = useMemo( + () => bnOrZero(availableToBorrowCryptoPrecision).times(assetPrice).toFixed(2), + [availableToBorrowCryptoPrecision, assetPrice], + ) + const isBelowMinimum = useMemo(() => { if (!effectiveMinimumUsd) return false return inputFiat.gt(0) && inputFiat.lt(effectiveMinimumUsd) @@ -174,29 +181,34 @@ export const BorrowInput = ({ assetId, onAssetChange }: BorrowInputProps) => { {translate('chainflipLending.borrow.amount')} - + + + + {asset.symbol} + + {!assetPrice.isZero() && bnOrZero(inputValue).gt(0) && ( )} @@ -209,12 +221,15 @@ export const BorrowInput = ({ assetId, onAssetChange }: BorrowInputProps) => { - + + + + )} @@ -262,12 +324,15 @@ export const CollateralInput = ({ assetId, onAssetChange }: CollateralInputProps - + + + + + )} @@ -215,12 +299,15 @@ export const DepositInput = ({ assetId, onAssetChange }: DepositInputProps) => { - + + + + + )} @@ -297,12 +357,17 @@ export const EgressInput = ({ assetId, onAssetChange }: EgressInputProps) => { - + + {availableFiat !== undefined && ( + + )} + + + )} @@ -176,12 +269,17 @@ export const SupplyInput = ({ assetId, onAssetChange }: SupplyInputProps) => { - + + {availableFiat !== undefined && ( + + )} + + diff --git a/src/pages/ChainflipLending/Pool/components/Supply/hooks/useSupplyActionCenter.tsx b/src/pages/ChainflipLending/Pool/components/Supply/hooks/useSupplyActionCenter.tsx index 4ab65715d0b..094f56d40a3 100644 --- a/src/pages/ChainflipLending/Pool/components/Supply/hooks/useSupplyActionCenter.tsx +++ b/src/pages/ChainflipLending/Pool/components/Supply/hooks/useSupplyActionCenter.tsx @@ -18,8 +18,6 @@ export const useSupplyActionCenter = () => { const supplyAmountCryptoPrecision = SupplyMachineCtx.useSelector( s => s.context.supplyAmountCryptoPrecision, ) - const txHash = SupplyMachineCtx.useSelector(s => s.context.txHash) - useEffect(() => { if (isSigning && !actionIdRef.current && accountId) { actionIdRef.current = createAction({ @@ -33,10 +31,10 @@ export const useSupplyActionCenter = () => { useEffect(() => { if (isSuccess && actionIdRef.current) { - completeAction(actionIdRef.current, txHash ?? undefined) + completeAction(actionIdRef.current) actionIdRef.current = null } - }, [isSuccess, completeAction, txHash]) + }, [isSuccess, completeAction]) useEffect(() => { if (isError && actionIdRef.current) { diff --git a/src/pages/ChainflipLending/Pool/components/Withdraw/Withdraw.tsx b/src/pages/ChainflipLending/Pool/components/Withdraw/Withdraw.tsx index 57609e2a618..26e6d02afe4 100644 --- a/src/pages/ChainflipLending/Pool/components/Withdraw/Withdraw.tsx +++ b/src/pages/ChainflipLending/Pool/components/Withdraw/Withdraw.tsx @@ -1,3 +1,4 @@ +import { Flex } from '@chakra-ui/react' import type { AssetId } from '@shapeshiftoss/caip' import { AnimatePresence } from 'framer-motion' import { lazy, memo, Suspense, useEffect, useMemo, useState } from 'react' @@ -17,7 +18,11 @@ const WithdrawConfirm = lazy(() => import('./WithdrawConfirm').then(({ WithdrawConfirm }) => ({ default: WithdrawConfirm })), ) -const suspenseFallback = +const suspenseFallback = ( + + + +) type WithdrawProps = { assetId: AssetId diff --git a/src/pages/ChainflipLending/Pool/components/Withdraw/WithdrawConfirm.tsx b/src/pages/ChainflipLending/Pool/components/Withdraw/WithdrawConfirm.tsx index 7bf086274f4..1034d8f6fa6 100644 --- a/src/pages/ChainflipLending/Pool/components/Withdraw/WithdrawConfirm.tsx +++ b/src/pages/ChainflipLending/Pool/components/Withdraw/WithdrawConfirm.tsx @@ -1,8 +1,9 @@ -import { CheckCircleIcon } from '@chakra-ui/icons' -import { Button, CardBody, CardFooter, Flex, VStack } from '@chakra-ui/react' +import { ArrowForwardIcon, CheckCircleIcon } from '@chakra-ui/icons' +import { Button, CardBody, CardFooter, Flex, HStack, VStack } from '@chakra-ui/react' import type { AssetId } from '@shapeshiftoss/caip' +import { flipAssetId } from '@shapeshiftoss/caip' import { useQueryClient } from '@tanstack/react-query' -import { memo, useCallback, useMemo } from 'react' +import { memo, useCallback } from 'react' import { useTranslate } from 'react-polyglot' import { useWithdrawActionCenter } from './hooks/useWithdrawActionCenter' @@ -74,15 +75,6 @@ export const WithdrawConfirm = memo(({ assetId }: WithdrawConfirmProps) => { actorRef.send({ type: 'BACK' }) }, [actorRef]) - const confirmDescription = useMemo( - () => - translate('chainflipLending.withdraw.confirmDescription', { - amount: withdrawAmountCryptoPrecision, - asset: asset?.symbol ?? '', - }), - [translate, withdrawAmountCryptoPrecision, asset?.symbol], - ) - if (!asset) return null if (isSuccess) { @@ -101,16 +93,18 @@ export const WithdrawConfirm = memo(({ assetId }: WithdrawConfirmProps) => { })} - - - {translate('chainflipLending.withdraw.withdrawn')} - - + + + + {translate('chainflipLending.withdraw.withdrawn')} + + + @@ -143,7 +137,11 @@ export const WithdrawConfirm = memo(({ assetId }: WithdrawConfirmProps) => { - + + + + + {translate('chainflipLending.withdraw.errorTitle')} @@ -182,18 +180,29 @@ export const WithdrawConfirm = memo(({ assetId }: WithdrawConfirmProps) => { ) } + const isAwaitingNativeConfirm = isNativeWallet && !isConfirming && !stepConfirmed + if (!isConfirm) { return ( - + + + + + + {!isAwaitingNativeConfirm && } - {translate('chainflipLending.withdraw.executingTitle')} + {isAwaitingNativeConfirm + ? translate('chainflipLending.awaitingConfirmTitle') + : translate('chainflipLending.withdraw.executingTitle')} - {translate('chainflipLending.withdraw.executingDescription')} + {isAwaitingNativeConfirm + ? translate('chainflipLending.awaitingConfirmDescription') + : translate('chainflipLending.withdraw.executingDescription')} @@ -231,15 +240,14 @@ export const WithdrawConfirm = memo(({ assetId }: WithdrawConfirmProps) => { - - - - {translate('chainflipLending.withdraw.confirmTitle')} - - - {confirmDescription} - - + + + + + + + {translate('chainflipLending.withdraw.confirmTitle')} + } = useLocaleFormatter() const asset = useAppSelector(state => selectAssetById(state, assetId)) + const marketData = useAppSelector(state => selectMarketDataByAssetIdUserCurrency(state, assetId)) const actorRef = WithdrawMachineCtx.useActorRef() const supplyPositionCryptoBaseUnit = WithdrawMachineCtx.useSelector( s => s.context.supplyPositionCryptoBaseUnit, ) + const savedWithdrawAmount = WithdrawMachineCtx.useSelector( + s => s.context.withdrawAmountCryptoPrecision, + ) - const [withdrawAmountCryptoPrecision, setWithdrawAmountCryptoPrecision] = useState('') + const [withdrawAmountCryptoPrecision, setWithdrawAmountCryptoPrecision] = useState( + savedWithdrawAmount || '', + ) const availableCryptoPrecision = useMemo( () => @@ -60,6 +67,11 @@ export const WithdrawInput = ({ assetId, onAssetChange }: WithdrawInputProps) => const { minSupply, isLoading: isMinSupplyLoading } = useChainflipMinimumSupply(assetId) + const availableFiat = useMemo(() => { + if (!marketData?.price) return undefined + return bnOrZero(availableCryptoPrecision).times(marketData.price).toString() + }, [availableCryptoPrecision, marketData?.price]) + const hasPosition = useMemo( () => bnOrZero(supplyPositionCryptoBaseUnit).gt(0), [supplyPositionCryptoBaseUnit], @@ -199,29 +211,34 @@ export const WithdrawInput = ({ assetId, onAssetChange }: WithdrawInputProps) => ) : ( - + + + + {asset.symbol} + + )} @@ -232,12 +249,17 @@ export const WithdrawInput = ({ assetId, onAssetChange }: WithdrawInputProps) => - + + {availableFiat !== undefined && ( + + )} + +